我的CSS动画消耗了大量资源

时间:2019-07-16 01:48:49

标签: javascript html css performance

我试图在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上运行的上述小提琴的配置文件:

firefox profile

CPU使用率: System monitor

4 个答案:

答案 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以提高性能。

有时您无法在低规格的计算机上制作中高动画。