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
2 Answers
Reset to default 4You 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>