How to implement object rotation animation? - javascript

There is a falling balls code https://jsfiddle.net/d1e8x7wk/,
which is generated using canvas
Code also added to this editor
window.addEventListener('load', () => {
//---------------------------------------
// Set up ball options
//---------------------------------------
const imgBalls = [
'https://cdn.iconscout.com/icon/premium/png-256-thumb/basketball-2500972-2093649.png',
'https://cdn.iconscout.com/icon/premium/png-256-thumb/basketball-2500972-2093649.png',
'https://cdn.iconscout.com/icon/premium/png-256-thumb/basketball-2500972-2093649.png',
'https://cdn.iconscout.com/icon/premium/png-256-thumb/basketball-2500972-2093649.png',
'https://cdn.iconscout.com/icon/premium/png-256-thumb/basketball-2500972-2093649.png',
'https://cdn.iconscout.com/icon/premium/png-256-thumb/basketball-2500972-2093649.png'
]
let ballCount = imgBalls.length, // How many balls
DAMPING = 0.4, // Damping
GRAVITY = 0.01, // Gravity strength
SPEED = 5, // Ball speed
ballAdditionTime = 100, // How fast are balls added
ballSrc = imgBalls, // Ball image source
topOffset = 400, // Adjust this for initial ball spawn point
xOffset = 0, // left offset
yOffset = 0, // bottom offset
ballDensity = 20, // How dense are the balls
ball_1_size = 200, // Ball 1 size
ball_2_size = 180, // Ball 2 size
ball_3_size = 62, // Ball 6 size
canvasWidth = 1500, // Canvas width
canvasHeight = 1000, // Canvas height
stackBall = true, // Stack the balls (or false is overlap)
ballsLoaded = 0,
stopAnimation = false;
//---------------------------------------
// Canvas globals
//---------------------------------------
let canvas,
ctx,
TWO_PI = Math.PI * 2,
balls = [],
vel_x,
vel_y;
let rect = {
x: 0,
y: 0,
w: canvasWidth,
h: canvasHeight
};
//---------------------------------------
// do the animation
//---------------------------------------
window.requestAnimFrame =
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, ballAdditionTime);
};
//---------------------------------------
// set up the ball
//---------------------------------------
const Ball = function(x, y, radius, num) {
this.x = x;
this.y = y;
this.px = x;
this.py = y;
this.fx = 0;
this.fy = 0;
this.radius = radius;
this.num = num;
this.angle = 0;
// Different ball sizes
let random = Math.round(Math.random() * imgBalls.length)
if (random === 0) {
this.width = ball_1_size;
this.height = ball_1_size;
if (stackBall) {
this.radius = ball_1_size / 2;
}
} else if (random === 1 || random === 2) {
this.width = ball_2_size;
this.height = ball_2_size;
if (stackBall) {
this.radius = ball_2_size / 2;
}
} else {
this.width = ball_3_size;
this.height = ball_3_size;
if (stackBall) {
this.radius = ball_3_size / 2;
}
}
ctx.rotate(this.angle * Math.PI / 180);
};
//---------------------------------------
// Apply the physics
//---------------------------------------
Ball.prototype.apply_force = function(delta) {
delta *= delta;
this.fy += GRAVITY;
this.x += this.fx * delta;
this.y += this.fy * delta;
this.fx = this.fy = 0;
};
//---------------------------------------
// Newtonian motion algorithm
//---------------------------------------
Ball.prototype.velocity = function() {
var nx = this.x * 2 - this.px;
var ny = this.y * 2 - this.py;
this.px = this.x;
this.py = this.y;
this.x = nx;
this.y = ny;
};
//---------------------------------------
// Ball prototype
//---------------------------------------
Ball.prototype.draw = function(ctx) {
img = new Image();
img.src = imgBalls[this.num];
if (stackBall) {
ctx.drawImage(
img,
this.x - this.radius - xOffset,
this.y - this.radius - xOffset,
this.width,
this.height
);
} else {
ctx.drawImage(
img,
this.x - xOffset,
this.y - yOffset,
this.width,
this.height
);
}
};
//---------------------------------------
// resolve collisions (ball on ball)
//---------------------------------------
let resolve_collisions = function(ip) {
let i = balls.length;
while (i--) {
let ball_1 = balls[i];
let n = balls.length;
while (n--) {
if (n == i) continue;
let ball_2 = balls[n];
let diff_x = ball_1.x - ball_2.x;
let diff_y = ball_1.y - ball_2.y;
let length = diff_x * diff_x + diff_y * diff_y;
let dist = Math.sqrt(length);
let real_dist = dist - (ball_1.radius + ball_2.radius);
if (real_dist < 0) {
let vel_x1 = ball_1.x - ball_1.px;
let vel_y1 = ball_1.y - ball_1.py;
let vel_x2 = ball_2.x - ball_2.px;
let vel_y2 = ball_2.y - ball_2.py;
let depth_x = diff_x * (real_dist / dist);
let depth_y = diff_y * (real_dist / dist);
ball_1.x -= depth_x * 0.5;
ball_1.y -= depth_y * 0.5;
ball_2.x += depth_x * 0.5;
ball_2.y += depth_y * 0.5;
if (ip) {
let pr1 = (DAMPING * (diff_x * vel_x1 + diff_y * vel_y1)) / length;
let pr2 = (DAMPING * (diff_x * vel_x2 + diff_y * vel_y2)) / length;
vel_x1 += pr2 * diff_x - pr1 * diff_x;
vel_x2 += pr1 * diff_x - pr2 * diff_x;
vel_y1 += pr2 * diff_y - pr1 * diff_y;
vel_y2 += pr1 * diff_y - pr2 * diff_y;
ball_1.px = ball_1.x - vel_x1;
ball_1.py = ball_1.y - vel_y1;
ball_2.px = ball_2.x - vel_x2;
ball_2.py = ball_2.y - vel_y2;
}
}
}
}
};
//---------------------------------------
// Bounce off the walls
//---------------------------------------
let check_walls = function() {
let i = balls.length;
while (i--) {
let ball = balls[i];
if (ball.x < ball.radius) {
let vel_x = ball.px - ball.x;
ball.x = ball.radius;
ball.px = ball.x - vel_x * DAMPING;
} else if (ball.x + ball.radius > canvas.width) {
vel_x = ball.px - ball.x;
ball.x = canvas.width - ball.radius;
ball.px = ball.x - vel_x * DAMPING;
}
// Ball is new. So don't do collision detection until it hits the canvas. (with an offset to stop it snapping)
if (ball.y > 100) {
if (ball.y < ball.radius) {
let vel_y = ball.py - ball.y;
ball.y = ball.radius;
ball.py = ball.y - vel_y * DAMPING;
} else if (ball.y + ball.radius > canvas.height) {
vel_y = ball.py - ball.y;
ball.y = canvas.height - ball.radius;
ball.py = ball.y - vel_y * DAMPING;
}
}
}
};
//---------------------------------------
// Add a ball to the canvas
//---------------------------------------
let add_ball = function(num) {
let x = Math.random() * canvas.width;
let y = Math.random() * canvas.height;
let r = 30 + Math.random() * ballDensity;
let diff_x = x;
let diff_y = y;
let dist = Math.sqrt(diff_x * diff_x + diff_y * diff_y);
balls.push(new Ball(x, y, r, num));
};
//---------------------------------------
// iterate balls
//---------------------------------------
let update = function() {
let iter = 1;
let delta = SPEED / iter;
while (iter--) {
let i = balls.length;
while (i--) {
balls[i].apply_force(delta);
balls[i].velocity();
}
resolve_collisions();
check_walls();
i = balls.length;
while (i--) {
balls[i].velocity();
let ball = balls[i];
}
resolve_collisions();
check_walls();
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
i = balls.length;
while (i--) {
balls[i].draw(ctx);
}
requestAnimFrame(update);
};
//---------------------------------------
// Set up the canvas object
//---------------------------------------
function doBalls() {
stopAnimation = false;
canvas = document.getElementById("balls");
ctx = canvas.getContext("2d");
let $canvasDiv = document.querySelector(".section");
function respondCanvas() {
canvas.height = $canvasDiv.clientHeight;
canvas.width = $canvasDiv.clientWidth;
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
respondCanvas();
ballAdd();
}
function ballAdd() {
let count = 0;
let timer = setInterval(function() {
addBallTimer();
}, 100);
let addBallTimer = function() {
add_ball(count % ballCount);
count++;
if (count === ballCount) {
stopTimer();
}
};
let stopTimer = function() {
clearInterval(timer);
};
update();
}
doBalls();
});
.section {
position: relative;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: #000;
}
<div class="section">
<canvas id="balls"></canvas>
</div>
I'm trying to make a rotation the balls
const Ball = function(x, y, radius, num) {
this.x = x;
this.y = y;
this.px = x;
this.py = y;
this.fx = 0;
this.fy = 0;
this.radius = radius;
this.num = num;
this.angle = 0;
// Different ball sizes
let random = Math.round(Math.random() * imgBalls.length)
if (random === 0) {
this.width = ball_1_size;
this.height = ball_1_size;
if (stackBall) {
this.radius = ball_1_size / 2;
}
} else if (random === 1 || random === 2) {
this.width = ball_2_size;
this.height = ball_2_size;
if (stackBall) {
this.radius = ball_2_size / 2;
}
} else {
this.width = ball_3_size;
this.height = ball_3_size;
if (stackBall) {
this.radius = ball_3_size / 2;
}
}
ctx.rotate(this.angle * Math.PI / 180);
};
After reading the information,
I wrote this code, but it does not work for me
ctx.rotate(this.angle * Math.PI / 180);
I must have misunderstood how to do this, tell me how to properly rotate the balls
Thanks in advance

You will need to
increase the angle at some point in your code
apply a rotation transform to rotate around each ball center before each drawImage call
ctx.save();
ctx.translate(+this.x, +this.y);
ctx.rotate(this.angle++ * Math.PI / 180);
ctx.translate(-this.x, -this.y);
ctx.drawImage
ctx.restore()
ctx.save() and ctx.restore() is called to restore the canvas transform after each draw call.
Please read
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/rotate#rotating_a_shape_around_its_center
for details on applying transforms to Canvas.

Related

How to evenly spread circles gravitating towards a point?

I have created a full demonstration of the problem I'm experiencing below:
const rng = (min, max) => Math.random() * (max - min + 1) + min;
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
ctx.strokeStyle = "#000";
ctx.lineWidth = 4;
ctx.fillStyle = "#ff0000";
function drawCircle(c) {
ctx.beginPath();
ctx.arc(c.x, c.y, c.r, 0, 2 * Math.PI);
ctx.stroke();
ctx.fill();
}
class Circle {
constructor(x, y, r) {
this.x = x;
this.y = y;
this.r = r;
this.vX = 0;
this.vY = 0;
}
}
const circles = [];
for (let i = 0; i < 300; i++) {
circles.push(new Circle(rng(0, canvas.width), rng(0, canvas.height), rng(12, 14)));
}
function processCollision(c1, c2) {
const deltaX = c2.x - c1.x;
const deltaY = c2.y - c1.y;
const sumRadius = c1.r + c2.r;
const centerDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (centerDistance === 0 || centerDistance > sumRadius) { return; } // not colliding
const circleDistance = centerDistance - sumRadius;
const aX = deltaX / centerDistance;
const aY = deltaY / centerDistance;
const force = 5;
c1.vX += aX * circleDistance * force;
c1.vY += aY * circleDistance * force;
}
function update() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (const c of circles) {
c.vX = (canvas.width / 2) - c.x; // move towards center x
c.vY = (canvas.height / 2) - c.y; // move towards center y
}
for (const c1 of circles) {
for (const c2 of circles) {
c1 !== c2 && processCollision(c1, c2);
}
}
for (const c of circles) {
c.x += c.vX * (1 / 60);
c.y += c.vY * (1 / 60);
drawCircle(c);
}
}
setInterval(update, 16.6666);
<canvas width="600" height="600" style="border:1px solid #d3d3d3;">
Notice how all the circles gravitate around the center. However, they are all heavily colliding with one another. I would like to modify the processCollision function such that the circles no longer significantly overlap one another and instead are roughly evenly spread around the center point.
I tried increasing the force variable, but unfortunately while this does indeed cause greater spread, it also creates lot of shaky and jerky movement. The solution must be smooth, similar to the example above. I have been messing with this for weeks but unfortunately cannot seem to come to a solution.
This seems to behave the way you probably want (or close to it)... It uses a control theory model combined with a physics model, and one needs to tweak the constants k0, k1, strength, buffer, step_size...
const rng = (min, max) => Math.random() * (max - min + 1) + min;
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
ctx.strokeStyle = '#000';
ctx.lineWidth = 4;
ctx.fillStyle = '#ff0000';
const k0 = 1.5;
const k1 = 5;
const strength = 1000000;
const buffer = 2;
class Disc {
constructor(x, y, r) {
this.x = x;
this.y = y;
this.r = r;
this.vX = 0;
this.vY = 0;
return;
}
drawDisc(ctx) {
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI);
ctx.stroke();
ctx.fill();
return;
}
addVelocity(step_size) {
this.x = this.x + step_size * this.vX;
this.y = this.y + step_size * this.vY;
return;
}
addAcceleration(aX, aY, step_size) {
this.vX = this.vX + step_size * aX;
this.vY = this.vY + step_size * aY;
return;
}
applyCentralAcceleration(step_size) {
const accelX = -k1 * this.vX - k0 * (this.x - canvas.width / 2);
const accelY = -k1 * this.vY - k0 * (this.y - canvas.height / 2);
this.addAcceleration(accelX, accelY, step_size);
return;
}
applyInteractionAcceleration(that, step_size) {
let dX = this.x - that.x;
let dY = this.y - that.y;
const dist = dX * dX + dY * dY;
const magnitude = strength / (dist - (this.r + buffer + that.r) ** 2) ** 2;
dX = magnitude * dX;
dY = magnitude * dY;
this.addAcceleration(dX, dY, step_size);
return;
}
}
class System {
constructor(numDiscs) {
this.n = numDiscs;
this.discs = [];
for (let i = 0; i < numDiscs; i++) {
this.discs.push(
new Disc(rng(0, canvas.width), rng(0, canvas.height), rng(6, 7))
);
}
return;
}
applyCentralAcceleration(step_size) {
for (let i = 0; i < this.n; i++) {
this.discs[i].applyCentralAcceleration(step_size);
}
}
applyInteractionAcceleration(step_size) {
for (let i = 0; i < this.n; i++) {
for (let j = 0; j < this.n; j++) {
if (i === j) {
continue;
}
this.discs[i].applyInteractionAcceleration(this.discs[j], step_size);
}
}
}
applyVelocity(step_size) {
for (let i = 0; i < this.n; i++) {
this.discs[i].addVelocity(step_size);
}
}
updateSystemState(step_size) {
this.applyCentralAcceleration(step_size);
this.applyInteractionAcceleration(step_size);
this.applyVelocity(step_size);
return;
}
drawSystemDiscs() {
for (let i = 0; i < this.n; i++) {
this.discs[i].drawDisc(ctx);
}
}
}
systemOfDiscs = new System(50);
function update() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const step_size = 1 / 100;
systemOfDiscs.updateSystemState(step_size);
systemOfDiscs.drawSystemDiscs();
return;
}
setInterval(update, 16.6666);
<canvas width="300" height="300" style="border: 1px solid #d3d3d3"></canvas>

how to load next string when i erase particle text?

I am new to vanilla js and webgl content. I created a particle text which get distorted on mouse hover. and whose particles are connected with each other with a line. the code is as follows.
const canvas = document.getElementById("canvas1");
const dpr = window.devicePixelRatio;
//console.log(dpr);
const ctx = canvas.getContext("2d");
ctx.scale(dpr, dpr);
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let particleArray = [];
let adjustX = 10;
let adjustY = 0;
//handle mouse
const mouse = {
x: null,
y: null,
radius: 30,
};
window.addEventListener("mousemove", function (event) {
mouse.x = event.x;
mouse.y = event.y;
//console.log(mouse.x, mouse.y);
});
ctx.fillStyle = "white";
ctx.font = "35px verdana";
ctx.fillText("aman ", 0, 30);
const textCoordinates = ctx.getImageData(0, 0, 100, 100);
class Particle {
constructor(x, y) {
this.x = x + Math.random() * 6 - 3;
this.y = y + Math.random() * 6 - 3;
this.size = 1;
this.baseX = this.x;
this.baseY = this.y;
this.density = Math.random() * 10 + 50;
}
draw() {
ctx.fillStyle = "white";
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.closePath();
ctx.fill();
}
//distance between mouse and particle
update() {
let dx = mouse.x - this.x;
let dy = mouse.y - this.y;
let distance = Math.sqrt(dx * dx + dy * dy);
let forceDirectionX = dx / distance;
let forceDirectionY = dy / distance;
let maxDistance = mouse.radius;
let force = (maxDistance - distance) / maxDistance;
let directionX = forceDirectionX * force * this.density;
let directionY = forceDirectionY * force * this.density;
if (distance < mouse.radius) {
this.x -= directionX;
this.y -= directionY;
} else {
if (this.x !== this.baseX) {
let dx = this.x - this.baseX;
this.x += dx / 10;
}
if (this.y !== this.baseY) {
let dy = this.y - this.baseY;
this.y += dy / 10;
}
}
}
}
//console.log(textCoordinates);
function init() {
particleArray = [];
for (let y = 0, y2 = textCoordinates.height; y < y2; y++) {
for (let x = 0, x2 = textCoordinates.width; x < x2; x++) {
if (
textCoordinates.data[y * 4 * textCoordinates.width + x * 4 + 3] > 128
) {
let positionX = x + adjustX;
let positionY = y + adjustY;
particleArray.push(new Particle(positionX * 4, positionY * 4));
}
}
}
}
init();
//console.log(particleArray);
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let i = 0; i < particleArray.length; i++) {
particleArray[i].draw();
particleArray[i].update();
}
connect();
requestAnimationFrame(animate);
}
animate();
function connect() {
let opacityValue = 1;
for (let a = 0; a < particleArray.length; a++) {
for (let b = a; b < particleArray.length; b++) {
let dx = particleArray[a].x - particleArray[b].x;
let dy = particleArray[a].y - particleArray[b].y;
let distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 14) {
opacityValue = 0.5;
ctx.strokeStyle = "rgb(200,150,160," + opacityValue + ")";
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(particleArray[a].x, particleArray[a].y);
ctx.lineTo(particleArray[b].x, particleArray[b].y);
ctx.stroke();
}
}
}
}
now what I want to do is that, when I erase this particle text, next text must appear. just like on this website https://animal.cc/ .
can anyone please help me in this :))
(This isn't three.js related)
When you determine "this particle text" has been erased, you can do this in animate():
ctx.fillText("NEW ", 0, 30);
textCoordinates = ctx.getImageData(0, 0, 100, 100);
init();
EDIT: code for comment (below)
for (let y = 0, y2 = textCoordinates.height; y < y2; y++) {
for (let x = 0, x2 = textCoordinates.width; x < x2; x++) {

Javascript text particle in canvas slow in firefox but not on chrome (requestAnimationFrame ?)

I'm struggling with the implementation of a text particle in javascript.
My code is working fine with chromium-based browsers (Chromium: 84) but is unexpectedly slow on firefox (79.0).
I've searched a lot, but still don't find a solution and where the problem is.(This is pretty much linked to this Javascript Animation(Canvas) not working in firefox, Edge but works on Chrome).
Here is my codepen:
https://codepen.io/jgazeau/pen/LYNYPYB
function getRandomInt(max) {
return Math.floor(Math.random() * Math.floor(max));
}
function getCssVar(v) {
return getComputedStyle(document.documentElement).getPropertyValue(v);
}
let text404 = '404';
let canvas = document.getElementById('scene');
let ch = canvas.height = canvas.getBoundingClientRect().height;
let cw = canvas.width = canvas.getBoundingClientRect().width;
let sceneBackground = getCssVar('--scene-background');
let context = canvas.getContext('2d');
let previousMouseCoord = {x:0, y:0};
let mouseCoord = {x:0, y:0};
let sceneResize = false;
let particlesCount = 0;
let particles = [];
let colors = [
getCssVar('--particle-color-1'),
getCssVar('--particle-color-2'),
getCssVar('--particle-color-3'),
getCssVar('--particle-color-4'),
getCssVar('--particle-color-5')
];
const dpi = 200;
let Particle = function(x, y) {
this.x = getRandomInt(cw);
this.y = getRandomInt(ch);
this.coord = {x:x, y:y};
this.r = Math.min((getRandomInt((cw / dpi)) + 1), 6);
this.vx = (Math.random() - 0.5) * 100;
this.vy = (Math.random() - 0.5) * 100;
this.accX = 0;
this.accY = 0;
this.friction = Math.random() * 0.05 + 0.90;
this.color = colors[Math.floor(Math.random() * 6)];
}
Particle.prototype.render = function(isDisableMouse) {
this.accX = (this.coord.x - this.x) / 100;
this.accY = (this.coord.y - this.y) / 100;
this.vx += this.accX;
this.vy += this.accY;
this.vx *= this.friction;
this.vy *= this.friction;
this.x += this.vx;
this.y += this.vy;
if (!isDisableMouse) {
let a = this.x - mouseCoord.x;
let b = this.y - mouseCoord.y;
var distance = Math.sqrt(a * a + b * b);
if(distance < (cw / 15)) {
this.accX = (this.x - mouseCoord.x) / 100;
this.accY = (this.y - mouseCoord.y) / 100;
this.vx += this.accX;
this.vy += this.accY;
}
}
context.fillStyle = this.color;
context.beginPath();
context.arc(this.x, this.y, this.r, 0, Math.PI * 2, false);
context.fill();
context.closePath();
}
function onmouseCoordMove(e) {
mouseCoord.x = e.clientX;
mouseCoord.y = e.clientY;
}
function onTouchMove(e) {
if(e.touches.length > 0 ) {
mouseCoord.x = e.touches[0].clientX;
mouseCoord.y = e.touches[0].clientY;
}
}
function onTouchEnd() {
mouseCoord.x = -9999;
mouseCoord.y = -9999;
}
function initScene() {
ch = canvas.height = canvas.getBoundingClientRect().height;
cw = canvas.width = canvas.getBoundingClientRect().width;
context.clearRect(0, 0, canvas.width, canvas.height);
context.font = 'bold ' + (cw / 5) + 'px sans-serif';
context.fillStyle = sceneBackground;
context.textAlign = 'center';
context.fillText(text404, cw / 2, ch / 2);
let imageData = context.getImageData(0, 0, cw, ch).data;
context.clearRect(0, 0, canvas.width, canvas.height);
context.globalCompositeOperation = 'screen';
particles = [];
for(let y = 0; y < ch; y += Math.round(cw / dpi)) {
for(let x = 0; x < cw; x += Math.round(cw / dpi)) {
if(imageData[((x + y * cw) * 4) + 3] > 128){
particles.push(new Particle(x, y));
}
}
}
particlesCount = particles.length;
}
function renderScene() {
context.clearRect(0, 0, canvas.width, canvas.height);
let isDisableMouse = false;
if ((previousMouseCoord.x === mouseCoord.x) && (previousMouseCoord.x === mouseCoord.x)) {
isDisableMouse = true;
} else {
previousMouseCoord.x = mouseCoord.x;
previousMouseCoord.x = mouseCoord.x;
isDisableMouse = false;
}
for (let i = 0; i < particlesCount; i++) {
particles[i].render(isDisableMouse);
}
requestAnimationFrame(renderScene);
};
document.addEventListener('DOMContentLoaded', function() {
initScene();
renderScene();
window.addEventListener('mousemove', onmouseCoordMove);
window.addEventListener('touchmove', onTouchMove);
window.addEventListener('touchend', onTouchEnd);
window.addEventListener('resize', function() {
if (!sceneResize) {
requestAnimationFrame(function() {
initScene();
sceneResize = false;
});
sceneResize = true;
}
});
});
Can anyone know where this slow issue could come from ?
Thanks a lot for your help.

Adding time elapsed to a canvas game in Javascript

I am having a problem with integrating a simple timer to a game I am trying to build for a practice exercise. I have attempted to search for this solution on the internet but my basic knowledge of .js leaves me stuck at this problem. Can anyone help me with a solution to this?
My codepen is located here: https://codepen.io/jankyvision/pen/PoqYEXV
var c = document.createElement("canvas");
var ctx = c.getContext("2d");
c.width = 720;
c.height = 480;
document.body.appendChild(c);
var perm = [];
while (perm.length < 255){
while(perm.includes(val = Math.floor(Math.random()*255)));
perm.push(val);
}
var lerp = (a,b,t) => a + (b-a) * (1-Math.cos(t*Math.PI))/2;
var noise = x=>{
x = x * 0.01 % 254;
return lerp(perm[Math.floor(x)], perm[Math.ceil(x)], x - Math.floor(x));
}
var Player = function(){
this.x = c.width/2;
this.y = 0;
this.ySpeed = 0;
this.rot = 0;
this.rSpeed = 0;
this.img = new Image();
this.img.src = "./images/moto.png";
this.draw = function(){
var p1 = c.height - noise(t + this.x) * 0.25;
var p2 = c.height - noise(t+5 + this.x) * 0.25;
var grounded = 0;
if(p1-12 > this.y){
this.ySpeed += 0.1;
}else{
this.ySpeed -= this.y - (p1-12);
this.y = p1 - 12;
grounded = 1;
}
var angle = Math.atan2((p2-12) - this.y, (this.x+5) - this.x);
this.y += this.ySpeed;
if(!playing || grounded && Math.abs(this.rot) > Math.PI * 0.5){
playing = false;
this.rSpeed = 5;
k.ArrowUp = 1;
this.x -= speed * 5;
}
if(grounded && playing){
this.rot -= (this.rot - angle) * 0.65;
this.rSpeed = this.rSpeed - (angle - this.rot);
}
this.rSpeed += (k.ArrowLeft - k.ArrowRight) * 0.05;
this.rot -= this.rSpeed * 0.1;
if(this.rot > Math.PI) this.rot = -Math.PI;
if(this.rot < -Math.PI) this.rot = Math.PI;
ctx.save();
ctx.translate(this.x, this.y - 3);
ctx.rotate(this.rot);
ctx.drawImage(this.img, -15, -15, 60, 30);
ctx.restore();
}
}
var player = new Player();
var t = 0;
var speed = 0;
var playing = true;
var k = {ArrowUp:0, ArrowDown:0, ArrowLeft:0, ArrowRight:0};
function loop(){
speed -= (speed - (k.ArrowUp - k.ArrowDown)) * 0.01;
t += 10 * speed;
ctx.fillStyle = "#8D5BC2";
ctx.fillRect(0,0,c.width, c.height);
ctx.fillStyle = "rgba(0,0,0,0.1)";
ctx.beginPath();
ctx.moveTo(0, c.height);
for (let i = 0; i < c.width; i++)
ctx.lineTo(i, c.height*0.8 - noise(t + i*5) * 0.25);
ctx.lineTo(c.width, c.height);
ctx.fill();
ctx.fillStyle = "#444";
ctx.beginPath();
ctx.moveTo(0, c.height);
for (let i = 0; i < c.width; i++)
ctx.lineTo(i, c.height - noise(t + i) * 0.25);
ctx.lineTo(c.width, c.height);
ctx.fill();
player.draw();
if(player.x < 0)
restart();
requestAnimationFrame(loop);
}
onkeydown = d=> k[d.key] = 1;
onkeyup = d=> k[d.key] = 0;
function restart(){
player = new Player();
t = 0;
speed = 0;
playing = true;
k = {ArrowUp:0, ArrowDown:0, ArrowLeft:0, ArrowRight:0};
}
loop();
var instructions = document.createElement("div");
instructions.innerHTML += "[up] [down] = accelerate <br> [Left] [Rigth] = rotate";
document.body.appendChild(instructions);
With some help from https://stackoverflow.com/a/16255190/1309377 you are looking at a pretty simple function that uses ctx.fillText() to draw the amount of elapsed seconds onto the canvas.
You want to use the Date() object when doing a timer as it is more accurate than setTimeout or setInterval.
On restart you just set startTime to the current time again.
var c = document.createElement("canvas");
var ctx = c.getContext("2d");
var startTime = new Date();
c.width = 720;
c.height = 480;
document.body.appendChild(c);
var perm = [];
while (perm.length < 255) {
while (perm.includes(val = Math.floor(Math.random() * 255)));
perm.push(val);
}
var lerp = (a, b, t) => a + (b - a) * (1 - Math.cos(t * Math.PI)) / 2;
var noise = x => {
x = x * 0.01 % 254;
return lerp(perm[Math.floor(x)], perm[Math.ceil(x)], x - Math.floor(x));
}
function drawElapsedTime() {
var elapsed = parseInt((new Date() - startTime) / 1000);
ctx.save();
ctx.beginPath();
ctx.fillStyle = "black";
ctx.font = "14px Verdana"
// draw the running time at half opacity
ctx.globalAlpha = 0.50;
ctx.fillText(elapsed + " seconds", 30,30);
ctx.restore();
}
var Player = function() {
this.x = c.width / 2;
this.y = 0;
this.ySpeed = 0;
this.rot = 0;
this.rSpeed = 0;
this.img = new Image();
this.img.src = "https://upload.wikimedia.org/wikipedia/en/3/3b/SpongeBob_SquarePants_character.svg";
this.draw = function() {
var p1 = c.height - noise(t + this.x) * 0.25;
var p2 = c.height - noise(t + 5 + this.x) * 0.25;
var grounded = 0;
if (p1 - 12 > this.y) {
this.ySpeed += 0.1;
} else {
this.ySpeed -= this.y - (p1 - 12);
this.y = p1 - 12;
grounded = 1;
}
var angle = Math.atan2((p2 - 12) - this.y, (this.x + 5) - this.x);
this.y += this.ySpeed;
if (!playing || grounded && Math.abs(this.rot) > Math.PI * 0.5) {
playing = false;
this.rSpeed = 5;
k.ArrowUp = 1;
this.x -= speed * 5;
}
if (grounded && playing) {
this.rot -= (this.rot - angle) * 0.65;
this.rSpeed = this.rSpeed - (angle - this.rot);
}
this.rSpeed += (k.ArrowLeft - k.ArrowRight) * 0.05;
this.rot -= this.rSpeed * 0.1;
if (this.rot > Math.PI) this.rot = -Math.PI;
if (this.rot < -Math.PI) this.rot = Math.PI;
ctx.save();
ctx.translate(this.x, this.y - 3);
ctx.rotate(this.rot);
ctx.drawImage(this.img, -15, -15, 60, 30);
ctx.restore();
}
}
var player = new Player();
var t = 0;
var speed = 0;
var playing = true;
var k = {
ArrowUp: 0,
ArrowDown: 0,
ArrowLeft: 0,
ArrowRight: 0
};
function loop() {
speed -= (speed - (k.ArrowUp - k.ArrowDown)) * 0.01;
t += 10 * speed;
ctx.fillStyle = "#8D5BC2";
ctx.fillRect(0, 0, c.width, c.height);
ctx.fillStyle = "rgba(0,0,0,0.1)";
ctx.beginPath();
ctx.moveTo(0, c.height);
for (let i = 0; i < c.width; i++)
ctx.lineTo(i, c.height * 0.8 - noise(t + i * 5) * 0.25);
ctx.lineTo(c.width, c.height);
ctx.fill();
ctx.fillStyle = "#444";
ctx.beginPath();
ctx.moveTo(0, c.height);
for (let i = 0; i < c.width; i++)
ctx.lineTo(i, c.height - noise(t + i) * 0.25);
ctx.lineTo(c.width, c.height);
ctx.fill();
player.draw();
drawElapsedTime();
if (player.x < 0)
restart();
requestAnimationFrame(loop);
}
onkeydown = d => k[d.key] = 1;
onkeyup = d => k[d.key] = 0;
function restart() {
player = new Player();
startTime = new Date();
t = 0;
speed = 0;
playing = true;
k = {
ArrowUp: 0,
ArrowDown: 0,
ArrowLeft: 0,
ArrowRight: 0
};
}
loop();
var instructions = document.createElement("div");
instructions.innerHTML += "[up] [down] = accelerate <br> [Left] [Rigth] = rotate";
document.body.appendChild(instructions);
var secs = 0;
setInterval(function(){secs++},1000);
this.draw = function(){
// Fill in a rectangle to put the time inside of
ctx.fillStyle="#000000";
ctx.fillRect(10,10,55,25);
ctx.fillStyle="#8D5BC2";
ctx.font = "12px";
// Format the number of seconds - 0:00
timestr = Math.floor(secs/60) + ":";
timestr += ((secs%60<10)&&"0") + secs%60;
// Write the time string over the rectangle
ctx.fillText(timestr,25,25);

JavaScript Canvas Fabric Animation: set Background Image

I found this amazing plugin which simulates the physics of fabric: http://codepen.io/anon/pen/mxjKC
document.getElementById('close').onmousedown = function(e) {
e.preventDefault();
document.getElementById('info').style.display = 'none';
return false;
};
// settings
var physics_accuracy = 3,
mouse_influence = 20,
mouse_cut = 5,
gravity = 1200,
cloth_height = 30,
cloth_width = 50,
start_y = 20,
spacing = 7,
tear_distance = 60;
window.requestAnimFrame =
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60);
};
var canvas,
ctx,
cloth,
boundsx,
boundsy,
mouse = {
down: false,
button: 1,
x: 0,
y: 0,
px: 0,
py: 0
};
var Point = function (x, y) {
this.x = x;
this.y = y;
this.px = x;
this.py = y;
this.vx = 0;
this.vy = 0;
this.pin_x = null;
this.pin_y = null;
this.constraints = [];
};
Point.prototype.update = function (delta) {
if (mouse.down) {
var diff_x = this.x - mouse.x,
diff_y = this.y - mouse.y,
dist = Math.sqrt(diff_x * diff_x + diff_y * diff_y);
if (mouse.button == 1) {
if (dist < mouse_influence) {
this.px = this.x - (mouse.x - mouse.px) * 1.8;
this.py = this.y - (mouse.y - mouse.py) * 1.8;
}
} else if (dist < mouse_cut) this.constraints = [];
}
this.add_force(0, gravity);
delta *= delta;
nx = this.x + ((this.x - this.px) * .99) + ((this.vx / 2) * delta);
ny = this.y + ((this.y - this.py) * .99) + ((this.vy / 2) * delta);
this.px = this.x;
this.py = this.y;
this.x = nx;
this.y = ny;
this.vy = this.vx = 0
};
Point.prototype.draw = function () {
if (!this.constraints.length) return;
var i = this.constraints.length;
while (i--) this.constraints[i].draw();
};
Point.prototype.resolve_constraints = function () {
if (this.pin_x != null && this.pin_y != null) {
this.x = this.pin_x;
this.y = this.pin_y;
return;
}
var i = this.constraints.length;
while (i--) this.constraints[i].resolve();
this.x > boundsx ? this.x = 2 * boundsx - this.x : 1 > this.x && (this.x = 2 - this.x);
this.y < 1 ? this.y = 2 - this.y : this.y > boundsy && (this.y = 2 * boundsy - this.y);
};
Point.prototype.attach = function (point) {
this.constraints.push(
new Constraint(this, point)
);
};
Point.prototype.remove_constraint = function (constraint) {
this.constraints.splice(this.constraints.indexOf(constraint), 1);
};
Point.prototype.add_force = function (x, y) {
this.vx += x;
this.vy += y;
};
Point.prototype.pin = function (pinx, piny) {
this.pin_x = pinx;
this.pin_y = piny;
};
var Constraint = function (p1, p2) {
this.p1 = p1;
this.p2 = p2;
this.length = spacing;
};
Constraint.prototype.resolve = function () {
var diff_x = this.p1.x - this.p2.x,
diff_y = this.p1.y - this.p2.y,
dist = Math.sqrt(diff_x * diff_x + diff_y * diff_y),
diff = (this.length - dist) / dist;
if (dist > tear_distance) this.p1.remove_constraint(this);
var px = diff_x * diff * 0.5;
var py = diff_y * diff * 0.5;
this.p1.x += px;
this.p1.y += py;
this.p2.x -= px;
this.p2.y -= py;
};
Constraint.prototype.draw = function () {
ctx.moveTo(this.p1.x, this.p1.y);
ctx.lineTo(this.p2.x, this.p2.y);
};
var Cloth = function () {
this.points = [];
var start_x = canvas.width / 3 - cloth_width * spacing / 2;
for (var y = 0; y <= cloth_height; y++) {
for (var x = 0; x <= cloth_width; x++) {
var p = new Point(start_x + x * spacing, start_y + y * spacing);
x != 0 && p.attach(this.points[this.points.length - 1]);
y == 0 && p.pin(p.x, p.y);
y != 0 && p.attach(this.points[x + (y - 1) * (cloth_width + 1)])
this.points.push(p);
}
}
};
Cloth.prototype.update = function () {
var i = physics_accuracy;
while (i--) {
var p = this.points.length;
while (p--) this.points[p].resolve_constraints();
}
i = this.points.length;
while (i--) this.points[i].update(.016);
};
Cloth.prototype.draw = function () {
ctx.beginPath();
var i = cloth.points.length;
while (i--) cloth.points[i].draw();
ctx.stroke();
};
function update() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
cloth.update();
cloth.draw();
requestAnimFrame(update);
}
function start() {
canvas.onmousedown = function (e) {
mouse.button = e.which;
mouse.px = mouse.x;
mouse.py = mouse.y;
var rect = canvas.getBoundingClientRect();
mouse.x = e.clientX - rect.left,
mouse.y = e.clientY - rect.top,
mouse.down = true;
e.preventDefault();
};
canvas.onmouseup = function (e) {
mouse.down = false;
e.preventDefault();
};
canvas.onmousemove = function (e) {
mouse.px = mouse.x;
mouse.py = mouse.y;
var rect = canvas.getBoundingClientRect();
mouse.x = e.clientX - rect.left,
mouse.y = e.clientY - rect.top,
e.preventDefault();
};
canvas.oncontextmenu = function (e) {
e.preventDefault();
};
boundsx = canvas.width - 1;
boundsy = canvas.height - 1;
ctx.strokeStyle = 'red';
cloth = new Cloth();
update();
}
window.onload = function () {
canvas = document.getElementById('c');
ctx = canvas.getContext('2d');
canvas.width = 1120;
canvas.height = 700;
canvas.style.width = '560px';
canvas.style.height = '350px';
ctx.scale(2,2);
start();
};
<div id=info>
<input type="button" value="close" id="close"></input>
<canvas id="c"></canvas>
</div>
I would love to know how to use it for an image instead of the net but I don't know where to start at all.
What is the theory behind that - where could I place the image?
You can not put an image on that mesh because the resulting squares are not 2d. This answer gives a little detail as to why.
To do what you want you need to use webGL. It is a straightforward conversion, the cloth is a mesh (vertices and polygons connecting the vertices) where each vertex will hold a texture coordinate and you let the sim run as is. There should be plenty of examples of how to map a image onto a mesh in stackoverflow. You might consider using three.js if you are new to 3D and coding.
Here is an example using three.js and a verlet cloth with texture mapped onto it.

Categories