There is not enough information in your question to get a clear picture of where there may be a problem.
Your code
You code does not have any glaring performance issues. Some minor changes would be...
Use for
loops rather than forEach
. (Minor performance increase but when animating its always good to use the fastest style)
Don't use 3D transforms if you are only doing 2D animation. Better yet, if only 2d dont use the transform ate all. Set the
style.positionto
absoluteand use the
top,
left` CSS style attributes to set position.
You only use jQuery to get the onload
event. Its just bloat so don't load it if you don't need it.
Animating elements.
Some points related to animating elements.
Its not just the elements that you are animating that add to the total load of an animation. Other elements may require rendering (compositing) and they may also be triggering reflow/s. Composites and reflows are expensive operations
The order of the animated elements in relationship to other content can have a big impact on performance due to inefficient compositing. For example you may be animating items A and B, and on the page you have c, A, d, B, e (where c,d,e are other elements) . In the worst case the compositing of the content will be as follows... render c, composite c with background, render A, composite A with previous composite, render b, composite b with previous composite, and so on.
So you just change and move 2 items but that may trigger 5 renders and 5 composites, and depending on the layout, they may be the 5 full screen composites and renders each frame.
Are you using filters? Depending on the filters (and the GPU's abilities) they can add to the workload. Use filters sparingly, don't make more than one change to filters during a frame.
Can all the image and pixel related content fit into GPU RAM. If not reduce the number of pixels. Low end devices can have very low amounts of GPU RAM and if you exceed the capacity of the GPU the browser will swap it in and out of CPU RAM as needed. This is SLOW... very SLOW...
Take charge of the rendering.
It can be very difficult to locate animation bottle necks when animating elements.
One way to remove the rendering/compositing ambiguity is to use the canvas
. You then get to efficiently control the rendering compositing process. The canvas 2D and WebGL API's use the same code (there abouts) as the browsers rendering and compositing systems.
With some minor changes your animation items can be rendered on a canvas, needing only one render pass and one composite (if you also render the background you can eliminate composite)
The snippet is an example of canvas rendering performance. There are 500 bugs
each rendered with a full transform scale, position, and rotated. This you would never be able to do using HTML elements.
requestAnimationFrame(update); // Start animation when ready
const image = new Image;
image.src = "https://i.stack.imgur.com/C7qq2.png?s=328&g=1";
const canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.style.position = "absolute";
canvas.style.top = "0px";
canvas.style.left = "0px";
document.body.appendChild(canvas);
const mouse = {x : 0, y : 0}
document.addEventListener("mousemove", (e) => {
mouse.x = e.pageX;
mouse.y = e.pageY;
});
var w,h;
function resize(){
w = canvas.width = innerWidth;
h = canvas.height = innerHeight;
}
resize();
addEventListener("resize",resize);
const rand = (min = 1, max = min + (min = 0)) => Math.random() * (max - min) + min;
const DO = (count,callback) => { while (count--) { callback(count) } }
const sprites = [];
DO(500,()=>{
sprites.push({
x : rand(w),
y : rand(h),
xr : 0,
yr : 0, // actual position of sprite
r : rand(Math.PI * 2),
scale : rand(0.1,0.25),
dx : rand(-2,2),
dy : rand(-2,2),
dr : rand(-0.1,0.1),
});
});
// Draws a sprite.
function drawImage(image, spr){
ctx.setTransform(spr.scale, 0, 0, spr.scale, spr.xr, spr.yr); // sets scales and origin
ctx.rotate(spr.r);
ctx.drawImage(image, -image.width / 2, -image.height / 2);
}
// Main animation loop
function update(time){
var ihM,iwM,dx,dy,spr,i,iw,ih,dist;
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(0,0,w,h);
const runHalf = time / 3000;
if(image.complete){
iw = image.width;
ih = image.height;
const size = (iw * iw + ih * ih) / 2;
for(i = 0; i < sprites.length; i ++){
spr = sprites[i];
dx = mouse.x - spr.xr;
dy = mouse.y - spr.yr;
dist = dx * dx + dy * dy;
if(dist/ spr.scale < size){
const dir = -Math.atan2(dy,dx) -0.2 + rand(0.4);
const speed = rand(18);
spr.dx += Math.cos(dir) * speed;
spr.dy += Math.sin(dir) * speed;
}else if(((i + runHalf) | 0) % 2){ // half run away
spr.dx += dx * 0.001 + Math.cos(spr.r) * 0.1;
spr.dy += dy * 0.001 + Math.sin(spr.r) * 0.1;
}else{
spr.dx += -dx * 0.001 + Math.cos(spr.r) * 0.1;
spr.dy += -dy * 0.001 + Math.sin(spr.r) * 0.1;
}
spr.dx *= 0.99;
spr.dy *= 0.99;
// limit speed
dist = spr.dx * spr.dx + spr.dy * spr.dy;
if(dist > 54){
spr.dx *= 0.9;
spr.dy *= 0.9;
}
spr.x += spr.dx;
spr.y += spr.dy;
spr.r += spr.dr;
iwM = iw * spr.scale * 2 + w;
ihM = ih * spr.scale * 2 + h;
spr.xr = ((spr.x % iwM) + iwM) % iwM - iw * spr.scale;
spr.yr = ((spr.y % ihM) + ihM) % ihM - ih * spr.scale;
drawImage(image,spr);
}
}
requestAnimationFrame(update);
}