admin管理员组

文章数量:1389855

So I have a simple CSS animation (a circle that grows, and then shrinks back down). I am able to successfully use Javascript to detect the start and end of the animation, but can't figure out how to detect the individual keyframes of that animation.

So my question is: how can I detect when the 50% keyframe has been reached in my animation?

Demo:

HTML

<div class="center">
    <svg class="center circle-animation" xmlns="/">
        <g>
            <circle cx="65" cy="70" r=60 fill="#96CCFF" stroke-width="3" stroke="#8181F7"></circle>
            <text x="35" y="75" font-size="18" class="output">circle</text>
        </g>
    </svg>
</div>

CSS

svg { width:150px;}

text {color:#000; font-family: sans-serif; font-weight: bold;}

.center{
    margin-top:100px;
    text-align:center;
    padding-bottom:50px;
}  

.circle-animation{
    animation-delay:2s;
    animation-duration: 4s;
    animation-name: circle-animation;
    animation-direction: normal;
    animation-timing-function: linear;
}

@-webkit-keyframes circle-animation {
    0% {transform: scale( 1 );}
    50% {transform: scale( 2.25 );}
    100% {transform: scale( 1 );}
}

JS

window.onload = function() {
    var elm = document.querySelector('.circle-animation');
    var op = document.querySelector('.output');

    elm.addEventListener('animationend', function(e) {
        op.innerHTML = 'ended';
    });

    elm.addEventListener('animationstart', function(e) {
        op.innerHTML = 'started';
    });
}

So I have a simple CSS animation (a circle that grows, and then shrinks back down). I am able to successfully use Javascript to detect the start and end of the animation, but can't figure out how to detect the individual keyframes of that animation.

So my question is: how can I detect when the 50% keyframe has been reached in my animation?

Demo: http://codepen.io/tymichaels/pen/Mprrxw

HTML

<div class="center">
    <svg class="center circle-animation" xmlns="https://www.w3/TR/SVG/">
        <g>
            <circle cx="65" cy="70" r=60 fill="#96CCFF" stroke-width="3" stroke="#8181F7"></circle>
            <text x="35" y="75" font-size="18" class="output">circle</text>
        </g>
    </svg>
</div>

CSS

svg { width:150px;}

text {color:#000; font-family: sans-serif; font-weight: bold;}

.center{
    margin-top:100px;
    text-align:center;
    padding-bottom:50px;
}  

.circle-animation{
    animation-delay:2s;
    animation-duration: 4s;
    animation-name: circle-animation;
    animation-direction: normal;
    animation-timing-function: linear;
}

@-webkit-keyframes circle-animation {
    0% {transform: scale( 1 );}
    50% {transform: scale( 2.25 );}
    100% {transform: scale( 1 );}
}

JS

window.onload = function() {
    var elm = document.querySelector('.circle-animation');
    var op = document.querySelector('.output');

    elm.addEventListener('animationend', function(e) {
        op.innerHTML = 'ended';
    });

    elm.addEventListener('animationstart', function(e) {
        op.innerHTML = 'started';
    });
}
Share asked Mar 18, 2017 at 23:44 Ty RoderickTy Roderick 512 silver badges3 bronze badges 1
  • Possible duplicate: stackoverflow./questions/38653207/… – Waxi Commented Mar 18, 2017 at 23:48
Add a ment  | 

2 Answers 2

Reset to default 4

You can dispatch a custom event on animationstart with setInterval and clear the interval on animationend.

window.onload = function() {
    var elm = document.querySelector('.circle-animation');
    var op = document.querySelector('.output');
    var eventOnAnimate = new Event('onanimate');
    var time = 0;

    elm.addEventListener('animationend', function(e) {
        op.innerHTML = 'ended';
        clearInterval(elm.interval);
        time = 0;
    });

    elm.addEventListener('animationstart', function(e) {
        op.innerHTML = 'started';
        time = 0;
        elm.interval = setInterval(function(){
          eventOnAnimate.data = {sampleData: ++time};
          elm.dispatchEvent(eventOnAnimate);
        });
    });
    
    elm.addEventListener('onanimate', function(e) {
        op.innerHTML = e.data.sampleData + 'ms';
    });
}
svg { width:150px;}

text {color:#000; font-family: sans-serif; font-weight: bold;}

.center{
    margin-top:30px;
    text-align:center;
    padding-bottom:50px;
}  

.circle-animation{
    animation-delay:2s;
    animation-duration: 4s;
    animation-name: circle-animation;
    animation-direction: normal;
    animation-timing-function: linear;
}

@-webkit-keyframes circle-animation {
    0% {transform: scale( 1 );}
    50% {transform: scale( 2.25 );}
    100% {transform: scale( 1 );}
}
<div class="center">
    <svg class="center circle-animation" xmlns="https://www.w3/TR/SVG/">
        <g>
            <circle cx="65" cy="70" r=60 fill="#96CCFF" stroke-width="3" stroke="#8181F7"></circle>
            <text x="35" y="75" font-size="18" class="output">circle</text>
        </g>
    </svg>
</div>

There is no native event support for listening keyframe-by-keyframe, but you can create a workaround with setTimeout and window.getComputedStyle (to get the animation-duration property).

Below is an onKeyframes utility which can be used to listen for an arbitrary number of keyframe events using a more intuitive percentage-based syntax:

onKeyframes(elm, {
  0: function() {
    op.textContent = 'started'
  },
  50: function() {
    op.textContent = 'midpoint'
  },
  100: function() {
    op.textContent = 'ended'
  }
})


Demo Snippet:

function onKeyframes(element, handlers) {
  var from = handlers[0] || handlers.from
  var to = handlers[100] || handlers.to
  delete handlers.from
  delete handlers[0]
  delete handlers.to
  delete handlers[100]

  handlers = Object.keys(handlers).map(function(k) {
    return [k, this[k]]
  }, handlers)

  element.addEventListener('animationstart', function() {
    from && from.apply(this, arguments)
    if (handlers.length) {
      var match = /(\d+)(m?)s/.exec(window.getComputedStyle(element).animationDuration)
      var duration = (match[2] ? 1 : 1e3) * match[1]
      handlers.forEach(function(pair) {
        setTimeout(pair[1], pair[0] / 100 * duration)
      })
    }
  })
  to && element.addEventListener('animationend', to)
}

window.onload = function() {
  var elm = document.querySelector('.circle-animation')
  var op = document.querySelector('.output')

  onKeyframes(elm, {
    0: function() {
      op.textContent = 'started'
    },
    50: function() {
      op.textContent = 'midpoint'
    },
    100: function() {
      op.textContent = 'ended'
    }
  })
}
svg {
  width: 150px;
}

text {
  color: #000;
  font-family: sans-serif;
  font-weight: bold;
}

.center {
  margin-top: 100px;
  text-align: center;
  padding-bottom: 50px;
}

.circle-animation {
  animation-delay: 2s;
  animation-duration: 4s;
  animation-name: circle-animation;
  animation-direction: normal;
  animation-timing-function: linear;
}

@-webkit-keyframes circle-animation {
  0% {
    transform: scale( 1);
  }
  50% {
    transform: scale( 2.25);
  }
  100% {
    transform: scale( 1);
  }
}
<div class="center">
  <svg class="center circle-animation" xmlns="https://www.w3/TR/SVG/">
        <g>
            <circle cx="65" cy="70" r=60 fill="#96CCFF" stroke-width="3" stroke="#8181F7"></circle>
            <text x="35" y="75" font-size="18" class="output">circle</text>
        </g>
    </svg>
</div>

本文标签: Detect CSS Animation Keyframes with JavascriptStack Overflow