job.answiz.com
  • 1
Votes
name

I set up a keyframe animation in CSS. Attached it to a DOM element and set it to pause. With javascript (jQuery), I am changing the animation delay from0s to 100sachieving a nice animation while scrolling.

This works well on all of the browsers, but not on Safari (Version 11.1.1 (13605.2.8)).

 

$(document).ready(function() {
      fluider([
        {
          selector: '.manualAnim',
          start: 100,
          end: 500
        },

        {
          selector: '.manualAnim2',
          start: 500,
          end: 1000
        },

        {
          selector: '.manualAnim3',
          start: 0,
          end: 1500
        }

      ])
    })
    
    
    function fluider(o) {
      for(var i = 0; i < o xss=removed xss=removed>= from) {
        if(val <= to) {
          return val;
        }
        else {
          return to;
        }
      }
        else {
          return from;
      }
    }
   body {
      height: 1000vh;
    }
    .manualAnim {
      position: fixed;
      display: block;
      width: 100px;
      height: 100px;
      background-color: red;
      animation: 100s anim paused both;
      animation-delay: 0s;
    }
    
    .manualAnim2 {
      position: fixed;
      display: block;
      left: 120px;
      width: 100px;
      height: 100px;
      background-color: red;
      animation: 100s anim paused both;
      animation-delay: 0s;
    }
    
    .manualAnim3 {
      position: fixed;
      display: block;
      left: 240px;
      width: 100px;
      height: 100px;
      background-color: red;
      animation: 100s anim paused both;
      animation-delay: 0s;
    }
    
    @keyframes anim{
      0% {
        background-color: red;
        transform: scale(1);
      }
      30% {
        background-color: green;
        transform: scale(1.5);
      }
      60% {
        background-color: blue;
        transform: scale(0.5);
      }
      100% {
        background-color: yellow;
        transform: scale(1);
      }
    }

I Googled a few hours days for now, but I have no clue what could be the problem. Any idea?

After a lot of experimentation, here's a version with workarounds that gives smooth, expected behavior in both Safari 11.1.2 and Chrome 68 (and hopefully other browsers as well).

It looks like the underlying issue is that elements don't get redrawn when animation properties are changed for a paused animation, as the question states. This solution works around that by re-adding the necessary animation-related CSS (with the correct delay) each frame. This would normally cause flickering (since Safari tries to revert to the unanimated styling when the animation is removed), so this solution manually applies the current animation style each time it modifies the animation.

 

$(document).ready(function() {
  fluider([{
      selector: '.manualAnim',
      start: 100,
      end: 500
    },

    {
      selector: '.manualAnim2',
      start: 500,
      end: 1000
    },

    {
      selector: '.manualAnim3',
      start: 0,
      end: 1500
    }

  ])
})

function getAnimatedProperties(animName) {
  // Get an array of all property names that
  // are modified by the animation ${animName}
  let properties = {};
  let sheets = document.styleSheets;
  let propertyRegex = /([a-z-]+):/ig;
  for (let sheet of sheets) {
    let rules = sheet.rules || sheet.cssRules;
    for (let r of rules) {
      if (r.name === animName) {
        let rText = r.cssText;
        let match = propertyRegex.exec(rText);
        while (match) {
          properties[match[1]] = true;
          match = propertyRegex.exec(rText);
        }
      }
    }
  }
  return Object.keys(properties);
}

function fluider(o) {
  const animationName = "anim";
  const preservedProperties = getAnimatedProperties(animationName);
  $(window).scroll(function() {
    var h = $(window).scrollTop();
    for (var i = 0; i < o.length; i++) {
      let el = document.querySelector(o[i].selector);
      let pct = 100 * (parseInt(h) - o[i].start) / o[i].end;
      let delay = -Math.max(Math.min(pct, 100), 0) + 's';
      let s = window.getComputedStyle(el);
      // without setting these properties and overwriting .style, 
      // the animation will flicker
      let preservedStyles = preservedProperties.map(p => `${p}: ${s[p]};`).join("");
      el.style = `${preservedStyles} animation-delay: ${delay}; animation-duration: 100s; animation-play-state: paused;`;
      // without scheduling this *using setTimeout*, 
      // the animation will not rerender
      window.setTimeout(() => {
        el.style.animationName = animationName;
      }, 0);
    }
  });
}
body {
  height: 1000vh;
}

.manualAnim {
  position: fixed;
  display: block;
  width: 100px;
  height: 100px;
  background-color: red;
}

.manualAnim2 {
  position: fixed;
  display: block;
  left: 120px;
  width: 100px;
  height: 100px;
  background-color: red;
}

.manualAnim3 {
  position: fixed;
  display: block;
  left: 240px;
  width: 100px;
  height: 100px;
  background-color: red;
}

@keyframes anim {
  0% {
    background-color: red;
    transform: scale(1);
  }
  30% {
    background-color: green;
    transform: scale(1.5);
  }
  60% {
    background-color: blue;
    transform: scale(0.5);
  }
  100% {
    background-color: yellow;
    transform: scale(1);
  }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div class="manualAnim"></div>
<div class="manualAnim2"></div>
<div class="manualAnim3"></div>
  • 1
Reply Report