我试图在css3 + Javascript中用闪烁的星星做一个繁星之夜,但是,我的动画消耗了很多CPU,主要的动画:
@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);
}
}
完整代码:https://jsfiddle.net/sam7krx0/ 有什么办法可以使这段代码更好地执行?
编辑:
尝试使用translateZ(0)
和will-change: transform
,但动画仍由CPU渲染。
https://jsfiddle.net/8hn97kcx/2/
编辑2: 似乎是Firefox的问题,而在chrome上进行测试时,动画使用的CPU更少。
编辑3:
在firefox开发人员版本69.0b4上运行的上述小提琴的配置文件:
答案 0 :(得分:2)
您是否尝试过使用will-change属性-这可以帮助浏览器了解更改,并在可能的情况下将其卸载到合成器中。
答案 1 :(得分:2)
OP代码的效率极低,因为它使用了400多个唯一生成的选择器。因此,大量的处理时间涉及维护CSS动画循环,并在每个所述CSS动画交替时查找400多个类。这是一种罕见的情况,其中类选择器是一种负担而没有用。由于每个s.star
都需要这些独特的样式,因此在模板文字上生成CSS属性值,然后以内联样式将其分配给标签将需要较少的计算能力。 (请参见演示)
该演示除了消除了style肿的样式表上的庞大.class
列表之外,还充分利用了 documentFragment 。 DOM操作在资源上很昂贵(想象将400多个标签附加到一个位置)。进行片段上的所有操作,然后通过仅将documentFragment附加一次,然后将400 .star
附加到DOM中,最后然后到DOM ?。另一方面,? OP代码将一次附加400个s.star
...这是400多个DOM操作。?
在OP代码上,它还在欺骗实际CSS的大小。 SCSS是一种后处理程序,因此看起来8行怪异的CSS实际上是由浏览器编译并缓存后的? 3200行CSS ?。演示中的CSS似乎是{?1}选择器的...? 9行?。
.star
/**| 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);
}
}
答案 2 :(得分:1)
这是因为渲染是由CPU完成的,这可能会降低性能。 CSS中有一个选项可以在GPU上运行这样的动画。
您的代码段已调整
@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);
}
}
添加translateZ
非常重要,因为GPU仅完成3D渲染。
在GPU上执行动画也称为加速动画,请查看此有用的文章,以获取有关以下内容的更多信息:https://www.sitepoint.com/introduction-to-hardware-acceleration-css-animations/
答案 3 :(得分:0)
这不仅是您的代码问题。 这也取决于您的CPU能力,试图升级您的CPU和RAM以提高性能。
有时您无法在低规格的计算机上制作中高动画。