我正在尝试创建内容加载器,但是背景动画出现性能问题.当屏幕上只有几个元素时,它很平滑,但是当存根元素数增加到20-30时,它会急剧降低fps.现在,我知道动画背景位置属性是个坏主意,最好对此使用转换.但是我该怎么办呢?我想保持无缝动画.渐变应相对于屏幕而不是容器.
I'm trying to create content loader but got a performance problem with background animation. It's smooth when there are only a few elements on the screen but dramatically drops fps while increasing stub elements count to 20-30. Now I know that animating background-position property is a bad idea and it is better to use transforms for this. but how can I do this? I'd like to keep seamless animation. Gradient should be relative to screen, not to container.
这是一些代码:
const cardsRoot = document.getElementById('cards') const addButton = document.getElementById('add') const card = document.getElementsByClassName('card')[0] let cardsCount = 1 addButton.addEventListener('click', () => { cardsRoot.innerHTML = '' cardsCount++ for (let i = 0; i < cardsCount; i++) { let cardClone = card.cloneNode(true) cardsRoot.appendChild(cardClone) } }) body { padding: 40px; } .card { display: flex; margin-top: 20px; } .stub { width: 300px; height: 12px; margin: 8px; border-radius: 8px; background: linear-gradient(to right, rgba(0, 0, 0, 0.04), rgba(0, 0, 0, 0.1) 10%, rgba(0, 0, 0, 0.04) 20%) fixed; animation: stub 1.3s linear infinite; margin-bottom: 8px; } .circle { width: 40px; height: 40px; margin-right: 12px; border-radius: 20px; } @keyframes stub { 0% { background-position: 0vw; } 100% { background-position: 100vw; } } <button id="add"> ADD CARD </button> <div id="cards"> <div class="card"> <div> <div class="stub circle"></div> </div> <div> <div class="stub"></div> <div class="stub"></div> <div class="stub"></div> </div> </div> </div>
和演示: jsfiddle/3da4uzm2/57/
推荐答案您可以在应用翻译的地方使用伪元素替换动画.诀窍是考虑使用固定元素替换 background-attachment:fixed ,然后使该元素的尺寸比屏幕大两倍,并从左向右平移.
You can replace the animation using pseudo element where you apply translation. The trick is to consider fixed element to replace background-attachment:fixed, then you make the element twice bigger as the screen and you translate it from left to right.
const cardsRoot = document.getElementById('cards') const addButton = document.getElementById('add') const card = document.getElementsByClassName('card')[0] let cardsCount = 1 addButton.addEventListener('click', () => { cardsRoot.innerHTML = '' cardsCount++ for (let i = 0; i < cardsCount; i++) { let cardClone = card.cloneNode(true) cardsRoot.appendChild(cardClone) } }) body { padding: 40px; } .card { display: inline-flex; margin-top: 20px; } .stub { width: 150px; height: 12px; margin: 8px; border-radius: 8px; margin-bottom: 8px; position:relative; z-index:0; /*overflow:hidden; this is no more working, using mask instead */ -webkit-mask:linear-gradient(#fff 0 0); /* OR clip-path:inset(0) */ } .stub:before { content:""; position:fixed; z-index:-1; top:0; right:0; width:200vw; bottom:0; background: linear-gradient(rgba(0, 0, 0, 0.04),rgba(0, 0, 0, 0.04)) left/50% 100%, linear-gradient(to right, rgba(0, 0, 0, 0.04), rgba(0, 0, 0, 0.1) 10%, rgba(0, 0, 0, 0.04) 20%) right/50% 100%; background-repeat:no-repeat; animation: stub 1.3s linear infinite; pointer-events:none; } .circle { width: 40px; height: 40px; margin-right: 12px; border-radius: 20px; } @keyframes stub { 0% { transform:translate(0); } 100% { transform:translate(50%); } } <button id="add"> ADD CARD </button> <div id="cards"> <div class="card"> <div> <div class="stub circle"></div> </div> <div> <div class="stub"></div> <div class="stub"></div> <div class="stub"></div> </div> </div> <div class="card"> <div> <div class="stub circle"></div> </div> <div> <div class="stub"></div> <div class="stub"></div> <div class="stub"></div> </div> </div> </div>
为了更好地理解这里发生的事情,是一个简化版本,其中只有一个元素,我也更改了渐变颜色.
To better understand what is happening here is a simplified version with only one element where I also changed the gradient colors.
body:before { content:""; position:fixed; /*relative to the screen*/ z-index:-1; top:0; right:0; width:200vw; /*2x100vw*/ bottom:0; background: /*will cover the left area while sliding*/ linear-gradient(red,red) left/50% 100%, /*the red should be green*/ /*the main gradient*/ linear-gradient(to right, green, blue 10%, green 20%) right/50% 100%; background-repeat:no-repeat; animation: stub 3s linear infinite; } @keyframes stub { 0% { transform:translate(0); } 100% { transform:translate(50%); } /*50% will be 200vw/2 = 100vw*/ }
有关了解背景值背后的技巧的说明:将百分比值与线性梯度的背景位置一起使用
Related to understand the trick behind the background values: Using percentage values with background-position on a linear-gradient
更多推荐
如何在不使用背景位置的情况下为线性渐变设置动画?
发布评论