Reputation: 519
I'm trying to make a starring night with twinkling stars in css3 + Javascript, however, my animation is consuming a lot of CPU, the main animation:
@for $i from 0 through 400 {
.star:nth-child(#{$i}) {
$star-size: (random() * (1-4) +4) + px;
top: (random(100)) + vh;
left: (random(100)) + vw;
width: $star-size;
height: $star-size;
animation: blinker 1.2s alternate infinite ease-in-out;
animation-delay: (random(30) / 10) + s;
transform: scale(0.2);
}
}
@keyframes blinker {
100% {
transform: scale(1);
}
}
the full code: https://jsfiddle.net/sam7krx0/ is there any way to make this code perform better?
Edit:
tried with translateZ(0)
and with will-change: transform
but the animation still being rendered by the CPU.
https://jsfiddle.net/8hn97kcx/2/
Edit 2: It seems that firefox might be the problem, while testing on chrome the animation uses way less CPU.
Edit 3:
profile of the fiddle above running on firefox developer edition 69.0b4:
Upvotes: 2
Views: 842
Reputation: 44086
The OP code was horrendously inefficient in that it uses 400+ uniquely generated selectors. So the bulk of the processing time involves maintaining the CSS animation loop and looking up 400+ classes on each alternation of said CSS animation. This is a rare case wherein class selectors are a burden and not useful. Since each s.star
needs these unique styles, it would take less computing power to generate the CSS property values on a template literal and then assign it to the tag as an inline-style. (See Demo)
Besides doing away with ridiculously huge .class
lists on a bloated stylesheet, the demo makes full use of a documentFragment. DOM operations are expensive on resources (imagine 400+ tags being appended to one location). Doing everything on the fragment, then finally to the DOM by 👍appending documentFragment just once and 400 .star
are in the DOM👍. The OP code on the other hand 👎will append 400 s.star
one at a time... that's 400+ DOM operations.👎
Also on the OP code it is deceiving as to the size of the actual CSS. SCSS, a post-processor is used, so what looks like 8 lines of weird looking CSS is actually 👎3200 lines of CSS👎 after it has been compiled and cached by the browser. The CSS in the demo is what it appears to be ...👍9 lines👍 for .star
selector.
/**| documentFragment
- The only global variable points to a documentFragment not attached to the DOM.
- Use fragment as you would a document object when moving, creating, destroying,
appending, detaching, etc... HTML element tags from and to the DOM. Doing so will
greatly improve processing times when adding 400+ uniquely styled tags.
- When all .star tags have been created, modified, and appended to the fragment --
only the fragment itself needs to be appended to the DOM instead of 400 tags.
*/
var fragment = document.createDocumentFragment();
/**| randomRange(min, max, integer = false)
@Params: min [number].....: The minimum
max [number].....: The maximum
integer [boolean]: default is false which results will be floats.
If true then results will be integers.
Utility function that will return a random number from a given range of consecutive
numbers.
*/
const randomRange = (min, max, integer = false) => {
let numbers = integer ? {
min: Math.ceil(min),
max: Math.floor(max)
} : {
min: min,
max: max
};
return Math.random() * (numbers.max - numbers.min + 1) + numbers.min;
};
/**| starGenerator(limit)
@Params: limit [number]: The number of s.star to generate.
A generator function that creates s.star tags. Assigning individual tag properties
and setting randomly determined values would involve a ton of unique selectors.
To avoid a ton of lookups in a CSS stylesheet a mile long, it's easier to create and
maintain one template literal of the CSS properties interpolated with random values.
Each s.star would be assigned an inline-style of five CSS properties/values by one
statement via `.cssText` property.
*/
function* starGenerator(limit) {
let iteration = 0;
while (iteration < limit) {
iteration++;
const star = document.createElement("s");
star.classList.add("star");
let properties = `
width: ${randomRange(1, 4)}px;
height: ${randomRange(1, 4)}px;
top: ${randomRange(0, 100, true)}vh;
left: ${randomRange(0, 100, true)}vw;
animation-delay: ${randomRange(1, 30, true) / 10}s`;
star.style.cssText = properties;
yield star;
}
return fragment;
}
/**| nightfall(selector, limit = 400)
@Params: selector [string]: Target parent tag
limit [number].. : The maximum number of s.star to generate.
Interface function that facilitates DOM procedures with minimal presence in DOM.
*/
const nightfall = (selector, limit = 400) => {
const base = document.querySelector(selector);
base.classList.add('sky');
for (let star of starGenerator(limit)) {
fragment.appendChild(star);
}
return base.appendChild(fragment);
};
// Call nightfall() passing the selector "main"
nightfall("main");
.sky {
position: relative;
background: #000;
height: 100vh;
overflow: hidden;
}
.star {
display: block;
position: absolute;
animation: twinkle 1.2s alternate infinite ease-in-out;
transform: scale(0.2);
border-radius: 50%;
background: #fff;
box-shadow: 0 0 6px 1px #fff;
z-index: 2;
text-decoration: none;
}
@keyframes twinkle {
100% {
transform: scale(1);
}
}
<main></main>
Upvotes: 2
Reputation: 1764
Have you tried using the will-change property - this helps the browser know about the change and offload it to the compositor if possible.
Upvotes: 2
Reputation: 4536
That's because the rendering is done by CPU which can be a loose in performance. There is an option in CSS to run such an animation on GPU.
Your snippet adjusted
@for $i from 0 through 400 {
.star:nth-child(#{$i}) {
$star-size: (random() * (1-4) +4) + px;
transform: translateY((random(100)) + vh) translateX((random(100)) + vw) translateZ(0);
width: $star-size;
height: $star-size;
animation: blinker 1.2s alternate infinite ease-in-out;
animation-delay: (random(30) / 10) + s;
transform: scale(0.2);
}
}
@keyframes blinker {
100% {
transform: scale(1);
}
}
It's very important to add translateZ
because only 3D renderings are done by GPU.
Doing animations on GPU is also called accelerated animations, please check this helpful article for more information about: https://www.sitepoint.com/introduction-to-hardware-acceleration-css-animations/
Upvotes: 1
Reputation: 928
it's not only problem with you code. it's also from your CPU ability, trying to upgrade your CPU and RAM to perform better.
sometimes you can't build mid - high animation in low spec computer.
Upvotes: 0