Reputation: 60
First let me explain the problem, then what I tried to fix it. Including stuff that worked, but performance is less good, hence my question.
The problem is when animating an element with translateX (or translateY), part of it doesn't get rendered at all. This is true for CSS animations, and CSS transitions. Occurs on large elements only.
At 300vw (width of the animated element), it occurs rarely. At 700vw, it always occurs.
Example with 300vw Example with 700vw
In both these cases, the element in question is a pseudo element, and is being translateX by 100% - 100vw (to always stay visible)
Here is the CSS for that pseudo element, which is all the code needed to replicated this (and give the svg_divider class to the container);
.svg_divider{
overflow:hidden;
position:relative;
}
.svg_divider::before{
content:'';
position: absolute;
z-index: 3;
pointer-events: none;
background-repeat: no-repeat;
bottom: -0.1vw;
left: -0.1vw;
right: -600.1vw;
top: -0.1vw;
animation: 4s infinite alternate animateHorShape linear;
background-position: 50% 100%;
background-size: 100% 90px; background-image: url('data:image/svg+xml;charset=utf8, <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 320" preserveAspectRatio="none"><path fill="%23fbd8c2" d="M0 32l48 32c48 32 144 96 240 106.7 96 10.3 192-31.7 288-21.4 96 10.7 192 74.7 288 69.4 96-5.7 192-79.7 288-106.7s192-5 240 5.3l48 10.7v192H0z"/><path fill="%23fbd8c2" opacity=".66" d="M0 64l48 16c48 16 144 48 240 48s192-32 288-26.7C672 107 768 149 864 144s192-59 288-80 192-11 240-5.3l48 5.3v256H0z"/></svg>');
}
@media (min-width:2100px){
.svg_divider::before{
background-size: 100% calc(2vw + 90px);
}
}
@keyframes animateHorShape {
100% {
transform: translateX(calc( -100% + 100vw));
}
}
/* Only for preview*/
.preview{
display:flex;
flex-direction:column;
height:100vh;
}
.preview div{
flex-grow:1;
}
<div class="preview">
<div class="svg_divider"></div>
<div></div>
</div>
Run the code snippet in fullscreen, to see it well. You will notice that at any scroll event, it 'snaps' back in view for a bit.
Code is for the 700vw element, which always shows the bug (in Chrome and Safari, at least).
I tried the following to make it better:
Use transition instead of keyframe animations, by having a setInterval function toggle a class to change the translateX value. The exact same issue was present.
Tried to trigger a force repaint of the browser by dispatching window resize event, and window scroll event. Didn't work.
The following works, but performance is less good
Wrote it in JS with rAF. That works, no render problems. However when you have a few on the page, the CPU toll gets to ~60% (@ 6x slower CPU settings). The translateX version is still at ~2%. See that rAF version here
Animating background position directly also works. So keeping the shape at 100vw, but streching it in the background size instead, then animating background position. Similar problem, performance is less good, animation less smooth.
Any help with this would be greatly appreciated.
Cheers
Upvotes: 0
Views: 1729
Reputation: 60
Finally fixed this. Here is my understanding and the solution:
Browsers renders elements that are in view. So if you got something large that goes out of the viewport, the browsers don't bother rendering the part that's not in view. That's all well and good and as it should be.
In my case though it meant what you saw on that page... the shape isn't rendered so it 'stops' at some point.
That's because CSS transform doesn't render stuff, it transform that already rendered element. That's why it's so efficient and performance is so good. However that caused the bug with the large element.
But by having the element all be in the viewport from the start (by being ~100vw width) , and THEN stretch it by transform scaleX (100), all the shape is considered, as it was fully rendered by the browser originally.
.svg_divider {
overflow: hidden;
position: relative;
}
.svg_divider::before {
content: "";
position: absolute;
z-index: 3;
pointer-events: none;
background-repeat: no-repeat;
bottom: -0.1vw;
left: -0.1vw;
right: -0.1vw;
top: -0.1vw;
transform-origin: 100% 0;
transform: scaleX(100);
animation: 14s infinite alternate animateHorShapee linear;
background-position: 50% 0%;
background-size: 100% 90px;
background-image: url('data:image/svg+xml;charset=utf8, <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 35.28 2.17" preserveAspectRatio="none">><path d="M0 .5c3.07.55 9.27-.42 16.14 0 6.88.4 13.75.57 19.14-.11V0H0z" fill="%23FBD8C2"/><path d="M0 1c3.17.8 7.29-.38 10.04-.55 2.75-.17 9.25 1.47 12.67 1.3 3.43-.17 4.65-.84 7.05-.87 2.4-.02 5.52.88 5.52.88V0H0z" opacity=".5" fill="%23FBD8C2"/><g><path d="M0 1.85c2.56-.83 7.68-.3 11.79-.42 4.1-.12 6.86-.61 9.58-.28 2.73.33 5.61 1.17 8.61 1 3-.19 4.73-.82 5.3-.84V.1H0z" opacity=".5" fill="%23FBD8C2"/></g></svg>');
}
@media (min-width: 2100px) {
.svg_divider::before {
background-size: 100% calc(2vw + 90px);
}
}
@keyframes animateHorShapee {
100% {
transform: scaleX(100) translateX(98%);
}
}
/* Only for preview*/
.preview{
display:flex;
flex-direction:column;
height:100vh;
}
.preview div{
flex-grow:1;
}
<div class="preview">
<div class="svg_divider"></div>
<div></div>
</div>
Upvotes: 1