I'm trying to make a canvas animation using jquery animate and step callback function.
The problem I'm facing is that the animation is running too fast. If I set the duration to 6 seconds, it runs in less than 1 second, and then I have to wait another 5 seconds for the complete callback.
Here is a video recording. I've traced the "now" param in the step function and the time passed since start:
http://screencast.com/t/pPj87yBVOKY
You can see that the browser is trancing values while the animation is running, then it stops for a few seconds and then it goes to the end.
Here is some code :
obj.percent = 0;
$(obj).animate({percent: 100},{duration: transitionConfig.tweenDuration * 1000, easing: getEasing(transitionConfig.tweenType, transitionConfig.easeType), complete: onTransitionEnd, step: processFrame});
function processFrame(x, y) {
timePassed = new Date().getTime() - time;
showOutput.width = showOutput.width;
showOutput.height = showOutput.height;
var cx = config.width / 2;
var cy = config.height / 2;
var rad = Math.sqrt(cx * cx + cy * cy);
var start = 0;
var amount = x;
console.log(x, timePassed);
showDraw.beginPath();
showDraw.moveTo(cx, cy);
showDraw.lineTo(config.width, cy);
showDraw.arc(cx, cy, rad, start, amount, false);
showDraw.lineTo(cx, cy);
showDraw.closePath();
showDraw.fillStyle = pattern;
showDraw.fill();
}
Related
I'm trying to create a canvas animation with 2 objects: a circumference and a filled circle. My objective is to make it seem that the circumference represents the circles orbit. However when trying to animate there's no animation and only when I click to stop the page does the image appear with the circle in a random position in the orbit (this means that the moving part works).
Thank you for your time and here's the code:
function restartAnimate(){
runAnimation(0);
setTimeout(restartAnimate(),1000);
}
function runAnimation(i){
let animation = document.getElementById("Animation");
let anim = animation.getContext("2d");
anim.clearRect(0,0,300,150);
anim.save();
anim.strokeStyle = "#99ebff";
anim.lineWidth = 10;
anim.beginPath();
anim.arc(150, 75, 40, 0, 2 * Math.PI);
anim.stroke();
anim.restore();
anim.save()
anim.fillStyle = "#000000";
anim.translate(150,75);
anim.rotate(2 * Math.PI * i / 1000);
anim.translate(-150,-75);
anim.beginPath();
anim.arc(150 + 36.5, 75 ,13, 0, 2 * Math.PI);
anim.fill();
anim.restore();
i += 16;
if(i < 1000) setTimeout(runAnimation(i),16);
}
You should use requestAnimationFrame to animate so that the render results are displayed in sync with the display hardware refresh.
setTimeout is very inaccurate and your function will fall behind over time. If you use requestAnimationFrame you can use the first argument (time in ms) to keep precisely on time.
ctx.save, and ctx.restore can be very expensive calls and should be avoided if you can. As you are only restoring the transform you can set it manually as needed with ctx.setTransform()
There is no need to restart the animation, just let it cycle.
Example rewrites your code with above points in mind and some other changes. See code comments for more info.
// Define constants and query DOM outside animation functions
const canvas = document.getElementById("animCanvas");
const ctx = canvas.getContext("2d");
Math.PI2 = Math.PI * 2;
var startTime;
restartAnimate();
function restartAnimate() {
if (startTime === undefined) {
requestAnimationFrame(runAnimation);
} else {
startTime = 0; // next frame animation we have restarted
}
// setTimeout(restartAnimate(),1000); // No need to restart as angle is cyclic.
}
function runAnimation(time) {
if (!startTime) { startTime = time }
const currentTime = time - startTime;
ctx.setTransform(1,0,0,1,0,0); // resets transform, better than using save and restore
ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height); // avoid magic numbers
//ctx.save(); // not needed
ctx.setTransform(1,0,0,1,150, 75); // last two values set the origin
// and is the point we rotate around
ctx.strokeStyle = "#99ebff";
ctx.lineWidth = 10;
ctx.beginPath();
ctx.arc(0, 0, 40, 0, Math.PI2); // rendering at the origin
ctx.stroke();
//ctx.restore(); // not needed
//ctx.save(); // not needed
ctx.fillStyle = "#000000";
//ctx.translate(150,75); // working from origin so don't need to translate
ctx.rotate(Math.PI2 * currentTime / 1000);
//ctx.translate(-150,-75); // working from origin so don't need to translate
ctx.beginPath();
ctx.arc(36.5, 0 ,13, 0, Math.PI2);
ctx.fill();
//ctx.restore(); not needed
requestAnimationFrame(runAnimation);
}
<canvas id="animCanvas"></canvas>
I am trying to make a pendulum in HTML/JS and have its angular velocity and angle controlled by ranges. both of the ranges work changing what they're intend to, but each time the value of the ranges is changed it seems to heavily increase the speed of the pendulum if the speed or angle is increased
or decreased the speed.
Here's the HTML/JavaScript Snippet
var canvas = ctx = false;
var frameRate = 1/40;
var frameDelay = frameRate * 1000;
/*used to change the angle and the velocity of the pendulum*/
var arcSlider = document.getElementById("arc");
var velocitySlider = document.getElementById('velocity');
var arcNumber = document.getElementById("arcNum");
var velocityNumber = document.getElementById("velocityNum");
var arc = (arcSlider.value / 100);
var velocity = velocitySlider.value;
/*sets the values for the pendulum*/
var pendulum = {mass: 100, length:300, theta: (Math.PI/2) - arc , omega: 0, alpha:0, J:0};
/*listener for angl slider*/
arcSlider.addEventListener("change", function(){
arcNumber.innerHTML = "arc: " + (arcSlider.value / 100);
arc = arcSlider.value / 100;
init();
});
/*listener for velocity slider*/
velocitySlider.addEventListener("change", function(){
velocityNumber.innerHTML = "velocity: " + velocitySlider.value;
velocity = velocitySlider.value;
init();
});
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
function init() {
pendulum.J = pendulum.mass * pendulum.length * pendulum.length / velocity;
lastTime = new Date();
requestAnimFrame(draw);
}
/*loop for pendulum*/
function draw(){
var width = 1000, height = 600;
var len = 150;
var timeMs = (new Date()).getTime();
var deltaT = (timeMs - lastTime.getTime()) / 1000;
canvas = document.getElementById("myCanvas");
let ctx = canvas.getContext("2d");
if (deltaT > 0.050)
{
deltaT = 0.050;
}
deltaT = 0.01;
/* Calculate current position*/
pendulum.theta += pendulum.omega * deltaT + (.5 * pendulum.alpha * deltaT * deltaT );
/* calculates force */
var T = pendulum.mass * 9.81 * Math.cos(pendulum.theta) * pendulum.length;
/* Current acceleration */
var alpha = T / pendulum.J;
/*Calculate current velocity*/
pendulum.omega += .5 * (alpha + pendulum.alpha) * deltaT;
/* Update acceleration */
pendulum.alpha = alpha;
/*sets the current x and y for the pendulum*/
var bobX = width/2 + pendulum.length * Math.cos(pendulum.theta);
var bobY = pendulum.length * Math.sin(pendulum.theta);
/*clears the canvas*/
ctx.clearRect(0,0,width,height)
/*canvas line*/
ctx.strokeStyle = "green";
ctx.beginPath();
ctx.moveTo(width/2,0);
ctx.lineTo(bobX,bobY);
ctx.stroke();
ctx.closePath();
ctx.fillStyle = "red";
/*canvas pendulum*/
ctx.beginPath();
ctx.arc(bobX,bobY,16,0 ,Math.PI * 2 , false);
ctx.fill();
ctx.closePath();
requestAnimationFrame(draw);
}
init();
<div href="#0" class="button">
<canvas id="myCanvas" width="1000" height="400">
</canvas>
</div>
<div class="sliderOutline">
<div class="sliderContainer">
<p>Change the arc of the pendulum:</p>
<input type="range" name="arcRange"min="5" max="80" value="40" class="slider" id="arc">
<p>Change the velocity:</p>
<input type="range" min="0" max="1000" value="500" class="slider" id="velocity" >
<div>
<p id="arcNum">arc: 0.4 </p>
</div>
<div>
<p id="velocityNum">velocity: 500</p>
</div>
</div>
</div>
Each time you call init(), init() calls requestAnimationFrame(draw) and of course draw calls requestAnimationFrame(draw) as is a common pattern.
However when you call init() a second time (e.g. when the user modifies a slider) then you call requestAnimationFrame(draw) again. When the browser determines it's time for an animation frame, draw will get called twice.
There's a stack of functions that will get called every time an animation frame is ready to be requested. If you call requestAnimationFrame once, then the stack get's one entry of draw pushed on to it. When the animation frame is ready that stack is popped until it's empty, and the functions get called one at a time. It's common practice to push on one function that will push itself back onto that stack with another call to requestAnimationFrame right at the end.
When you call requestAnimationFrame multiple times from outside this normal loop, you end up with multiple functions in that stack that all get called each animation frame. Because your draw function modifies the state of your pendulum each time it's called, calling it twice as often will make the pendulum move twice as fast. Click around on your slider 5 or 6 times and it'll rock back and forth like crazy.
As a quick fix, you can modify init like so:
function init(begin) {
pendulum.mass = 100;
pendulum.theta = (Math.PI/2) - arc;
pendulum.omega = 0;
pendulum.alpha = 0;
pendulum.J = pendulum.mass * pendulum.length * pendulum.length / velocity;
lastTime = new Date();
if (begin) requestAnimFrame(draw);
}
And at the very bottom of your javascript you'd call:
init(true);
As shown with an updated codepen. It's not the best solution, just a minimal solution to show that not calling requestAnimationFrame multiple times should provide the results you're looking for.
I was working on a fun project that implicates creating "imperfect" circles by drawing them with lines and animate their points to generate a pleasing effect.
The points should alternate between moving away and closer to the center of the circle, to illustrate:
I think I was able to accomplish that, the problem is when I try to render it in a canvas half the render jitters like crazy, you can see it in this demo.
You can see how it renders for me in this video. If you pay close attention the bottom right half of the render runs smoothly while the top left just..doesn't.
This is how I create the points:
for (var i = 0; i < q; i++) {
var a = toRad(aDiv * i);
var e = rand(this.e, 1);
var x = Math.cos(a) * (this.r * e) + this.x;
var y = Math.sin(a) * (this.r * e) + this.y;
this.points.push({
x: x,
y: y,
initX: x,
initY: y,
reverseX: false,
reverseY: false,
finalX: x + 5 * Math.cos(a),
finalY: y + 5 * Math.sin(a)
});
}
Each point in the imperfect circle is calculated using an angle and a random distance that it's not particularly relevant (it relies on a few parameters).
I think it's starts to mess up when I assign the final values (finalX,finalY), the animation is supposed to alternate between those and their initial values, but only half of the render accomplishes it.
Is the math wrong? Is the code wrong? Or is it just that my computer can't handle the rendering?
I can't figure it out, thanks in advance!
Is the math wrong? Is the code wrong? Or is it just that my computer can't handle the rendering?
I Think that your animation function has not care about the elapsed time. Simply the animation occurs very fast. The number of requestAnimationFrame callbacks is usually 60 times per second, So Happens just what is expected to happen.
I made some fixes in this fiddle. This animate function take care about timestamp. Also I made a gradient in the animation to alternate between their final and initial positions smoothly.
ImperfectCircle.prototype.animate = function (timestamp) {
var factor = 4;
var stepTime = 400;
for (var i = 0, l = this.points.length; i < l; i++) {
var point = this.points[i];
var direction = Math.floor(timestamp/stepTime)%2;
var stepProgress = timestamp % stepTime * 100 / stepTime;
stepProgress = (direction == 0 ? stepProgress: 100 -stepProgress);
point.x = point.initX + (Math.cos(point.angle) * stepProgress/100 * factor);
point.y = point.initY + (Math.sin(point.angle) * stepProgress/100 * factor);
}
}
Step by Step:
based on comments
// 1. Calculates the steps as int: Math.floor(timestamp/stepTime)
// 2. Modulo to know if even step or odd step: %2
var direction = Math.floor(timestamp/stepTime)%2;
// 1. Calculates the step progress: timestamp % stepTime
// 2. Convert it to a percentage: * 100 / stepTime
var stepProgress = timestamp % stepTime * 100 / stepTime;
// if odd invert the percentage.
stepProgress = (direction == 0 ? stepProgress: 100 -stepProgress);
// recompute position based on step percentage
// factor is for fine adjustment.
point.x = point.initX + (Math.cos(point.angle) * stepProgress/100 * factor);
point.y = point.initY + (Math.sin(point.angle) * stepProgress/100 * factor);
How to draw a pie canvas by set time in inputs H:M:S?
I have fiddle, but it works on the percentage. Please help make animation process on set time values.
Thanks.
function animate(current) {
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.arc(x, y, radius, -(quart), ((circ) * current) - quart, false);
context.stroke();
curPerc++;
if (curPerc < endPercent) {
requestAnimationFrame(function () {
animate(curPerc / 100)
});
}
}
First of all what you need to do is to check the total time of your values. Do this when pressing the start-button.
var hours = document.getElementById("hh").value;
var minutes = document.getElementById("mm").value;
var seconds = document.getElementById("ss").value;
var totalTime = 60*60*hours + 60*minutes + seconds;
The next step is to separate the growing of your pie to outside the drawing. Right now, you're using rAF, which means you'll update your pie whenever the browser is ready.
var degreesPerSecond = 360/totalTime;
var updateFrequency = 200; //milliseconds
var curDeg = 0; //Keeping track on the currect degree
function update() {
curDeg += degreesPerSecond/1000*updateFrequency;
curPerc = curDeg/360;
}
When pressing the start-button, you also start an interval.
var myInterval = setInterval(update,updateFrequenzy);
Check out this update of your fiddle
Hello and thank you for your help in advance.
I am trying to push/create a new "ring" every couple seconds. I have a ring with a couple variables for the X and Y. The problem I am encountering is, how do I get a new ring and also increment the variables? I need a new variable name for every ring?
Here is how far I have gotten so far:
http://codepen.io/hossman/pen/AfwkF
You can see in the demo how 1 ring goes out, but I want more than 1 ring to go out of my eyes. So for instance 1 ring goes and then it waits a second and then shoots out another ring, so now there are 2 rings on the canvas, then 3, then 4, etc.... I have thought of multiple ways like using arrays and setTimeouts, but I cant put my finger on it. The only other idea I have is to create multiple variables with different names and have each ring be incremented, but thats not very D.R.Y.
Anyhelp?
Please ask questions if I didn't explain it good enough. Thanks again!
Add this to your global vars at the top (and set to whatever you want the distance to be between circles):
var distanceApart = 40;
Then update your main loop like this:
requestAnimationFrame(function print() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
var leftRing = new Ring(x, y);
var rightRing = new Ring(x2, y2);
var temp = startRadius;
var temp2 = 0;
while(temp > 0){
leftRing.draw(ctx, startRadius - temp2 , 'red');
rightRing.draw(ctx, startRadius - temp2 , 'red');
temp2 = temp2 + distanceApart;
temp = temp - distanceApart;
}
startRadius += increase;
requestAnimationFrame(print);
});
Forked here: http://codepen.io/anon/pen/plBmj
(Looks very memorizing!)
I would rewrite parts of your code to enable this. For example I would rewrite your Ring class as follows:
var Ring = defclass({
constructor: function (x, y, r) {
this.x = x;
this.y = y;
this.r = r;
},
draw: function (context) {
context.beginPath();
context.arc(this.x, this.y, this.r, 0, Math.PI * 2);
context.stroke();
return this;
},
addRadius: function (r) {
return new Ring(this.x, this.y, this.r + r);
}
});
Your Ring class constructor now takes x, y and a radius r. The addRadius function returns a new Ring instead of mutating the original one. This is good because immutability makes your code easier to work with. Oh, and defclass is declared as:
function defclass(prototype) {
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}
Then we create the two rings for your eyes:
var radius = 10;
var delta = 0.1;
var left = new Ring(cx - (cx / 3.6), cy - 5, radius);
var right = new Ring(cx + (cx / 3.6), cy - 10, radius);
After that we call the animation loop:
var interval = 50 / 3;
var start = Date.now();
loop(start, [left, right]);
Since we want to playback at 60 FPS the interval is 1000 / 60 which can be simplified to 50 / 3. The animation loop is defined as follows:
function loop(last, rings) {
var next = last + interval;
context.clearRect(0, 0, width, height);
var newRings = rings.map(function (ring) {
return ring.draw(context).addRadius(delta);
});
var now = Date.now();
setTimeout(loop, next - now, next,
Math.floor((now - start) / 1000) === rings.length / 2 ?
[left, right].concat(newRings) : newRings);
}
Here's what's happening:
First we clear the screen.
Then we draw all the rings and increase their size.
If one second has elapsed we add two new rings to the array.
Finally we calculate when to call loop again so that it fires after the correct interval.
See the demo: http://jsfiddle.net/LAr76/