Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

changing animation delay using j Query while animation is paused does not work on Safari but it does everywhere else

Tags:

jquery

css

safari

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 from 0s to 100s achieving 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.length; i++) {
        $(o[i].selector).css('animation-play-state','paused');
        $(o[i].selector).css('animation-duration','100s');
      }
      $(window).scroll(function() {
        var h = $(window).scrollTop();
        for(var i = 0; i < o.length; i++) {
    
            $(o[i].selector).css('animation-delay',-clamp(0,100,((h-o[i].start)/o[i].end * 100)) + 's');
        }
      });

    }
    
    function clamp(from, to, val) {
      if(val >= 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);
      }
    }
<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>

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

like image 351
Erik Putz Avatar asked Jul 23 '18 17:07

Erik Putz


2 Answers

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>
like image 75
Ollin Boer Bohan Avatar answered Oct 27 '22 00:10

Ollin Boer Bohan


You need to add the animation webkit to your code and css for Safari

-webkit-animation

-webkit-animation-delay

-webkit-animation-duration

-webkit-animation-play-state

@-webkit-keyframes

like image 32
Stef Avatar answered Oct 27 '22 00:10

Stef