I don't understand why the infinite loop does not work but uncommenting the function call works.
<body>
<canvas id="myCanvas"style="border:2px solid black;" width="1600" height="900">
Your browser does not support the HTML5 canvas tag.
</canvas>
<script>
var c = document.getElementById ("myCanvas").getContext ("2d");
var boxSize = 25;
var numBoxes = 1;
var x = 1000;
var dx = 1;
var y = 100;
var dy = 1;
var a = 0.01;
var da = 0.1;
var size = 20;
var w = c.canvas.width;
var h = c.canvas.height;
//function testing () {
while (true) {
c.clearRect (0, 0, w, h);
x = x + 1;
y = y + 1;
a = a + 0.1;
x = x + dx;
y = y + dy;
if (x < size / 2 || x + (size / 2) > w) {
dx = -dx;
da = -da;
}
if (y < size / 2 || y + (size / 2) > h) {
dy = -dy;
da = -da;
}
c.save ();
c.translate (x, y);
c.rotate (a);
c.strokeRect (-size / 2, -size / 2, size, size);
c.restore ();
}
// testing ();
// setInterval (testing, 10);
</script>
</body>
When you use setInterval you keep adding the function you are calling to the queue of things for the browser to do. Repaint events are also added to this queue.
When you use an infinite loop, the browser never gets to the end of the function, so it never gets around to running a repaint and the image in the document never updates.
JavaScript-in-a-browser is a part of a larger construction. You need to align to the rules of this construction and don't hurt it. One of the rule is that your JavaScript must run quick and exit then. Exit means that control gets back to the framework, which does all the job, repaint the screen etc.
Try to hide and show something many times:
for (n = 0; n < 200; n++) {
$("#test").hide();
$("#test").show();
}
When this code runs, you won't see any flickering, you will see that the last command will have effect.
Have to say, it's not easy to organize code that way, if you want to make a cycle which paints nice anims on a canvas, you have to do it without while.
Related
I'm trying to build an animated graph with paper.js that can react to different input. So I want to smoothly animate one point vertically to a different point.
I've looked at different examples and the closest ones to mine is this one:
paper.tool.onMouseDown = function(event) {
x = event.event.offsetX;
y = event.event.offsetY;
paper.view.attach('frame', moveSeg);
}
var x;
var y;
function moveSeg(event) {
event.count = 1;
if(event.count <= 100) {
myPath.firstSegment.point._x += (x / 100);
myPath.firstSegment.point._y += (y / 100);
for (var i = 0; i < points - 1; i++) {
var segment = myPath.segments[i];
var nextSegment = segment.next;
var vector = new paper.Point(segment.point.x - nextSegment.point.x,segment.point.y - nextSegment.point.y);
vector.length = length;
nextSegment.point = new paper.Point(segment.point.x - vector.x,segment.point.y - vector.y);
}
myPath.smooth();
}
}
This Code animates one Point to the click position, but I couldn't change it to my needs.
What I need is:
var aim = [120, 100];
var target = aim;
// how many frames does it take to reach a target
var steps = 200;
// Segment I want to move
myPath.segments[3].point.x
And then I dont know how to write the loop that will produce a smooth animation.
example of the graph:
I worked out the answer. The following steps in paperscript:
Generate Path
Set aim for the point
OnFrame Event that does the moving (eased)
for further animations just change the currentAim variable.
var myPath = new Path({
segments: [[0,100],[50,100],[100,100]]});
// styling
myPath.strokeColor = '#c4c4c4'; // red
myPath.strokeWidth = 8;
myPath.strokeJoin = 'round';
myPath.smooth();
// where the middle dot should go
var currentAim = [100,100];
// Speed
var steps = 10;
//Animation
function onFrame(event) {
dX1 = (currentAim[0] - myPath.segments[1].point.x )/steps;
dY1 = (currentAim[1] - myPath.segments[1].point.y )/steps;
myPath.segments[1].point.x += dX1;
myPath.segments[1].point.y += dY1;
}
Here is My JS fiddle
I have a requirement like when user clicks on center circle, that should toggle outer circle and when user clicks on outer small circles that should change center circle value.
Here i am not getting how to Show/ hide part of Canvas when user clicks on center circle?
Any help on how to do this?
GenerateCanvas();
function GenerateCanvas() {
try {
var FlagCircleCenterCoordinates = new Array();
var FlagCircles = [];
var CenterX = document.getElementById('canvasFlag').width / 2;
var CenterY = document.getElementById('canvasFlag').height / 2;
var OuterTrackRadius = 98;
var InnerTrackRadius = 70;
var InnerCircleRadius = 20;
var FlagElement = document.getElementById("canvasFlag");
var ObjContext = FlagElement.getContext("2d");
// Outer track
ObjContext.fillStyle = "#FFF";
ObjContext.beginPath();
ObjContext.arc(CenterX, CenterY, OuterTrackRadius, 0, 2 * Math.PI);
ObjContext.strokeStyle = '#CCC';
ObjContext.stroke();
ObjContext.fill();
// Inner track
ObjContext.beginPath();
ObjContext.arc(CenterX, CenterY, InnerTrackRadius, 0, 2 * Math.PI);
ObjContext.strokeStyle = '#CCC';
ObjContext.stroke();
// Inner small circle
ObjContext.beginPath();
ObjContext.arc(CenterX, CenterY, InnerCircleRadius, 0, 2 * Math.PI);
ObjContext.strokeStyle = '#CCC';
ObjContext.stroke();
//Max 17...other wide need to change the Inner and Outer circle radius
var FlagImagesArray = [1, 2, 3,4,5];
if (FlagImagesArray.length > 0) {
var StepAngle = 2 * Math.PI / FlagImagesArray.length;
var FlagCircleRadius = (OuterTrackRadius - InnerTrackRadius) / 2;
var RadiusOfFlagCircleCenters = OuterTrackRadius - FlagCircleRadius;
for (var LoopCnt in FlagImagesArray) {
var CircleCenterCoordinates = new Object();
CircleCenterCoordinates.PostionX = CenterX + (Math.cos(StepAngle * (parseInt(LoopCnt) + 1)) * RadiusOfFlagCircleCenters);
CircleCenterCoordinates.PostionY = CenterY + (Math.sin(StepAngle * (parseInt(LoopCnt) + 1)) * RadiusOfFlagCircleCenters);
ObjContext.beginPath();
ObjContext.arc(CircleCenterCoordinates.PostionX, CircleCenterCoordinates.PostionY, FlagCircleRadius, 0, 2 * Math.PI);
ObjContext.strokeStyle = '#CCC';
ObjContext.stroke();
ObjContext.fillStyle = 'blue';
ObjContext.fillText(FlagImagesArray[LoopCnt], CircleCenterCoordinates.PostionX, CircleCenterCoordinates.PostionY);
FlagCircleCenterCoordinates[LoopCnt] = CircleCenterCoordinates;
var ObjFlagCircle = {
Left : CircleCenterCoordinates.PostionX - FlagCircleRadius,
Top : CircleCenterCoordinates.PostionY - FlagCircleRadius,
Right : CircleCenterCoordinates.PostionX + FlagCircleRadius,
Bottom : CircleCenterCoordinates.PostionY + FlagCircleRadius,
FlagName : FlagImagesArray[LoopCnt]
}
FlagCircles[LoopCnt] = ObjFlagCircle;
}
$('#canvasFlag').mousemove(function (Event) {
debugger;
$(this).css('cursor', 'auto');
var ClickedX = Event.pageX - $('#canvasFlag').offset().left;
var ClickedY = Event.pageY - $('#canvasFlag').offset().top;
for (var Count = 0; Count < FlagCircles.length; Count++) {
if (ClickedX < FlagCircles[Count].Right &&
ClickedX > FlagCircles[Count].Left &&
ClickedY > FlagCircles[Count].Top &&
ClickedY < FlagCircles[Count].Bottom) {
$(this).css('cursor', 'pointer');
break;
}
}
});
$('#canvasFlag').click(function (Event) {
debugger;
$(this).css('cursor', 'auto');
var ClickedX = Event.pageX - $('#canvasFlag').offset().left;
var ClickedY = Event.pageY - $('#canvasFlag').offset().top;
for (var Count = 0; Count < FlagCircles.length; Count++) {
if (ClickedX < FlagCircles[Count].Right &&
ClickedX > FlagCircles[Count].Left &&
ClickedY > FlagCircles[Count].Top &&
ClickedY < FlagCircles[Count].Bottom) {
ObjContext.fillStyle = "#FFF";
ObjContext.beginPath();
ObjContext.arc(CenterX, CenterY, InnerCircleRadius - 1, 0, Math.PI * 2);
ObjContext.closePath();
ObjContext.fill();
ObjContext.fillStyle = "blue";
ObjContext.fillText(FlagCircles[Count].FlagName, CenterX, CenterY);
break;
}
}
});
}
}
catch (E) {
alert(E);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<canvas id="canvasFlag" width="200" height="200">
Your browser does not support the canvas
</canvas>
Oh man. Ok so first off, you'll need to get rid of the try/catch statement, it isn't doing anything.
Next you'll need to make all those vars you have outside of the function body, we need to be able to access them from another function
It looks like you have all your click functionality done, and it's working too. That's good, just go ahead and move both of the lines that start with jquery outside of the generateCanvas function. They only need to run once, and we're going to need to call generate canvas again.
Fourth, make a variable to toggle the outer circle off and on somewhere, and only draw the outer ring in generateCanvas() when that variable is true. You should also set another Global variable that gets set to Count, right before you break, so that you can remember it when you regenerate the canvas.
Take all the code you have in your click function to draw the inner circle with the number, and put that inside of generate canvas. Make sure that code only calls if the variable you used to remember Count is set to something (ie you had already clicked an outer number)
Next, add a generateCanvas() call in your click function.Now your click function sets the variable you use to represent the center value, redraws the canvas, and returns. You'll need more logic in your mouse down function in order to figure out when you clicked the center, but based on the code you already have you can figure that out, your main problem is just that this was set up to run once, not multiple times. That makes the canvas a lot more like an image instead of an active drawing element
Don't forget to add FlagElement.width = FlagElement.width to clear the canvas! (or just draw a background over it)
I am attempting to use particlesDepthBlur() in place of the opacity for the "snowflakes" - which is located inside the step function, however it produces an undesired strobe effect - why? Consider the following code,
Edited for clarification:
<canvas id="canvas"></canvas>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var num = 2000;
var canvas = document.getElementById("canvas");
var width = canvas.width = 960;
var height = canvas.height = 500;
var ctx = canvas.getContext("2d");
var particles = d3.range(num).map(function(i) {
return [Math.round(width*Math.random()), Math.round(height*Math.random())];
});
function particlesDepthBlur(){
return Math.random();
console.log(Math.random());
}
function particlesDepthSize(){
return Math.floor((Math.random()*4)+1);
}
d3.timer(step);
function step() {
ctx.shadowBlur=0;
ctx.shadowColor="";
ctx.fillStyle = "rgba(0,0,0,"+particlesDepthBlur()+")";
ctx.fillRect(0,0,width,height);
ctx.shadowBlur=particlesDepthSize();
ctx.shadowColor="white";
ctx.fillStyle = "rgba(255,255,255,1)";
particles.forEach(function(p) {
p[0] += Math.round(2*Math.random()-1);
p[1] += Math.round(2*Math.random()-1) + 2;
if (p[0] < 0) p[0] = width;
if (p[0] > width) p[0] = 0;
if (p[1] < 0) p[1] = height;
if (p[1] > height) p[1] = 0;
drawPoint(p);
});
};
function drawPoint(p) {
ctx.fillRect(p[0],p[1],1,1);
};
</script>
<style>
html, body { margin: 0; padding: 0; }
</style>
Couple things:
Firstly you are calling ctx.fillStyle = "rgba(0,0,0,"+particlesDepthBlur()+")"; immediately before filling the background of the canvas.
Second, you are only calculating the blur and opacity once per frame, not per particle.
Thirdly, if you calculate it per particle (and continue to use Math.random()) then it bogs down my machine with several thousand operations per second.
Here is my
fiddle!
~Every frame I calculate 10 opacities and 10 sizes and iterate across the particles setting them per particle.~ << This was an
old version; now the opacities are all set up before step() is called, and the sizes are proportional to opacities.
edit: good job with the random falling-downward motion!
edit2: tweaking to set constant opacity and size per particle. this still runs very slowly for me, probably because you are running Math.random() 4000 times per frame. You might consider calculating a couple dozen positional vectors once per frame, and iterate across all your particles. This way every n snowflakes would be falling in the same pattern, at the benefit of much less computation needed.
Finally, perhaps consider making the 'close' snowflakes (big and bright) fall faster than the 'far' snowflakes.
<snip>
// Set up an opacity value for each particle, this will later be indexed with j
var particleOpacities = [];
particles.forEach(function(p){
particleOpacities.push(particlesDepthBlur());
});
d3.timer(step);
var j = 0;
// since j is used by both step and drawPoint, it has to be outside both functions
function step() {
ctx.shadowBlur=0;
ctx.shadowColor="";
ctx.fillStyle = "rgba(0,0,0,1)";
ctx.fillRect(0,0,width,height);
j = 0;
particles.forEach(function(p) {
p[0] += Math.round(2*Math.random()-1);
p[1] += Math.round(2*Math.random()-1) + 2;
if (p[0] < 0) p[0] = width;
if (p[0] > width) p[0] = 0;
if (p[1] < 0) p[1] = height;
if (p[1] > height) p[1] = 0;
drawPoint(p);
});
};
function drawPoint(p) {
j++; // iterate over points
var particleSize = particleOpacities[j] * 4;
ctx.shadowBlur=particleSize;
ctx.shadowColor="white";
ctx.fillStyle = "rgba(255,255,255," + particleOpacities[j] + ")";
ctx.fillRect(p[0],p[1],particleSize,particleSize);
};
The ctx.fillStyle = "rgba(0,0,0,"+particlesDepthBlur()+")"; randomly changes how much of the previous canvas is visible. It does so uniformly across the entire canvas. Sometimes if fills the screen completely with black wiping out the past views, other times it lets the last screen partially show. When it lets previous views be seen it can as much double the amount of white on the canvas, and when followed by a low opacity suddenly the quantity of white drops.
function particlesDepthBlur(){
return Math.random(0.5)+.5;
}
smooths this out
I'm working on a small animation where the user drags a circle and the circle returns back to the starting point. I figured out a way to have the circle return to the starting point. The only problem is that it will hit one of the sides of the frame before returning. Is it possible for it to go straight back (follow the path of a line drawn between the shape and starting point).
The other problem is that my setInterval doesn't want to stop. If you try pulling it a second time it would pull it back before you release your mouse. It also seems to speed up after every time. I have tried using a while loop with a timer but the results weren't as good. Is this fixable?
var paper = Raphael(0, 0, 320, 200);
//var path = paper.path("M10 10L40 40").attr({stoke:'#000000'});
//var pathArray = path.attr("path");
var circle = paper.circle(50, 50, 20);
var newX;
var newY;
circle.attr("fill", "#f00");
circle.attr("stroke", "#fff");
var start = function () {
this.attr({cx: 50, cy: 50});
this.cx = this.attr("cx"),
this.cy = this.attr("cy");
},
move = function (dx, dy) {
var X = this.cx + dx,
Y = this.cy + dy;
this.attr({cx: X, cy: Y});
},
up = function () {
setInterval(function () {
if(circle.attr('cx') > 50){
circle.attr({cx : (circle.attr('cx') - 1)});
} else if (circle.attr('cx') < 50){
circle.attr({cx : (circle.attr('cx') + 1)});
}
if(circle.attr('cy') > 50){
circle.attr({cy : (circle.attr('cy') - 1)});
} else if (circle.attr('cy') < 50){
circle.attr({cy : (circle.attr('cy') + 1)});
}
path.attr({path: pathArray});
},2);
};
circle.drag(move, start, up);
Here's the Jfiddle: http://jsfiddle.net/Uznp2/
Thanks alot :D
I modified the "up" function to the one below
up = function () {
//starting x, y of circle to go back to
var interval = 1000;
var startingPointX = 50;
var startingPointY = 50;
var centerX = this.getBBox().x + (this.attr("r")/2);
var centerY = this.getBBox().y + (this.attr("r")/2);
var transX = (centerX - startingPointX) * -1;
var transY = (centerY - startingPointY) * -1;
this.animate({transform: "...T"+transX+", "+transY}, interval);
};
and the "start" function as follows:
var start = function () {
this.cx = this.attr("cx"),
this.cy = this.attr("cy");
}
Is this the behavior you are looking for? Sorry if I misunderstood the question.
If the circle need to get back to its initial position post drag, we can achieve that via simple animation using transform attribute.
// Assuming that (50,50) is the location the circle prior to drag-move (as seen in the code provided)
// The animation is set to execute in 1000 milliseconds, using the easing function of 'easeIn'.
up = function () {
circle.animate({transform: 'T50,50'}, 1000, 'easeIn');
};
Hope this helps.
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
context.canvas.width = window.innerWidth;
context.canvas.height = window.innerHeight;
var x1 = Math.random()*context.canvas.width;
var y1 = Math.random()*context.canvas.height;
var xdir = 0; var ydir = 0;
context.beginPath();
setInterval(function(){
for (var i = 0; i < 10; i++) {
randx = Math.random(); randy = Math.random();
if (randx > 0.95) {
if (xdir < 0) xdir = (xdir+((Math.random()*1.5) - 1))/2;
else if (xdir > 0) xdir = (xdir+((Math.random()*1.5) - 0.5))/2;
else xdir = (Math.random()*1.5) - 0.75;
}
if (randy > 0.95) {
if (ydir < 0) ydir = (ydir+((Math.random()*1.5) - 1))/2;
else if (ydir > 0) ydir = (ydir+((Math.random()*1.5) - 0.5))/2;
else ydir = (Math.random()*1.5) - 0.75;
}
context.lineTo(x1+xdir, y1+ydir);
context.stroke();
x1 = x1+xdir;
y1 = y1+ydir;
}
},50);
This is my random line script, but my lines are really ugly: http://i.stack.imgur.com/YZT2o.png
Is there any better way for achieving a smooth line using canvas?
take a look at this question:
Drawing GOOD LOOKING (like in Flash) lines on canvas (HTML5) - possible?
Lines on HTML5 Canvas are nicely antialiased on all browsers/OS (AFAIK). However, in your update callback with its 10-strokes-per-loop you are neither clearing your canvas nor clearing your path and so you are drawing the same path on top of itself 200 times per second. This is causing all the anti-aliasing to be destroyed as even the faintest opacity pixels build up until they are solid lines.
The simplest fix to make your code look pretty is to add this line:
context.clearRect(0,0,context.canvas.width,context.canvas.height);
inside your for loop, for example right before context.stroke();.
This one-line change makes it look good, but is bad for performance, clearing and redrawing the canvas 10 times for each visual update.
Here's a better alternative:
context.beginPath();
context.moveTo(x1,y1);
context.lineTo(x1+xdir, y1+ydir);
context.stroke();
x1 += xdir; y1 += ydir;
This way you never clear the canvas, and instead draw only the changed line each frame.
One other alternative (if you need the full path always available) is to accumulate your changes to the context path in one high-speed setInterval loop, and in another, slower loop occasionally clear the canvas and re-stroke the entire path. This is similar to what I've done for my Langton's (Very Fast) Ant simulation.