I'd like to use deceleration on a animation I'm running through request animation frame! I know how to do velocity, for deceleration I found this project https://github.com/gre/bezier-easing. I'm now TIAS, but not sure what to do https://github.com/gre/bezier-easing. I expect to see a decrease on speed at the end velocity <= parseFloat(attrs.radialBarPercentage). Code example:
var easing = BezierEasing(0, 0, 1, 0.5);
(function loop() {
velocity += (i + velocity) * friction;
// attempts:
//velocity = i - easing(i / 100);
//velocity = (i + velocity) * easing(i / 100);
if (velocity <= parseFloat(attrs.radialBarPercentage)) {
$knob.val(velocity).trigger('change');
i++;
animationFrame.request(loop);
}
})();
I found the solution, working for me:
// http://cubic-bezier.com/#0.25,0.25,0,1
var easing = BezierEasing(0.25, 0.25, 0, 0.9),
i = 0,
stepIncrementAmount = 0.25;
(function loop() {
// sorry about the * 100 but that's what $knob expects, scale range 0 > 100, and easing needs 0 to 1
velocity = easing(i / 100) * 100;
if (velocity <= parseFloat(attrs.radialBarPercentage)) {
$knob.val(velocity).trigger('change');
i += stepIncrementAmount;
animationFrame.request(loop);
}
})();
Related
I am current running into a bit of a math conundrum that has stumped me for days.
I am building a JavaScript game and attempting to create boundary coordinates to manage the pathing and movement of sprites, however it appears that lag/jitter/delay is reeking havoc on different entities moving in coordination with one another.
I believe I must calculate the jitter/lag/offset and somehow apply it to the coordinate range detection and movement functions but I have yet to crack the code correctly and alleviate the mis-aligning sprites.
Here is a replication of the issue in a CodeSandbox and the bulk of the code that shows it in action:
https://codesandbox.io/s/movetime-boundries-issue-example-2prow?file=/src/App.js
var obj = { x: 10, speed: 250 };
var obj2 = { x: 100 };
var objHighestX = { max: 0 };
var direction = 0;
var canvas = document.getElementById("mainScene");
var ctx = canvas && canvas.getContext("2d");
ctx.imageSmoothingEnabled = false;
ctx.font = "15px Courier";
var render = function () {};
var update = function (modifier) {
// console.log("Updating");
ctx.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight);
ctx.fillRect(obj2.x, 60, 15, 15);
if (obj.x > objHighestX.max) {
objHighestX.max = obj.x;
}
ctx.fillText(String("X" + obj.x), 25, 100);
ctx.fillText(String("Furthest" + objHighestX.max), 125, 100);
if (obj.x >= obj2.x - 15) {
direction = 1;
} else if (obj.x <= 0) {
direction = 0;
}
if (direction === 0) {
obj.x += obj.speed * modifier;
ctx.clearRect(obj.x - 7, 9, 17, 17);
ctx.fillRect(obj.x, 60, 15, 15);
}
if (direction === 1) {
obj.x -= obj.speed * modifier;
ctx.clearRect(obj.x, 9, 17, 17);
ctx.fillRect(obj.x, 60, 15, 15);
}
};
var lastUpdate = Date.now();
// The main game loop
var main = function () {
var now = Date.now();
var delta = now - lastUpdate;
lastUpdate = now;
update(delta / 1000);
render();
requestAnimationFrame(main);
};
main();
If anyone has any suggestions or questions towards my case, I'm very eager to hear of it.
Perhaps I have to use the rate of change to create an offset for the boundaries?
Which I've tried like:
if (obj.x >= obj2.x - (15 * 1 * modifier))
But am still not yet getting this one down. Thank you all, greatly, in advance.
First, you're delta time calculations aren't complete.
var now = Date.now();
var delta = now - lastUpdate;
lastUpdate = now;
update(delta / 1000);
If you now request update() to be invoked via requestAnimationFrame, the number passed as a parameter will be the number of miliseconds passed between the last and the current frame. So if the screen refresh rate is 60hz it's roughly 16.6ms.
This value alone though isn't meaningful - you need to compare it against a target value.
Say we want to achieve a framerate of 30fps - equal to ~33.3ms. If we take this value and divide it from the 16.6ms above, we get roughly 0.5. This makes complete sense. We want 30fps, the monitor refreshes at 60hz, so everything should move at half the speed.
Let's modify your main() function to reflect that:
var main = function() {
var targetFrameRate = 30;
var frameTime = 1000 / targetFrameRate;
var now = Date.now();
var delta = now - lastUpdate;
lastUpdate = now;
update(delta / frameTime);
render();
requestAnimationFrame(main);
};
Second problem is the update() function itself.
Let's have a look at the following block:
if (direction === 0) {
obj.x += obj.speed * modifier;
ctx.clearRect(obj.x - 7, 9, 17, 17);
ctx.fillRect(obj.x, 60, 15, 15);
}
That means, wherever obj currently is, move it to the right by some amount. We are missing the boundary check at this point. You need to check if it would leave the bounds if we would move it to the right. In case it does, just move it next to the bounds.
Something like this:
var maxX=100;
if (direction === 0) {
var speed = obj.speed * modifier;
if (obj.x + obj.width + speed > maxX) {
direction = 1;
obj.x = maxX - obj.width;
} else {
obj.x += speed;
}
}
Maintain correct speed during collision frame
I notice that the object is always moving, which means the given answer does not correctly solve the problem.
An object should not slow down between frames if it has a constant speed
The illustration shows an object moving
At top how far it would move without interruption.
At center the point of collision. Note that there is still a lot of distance needed to cover to maintain the same speed.
At bottom the object is moved left the remaining distance such the total distance traveled matches the speed.
To maintain speed the total distance traveled between frames must remain the same. Positioning the object at the point of collision reduces the distance traveled and thus the speed of the object during the collision frame can be greatly reduced
The correct calculation is as follows
const directions = {
LEFT: 0,
RIGHT: 1,
};
const rightWallX = 100;
const leftWallX = 0;
if (obj.direction === directions.RIGHT) {
obj.x = obj.x + obj.speed;
const remainDist = (rightWallX - obj.width) - obj.x;
if (remainDist <= 0) {
obj.direction = directions.LEFT;
obj.x = (rightWallX - obj.width) + remainDist;
}
} else if (obj.direction === directions.LEFT) {
obj.x = obj.x - obj.speed;
const remainDist = leftWallX - obj.x;
if (remainDist >= 0) {
obj.direction = directions.RIGHT;
obj.x = leftWallX + remainDist;
}
}
I'm trying to create a sine wave animation in using javascript and have had some success getting the look that I want but I am having performance issues seemingly because of the number of vectors being generated.
I'm currently using the p5js library. Here is a sample of what I have generated so far, would there be any options to optimise this to improve performance whilst keeping the level of detail / smoothness?
function setup () {
let size = min(windowWidth, windowHeight) * 0.96;
size = floor(size);
createCanvas(windowWidth, windowHeight);
noiseSeed(random(50));
frameRate(25);
noFill();
}
function windowResized () {
let size = min(windowWidth, windowHeight);
size = floor(size);
resizeCanvas(windowWidth, windowHeight);
noiseSeed(random(50));
frameRate(25);
draw();
}
function draw () {
clear();
beginShape();
const _o = millis() * 0.0005;
const amount = 20;
const ampl = ( 630 / ( windowHeight ) * 100 ) + 120;
for(var k=0;k<amount;k++) {
beginShape();
const offset = (1 - k / amount) * 3;
const detail = 10;
for(var i=0;i<(width+detail);i+=detail) {
let y = height * 0.5;
y += Math.sin(i * 0.01 - _o + ( k / 50 ) + offset) * ampl;
y += Math.sin(i * 0.005 - _o + 5 + offset + noise( 50 ) ) * ampl;
console.log(i,y);
vertex(i, y);
}
stroke(255, 255, 255, (k/(amount - 1) * 100));
frameRate(25);
endShape();
}
}
Codepen example:
https://codepen.io/craiell/pen/zYGbLKm
I am currently using the P5js library but if there are other libraries / methods I am open to alternatives. Any pointers would be much appreciated.
Remove the console.log line from inside the nested loops. This makes the animation smooth on my laptop, even if I increase the frame rate to 60.
I'm not familiar with P5js, but the extra calls to frameRate() appear to be unnecessary.
A few ideas off the top of my head:
for() loops are blocking the tread, rewrite your code using foreach() or map() to optimize the flow
Check out requestAnimationFrame()
Floating-point operations are expensive. See if you can generate a cache of reusable vector coordinates
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);
I'd like to make an app where a ball moves at the angle your mouse hits it. So if you swipe your mouse down from top left quadrant at 30 degrees (I guess that would be 180-30 = angle of 150 degrees), it will knock the ball that way. I've been drawing my lines as such:
function drawAngles () {
var d = 50; //start line at (10, 20), move 50px away at angle of 30 degrees
var angle = 80 * Math.PI/180;
ctx.beginPath();
ctx.moveTo(300,0);
ctx.lineTo(300,600); //x, y
ctx.moveTo(0,300);
ctx.lineTo(600,300);
ctx.moveTo(300,300);
ctx.lineTo(600,100);
ctx.arc(300,300,300,0,2*Math.PI);
ctx.stroke();
}
But this doesn't give me an idea of what the angles are.
Then I move the ball at that angle (for now, I'm animating it without mouse interaction)
function getAngleX (x) {
return x = x + (50 * Math.cos(Math.PI/6));
}
function getAngleY(y) {
return y = y + (50 * Math.sin(Math.PI/6));
}
//just animate this box to move at an angle from center down at 30 degrees
$(".anotherBox").mouseenter(function(e) {
pos = $(this).position();
box2X = pos.left;
box2Y = pos.top;
$(this).animate({
//top : $(window).outerHeight(),
top : getAngleY(box2Y)+"px",
left: getAngleX(box2X)+"px",
}, "slow");
});
So how can I draw a line at a specified angle? I'd like to make sure my ball is following along that path.
You can use different approaches to achieve this but if you want to use the same basis to move and draw then this approach may suit well.
First we use a function to get step values for x and y based on the angle (in radians):
function getSteps(angle) {
var cos = Math.cos(angle),
sin = Math.sin(angle);
return {
x: cos -sin,
y: sin + cos
}
}
Then using these steps values we can scale them to get an end point, or scale them gradually to animate an object along the line. A simple loop could look like this (just for example):
function loop() {
var x = i * step.x, // scale using i
y = i * step.y;
ctx.fillRect(200 + x, 200 + y, 2, 2); // add to origin start point 200, 200
i += 1; // increase i
if (i < length) requestAnimationFrame(loop);
}
Live demo
If you just want to draw a line at a certain angle you can do the following instead:
function lineAtAngle(x1, y1, length, angle) {
ctx.moveTo(x1, y1);
ctx.lineTo(x1 + length * Math.cos(angle), y1 + length * Math.sin(angle));
}
then stroke it.
Hope this helps!
If i guess right, i think you want the mouse act like a baseball bat, and you need to measure the current mouse angle, that is to store previous mouse position and do some math.
You have also to keep track if you allready handled current collision, to avoid the ball being 'sticky' and follow the mouse.
http://jsfiddle.net/gamealchemist/z3U8g/
var ctx = cv.getContext('2d');
var ball = {
x:200, y:200,
r : 30,
vx : 0.4, vy:0.4
}
// when mouse moved that distance, ball speed norm will be 1
var speedNorm = 10;
var collisionOnGoing = false;
function collide() {
var dist = sq(ball.x - mx) + sq (ball.y-my);
// too far from ball ?
if (dist > sq(ball.r)) {
collisionOnGoing = false;
return;
}
// return if collision allready handled
if (collisionOnGoing) return;
var mouseDist =Math.sqrt( sq(mx-lastmx) + sq(my-lastmy) );
// no collision if mouse too slow
if (mouseDist<speedNorm/5) return;
// launch the ball in current direction
// with a speed relative to the mouse speed.
var mouseAngle = Math.atan2(my-lastmy, mx-lastmx);
ball.vx= (mouseDist / speedNorm ) * Math.cos(mouseAngle);
ball.vy= (mouseDist / speedNorm ) * Math.sin(mouseAngle);
collisionOnGoing = true;
}
function animate() {
requestAnimationFrame(animate);
ctx.clearRect(0,0,400,400);
// collide ball with mouse
collide();
// draw ball
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.r, 0, 6.3);
ctx.fill();
ctx.closePath();
// move
ball.x+=ball.vx;
ball.y+=ball.vy;
// collide with screen
if (ball.x>400) ball.vx=-Math.abs(ball.vx);
if (ball.x<0) ball.vx=Math.abs(ball.vx);
if (ball.y>400) ball.vy=-Math.abs(ball.vy);
if (ball.y<0) ball.vy=Math.abs(ball.vy);
}
animate();
// --- Mouse handling ---
addEventListener('mousemove', mouseMove);
var mx=-1, my=-1, lastmx=-1, lastmy=-1;
var cvRect = cv.getBoundingClientRect();
var cvLeft = cvRect.left;
var cvTop = cvRect.top;
function mouseMove(e) {
lastmx = mx; lastmy=my;
mx=e.clientX - cvLeft;
my=e.clientY - cvTop;
}
function sq(x) { return x*x; }
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();
}