停止 CSS 在未知关键帧上平滑地旋转动画
Stop CSS rotate animation smoothly on unknown keyframe
我有一个使用 CSS 旋转动画摆动的图像。
我想顺利地停止它(并在单击另一个元素时将它放回原来的位置,但不会有跳动停止的感觉)。
似乎预期的行为只发生在动画的第一次迭代上,但不会发生在即将到来的动画上(这是在前 2 秒内单击 "slow" 按钮时)
这是一个示例代码
https://jsfiddle.net/pbarrientos/5v3xwak6/
我已经尝试添加 animation-iteration-count: 1;
和 adding/removing 类.
var css = {
'-webkit-animation-iteration-count': "1",
'-moz-animation-iteration-count': "1",
'animation-iteration-count': "1"
}
有线索吗?
尝试在 animation-play-state
设置为 paused
之前保存 #element
的 matrix
位置,将 #element
的已保存 matrix
位置添加到 css
在动画暂停后应用于 #element
;在 #play
click
事件中将 animation-iteration-count
重置为 infinite
- 例如 #slow
之前单击的位置,将 animation-iteration-count
设置为 1
$(document).ready(function() {
var el = $("#element");
// return `matrix` position of `el` `#element`
function tfx(el) {
return el.css(["-webkit-transform", "-moz-transform"]);
}
$("#pause").click(function() {
// save `matrix` position of `el`
var pos = tfx(el);
var css = {
"-webkit-animation-play-state": "paused",
"-moz-animation-play-state": "paused",
"animation-play-state": "paused"
}
// extend `css` with `pos`
var _css = $.extend(pos, css);
el.css(_css);
});
$("#play").click(function() {
// save `matrix` position of `el`
var pos = tfx(el);
// reset `animation-iteration-count` to `infinite`
var css = {
"-webkit-animation-iteration-count": "infinite",
"-moz-animation-iteration-count": "infinite",
"animation-iteration-count": "infinite",
"-webkit-animation-play-state": "running",
"-moz-animation-play-state": "running",
"animation-play-state": "running"
}
// extend `css` with `pos`
var _css = $.extend(pos, css);
el.removeClass("stopit").addClass("swing").css(_css);
});
$("#slow").click(function() {
el.removeClass("swing").addClass("stopit");
var css = {
"-webkit-transition": "all 4000ms ease-out",
"-webkit-animation-iteration-count": "1",
"-moz-animation-iteration-count": "1",
"animation-iteration-count": "1"
}
el.css(css);
// `stopit` class added above ?
// el
// .one("webkitAnimationEnd oanimationend msAnimationEnd animationend", function (e) {
// el.addClass("stopit");
// });
});
});
.swing {
transform-origin: top center;
-webkit-transform-origin: top center;
animation: badge-swing 2s infinite;
-webkit-animation: badge-swing 2s infinite;
-webkit-animation-fill-mode: both;
-webkit-animation-timing-function: ease-in-out;
-moz-animation: badge-swing 2s infinite;
-moz-animation-fill-mode: forwards;
animation-fill-mode: forwards;
}
.stopit {
-webkit-animation-duration: 2s;
-webkit-animation-name: stopit;
-webkit-animation-fill-mode: forwards;
-moz-animation-duration: 2s;
-moz-animation-name: stopit;
-moz-animation-fill-mode: forwards;
animation-name: stopit;
}
@-webkit-keyframes badge-swing {
0% {
-webkit-transform: rotate(-5deg);
-webkit-animation-timing-function: ease-in;
}
25% {
-webkit-transform: rotate(0deg);
-webkit-animation-timing-function: ease-out;
}
50% {
-webkit-transform: rotate(5deg);
-webkit-animation-timing-function: ease-in;
}
75% {
-webkit-transform: rotate(0deg);
-webkit-animation-timing-function: ease-out;
}
100% {
-webkit-transform: rotate(-5deg);
-webkit-animation-timing-function: ease-in;
}
}
@-moz-keyframes badge-swing {
0% {
-moz-transform: rotate(-5deg);
-moz-animation-timing-function: ease-in;
}
25% {
-moz-transform: rotate(0deg);
-moz-animation-timing-function: ease-out;
}
50% {
-moz-transform: rotate(5deg);
-moz-animation-timing-function: ease-in;
}
75% {
-moz-transform: rotate(0deg);
-moz-animation-timing-function: ease-out;
}
100% {
-moz-transform: rotate(-5deg);
-moz-animation-timing-function: ease-in;
}
}
@-webkit-keyframes stopit {
0% {
-webkit-transform: rotate(-5deg);
-webkit-animation-timing-function: ease-out;
}
100% {
-webkit-transform: rotate(0deg);
-webkit-animation-timing-function: ease-out;
}
}
@-moz-keyframes stopit {
0% {
-moz-transform: rotate(-5deg);
-moz-animation-timing-function: ease-out;
}
100% {
-moz-transform: rotate(0deg);
-moz-animation-timing-function: ease-out;
}
}
#pause,
#play,
#slow {
display: inline-block;
padding: 5px 30px;
background: lightgrey;
border-radius: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<img id="element" src="https://dl.dropboxusercontent.com/u/39131788/palmed.png" class="swing">
<br>
<br>
<div id="pause">pause</div>
<div id="play">play</div>
<div id="slow">slow</div>
jsfiddle https://jsfiddle.net/5v3xwak6/5/
如果您使用 TweenMax 会很有趣。
片段:
var element=document.getElementById('element');
var playButton=document.getElementById('play');
var pauseButton=document.getElementById('pause');
var slowButton=document.getElementById('slow');
var maxDegree=-10;
var minDegree=10;
var duration=.8;
var easeFunc=Power2;
var timeline=new TimelineMax({paused:true,repeat:-1});
TweenMax.set(element,{transformOrigin:'top center'});
timeline.to(element,duration,{rotation:maxDegree,ease:easeFunc.easeOut});
timeline.to(element,duration,{rotation:0,ease:easeFunc.easeIn});
timeline.to(element,duration,{rotation:minDegree,ease:easeFunc.easeOut});
timeline.to(element,duration,{rotation:0,ease:easeFunc.easeIn});
playButton.addEventListener('click',onPlayClick,false);
pauseButton.addEventListener('click',onPauseClick,false);
slowButton.addEventListener('click',onSlowClick,false);
function onPlayClick(){timeline.timeScale(1).play();}
function onPauseClick(){timeline.timeScale(1).pause();}
function onSlowClick(){
timeline.pause().timeScale(.5);
if(timeline.progress()<.25){
timeline.tweenTo(0);
}else if(timeline.progress()>=.25&&timeline.progress()<.75){
timeline.tweenTo(timeline.duration()*.5);
}else{
timeline.tweenTo(timeline.duration());
}
}
#pause, #play, #slow {
display: inline-block;
padding: 5px 30px;
background: lightgrey;
border-radius: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.17.0/TweenMax.min.js"></script>
<img id="element" src="https://dl.dropboxusercontent.com/u/39131788/palmed.png" class="swing">
<br>
<br>
<div id="pause">pause</div>
<div id="play">play</div>
<div id="slow">slow</div>
目的是为您提供一个替代方案,如果您有兴趣的话。希望对你有帮助。
我会在这里使用手动动画。浏览器负责其 CSS 动画,并且以完美同步的位置和速度进行干预将具有挑战性。
由于动画不是很复杂,我们可以简单地设置我们自己的矩阵,或使用辅助方法,以及使用停止时半径减小的正弦函数。
当按下停止按钮时,我们减小了半径以使其看起来像是在停止。我们可以反其道而行之,重新开始。好处是我们可以随时停下来,自然休息。如果你想偏移角度,你可以在减小半径的同时插值到那个角度。
通过使用 requestAnimationFrame
和变换,我们将获得像 CSS 一样流畅的动画。
主要功能是:
angle = Math.sin(time) * radius; // sin=[-1,1] radius => angle
然后在停止时,减小半径,最终会变成角度:
radius *= 0.99;
例子
var img = $("img"), btn = $("button"),
angle, maxRadius = 10, radius = maxRadius,
playing = true, time= 0;
(function loop() {
angle = Math.sin(time) * radius; // calc current angle
setTransform(img, angle);
if (playing) {
if (radius < maxRadius) radius *= 1.03; // increase 3% each frame upto max
} else {
radius *= 0.99; // reduce 1% each frame
}
time += 0.1;
requestAnimationFrame(loop) // loop, can be stopped when radius < n
})();
function setTransform(img, angle) {
img.css("transform", "rotate(" + angle + "deg)");
img.css("-webkit-transform", "rotate(" + angle + "deg)");
}
btn.on("click", function() {
playing = !playing;
if (playing && radius < 0.1) radius = 0.1; // give some meat in case =0
});
img {
transform-origin: top center; -webkit-transform-origin: top center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<img src="http://i.stack.imgur.com/Vma8v.png"><br>
<button>Start / Stop smoothly</button>
您可能希望实现一种机制,当半径小于 n 时也能中止循环,然后在需要时启动循环。为此使用一个单独的标志,以便消除启动多个 rAF 的风险。
我有一个使用 CSS 旋转动画摆动的图像。 我想顺利地停止它(并在单击另一个元素时将它放回原来的位置,但不会有跳动停止的感觉)。 似乎预期的行为只发生在动画的第一次迭代上,但不会发生在即将到来的动画上(这是在前 2 秒内单击 "slow" 按钮时)
这是一个示例代码 https://jsfiddle.net/pbarrientos/5v3xwak6/
我已经尝试添加 animation-iteration-count: 1; 和 adding/removing 类.
var css = {
'-webkit-animation-iteration-count': "1",
'-moz-animation-iteration-count': "1",
'animation-iteration-count': "1"
}
有线索吗?
尝试在 animation-play-state
设置为 paused
之前保存 #element
的 matrix
位置,将 #element
的已保存 matrix
位置添加到 css
在动画暂停后应用于 #element
;在 #play
click
事件中将 animation-iteration-count
重置为 infinite
- 例如 #slow
之前单击的位置,将 animation-iteration-count
设置为 1
$(document).ready(function() {
var el = $("#element");
// return `matrix` position of `el` `#element`
function tfx(el) {
return el.css(["-webkit-transform", "-moz-transform"]);
}
$("#pause").click(function() {
// save `matrix` position of `el`
var pos = tfx(el);
var css = {
"-webkit-animation-play-state": "paused",
"-moz-animation-play-state": "paused",
"animation-play-state": "paused"
}
// extend `css` with `pos`
var _css = $.extend(pos, css);
el.css(_css);
});
$("#play").click(function() {
// save `matrix` position of `el`
var pos = tfx(el);
// reset `animation-iteration-count` to `infinite`
var css = {
"-webkit-animation-iteration-count": "infinite",
"-moz-animation-iteration-count": "infinite",
"animation-iteration-count": "infinite",
"-webkit-animation-play-state": "running",
"-moz-animation-play-state": "running",
"animation-play-state": "running"
}
// extend `css` with `pos`
var _css = $.extend(pos, css);
el.removeClass("stopit").addClass("swing").css(_css);
});
$("#slow").click(function() {
el.removeClass("swing").addClass("stopit");
var css = {
"-webkit-transition": "all 4000ms ease-out",
"-webkit-animation-iteration-count": "1",
"-moz-animation-iteration-count": "1",
"animation-iteration-count": "1"
}
el.css(css);
// `stopit` class added above ?
// el
// .one("webkitAnimationEnd oanimationend msAnimationEnd animationend", function (e) {
// el.addClass("stopit");
// });
});
});
.swing {
transform-origin: top center;
-webkit-transform-origin: top center;
animation: badge-swing 2s infinite;
-webkit-animation: badge-swing 2s infinite;
-webkit-animation-fill-mode: both;
-webkit-animation-timing-function: ease-in-out;
-moz-animation: badge-swing 2s infinite;
-moz-animation-fill-mode: forwards;
animation-fill-mode: forwards;
}
.stopit {
-webkit-animation-duration: 2s;
-webkit-animation-name: stopit;
-webkit-animation-fill-mode: forwards;
-moz-animation-duration: 2s;
-moz-animation-name: stopit;
-moz-animation-fill-mode: forwards;
animation-name: stopit;
}
@-webkit-keyframes badge-swing {
0% {
-webkit-transform: rotate(-5deg);
-webkit-animation-timing-function: ease-in;
}
25% {
-webkit-transform: rotate(0deg);
-webkit-animation-timing-function: ease-out;
}
50% {
-webkit-transform: rotate(5deg);
-webkit-animation-timing-function: ease-in;
}
75% {
-webkit-transform: rotate(0deg);
-webkit-animation-timing-function: ease-out;
}
100% {
-webkit-transform: rotate(-5deg);
-webkit-animation-timing-function: ease-in;
}
}
@-moz-keyframes badge-swing {
0% {
-moz-transform: rotate(-5deg);
-moz-animation-timing-function: ease-in;
}
25% {
-moz-transform: rotate(0deg);
-moz-animation-timing-function: ease-out;
}
50% {
-moz-transform: rotate(5deg);
-moz-animation-timing-function: ease-in;
}
75% {
-moz-transform: rotate(0deg);
-moz-animation-timing-function: ease-out;
}
100% {
-moz-transform: rotate(-5deg);
-moz-animation-timing-function: ease-in;
}
}
@-webkit-keyframes stopit {
0% {
-webkit-transform: rotate(-5deg);
-webkit-animation-timing-function: ease-out;
}
100% {
-webkit-transform: rotate(0deg);
-webkit-animation-timing-function: ease-out;
}
}
@-moz-keyframes stopit {
0% {
-moz-transform: rotate(-5deg);
-moz-animation-timing-function: ease-out;
}
100% {
-moz-transform: rotate(0deg);
-moz-animation-timing-function: ease-out;
}
}
#pause,
#play,
#slow {
display: inline-block;
padding: 5px 30px;
background: lightgrey;
border-radius: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<img id="element" src="https://dl.dropboxusercontent.com/u/39131788/palmed.png" class="swing">
<br>
<br>
<div id="pause">pause</div>
<div id="play">play</div>
<div id="slow">slow</div>
jsfiddle https://jsfiddle.net/5v3xwak6/5/
如果您使用 TweenMax 会很有趣。
片段:
var element=document.getElementById('element');
var playButton=document.getElementById('play');
var pauseButton=document.getElementById('pause');
var slowButton=document.getElementById('slow');
var maxDegree=-10;
var minDegree=10;
var duration=.8;
var easeFunc=Power2;
var timeline=new TimelineMax({paused:true,repeat:-1});
TweenMax.set(element,{transformOrigin:'top center'});
timeline.to(element,duration,{rotation:maxDegree,ease:easeFunc.easeOut});
timeline.to(element,duration,{rotation:0,ease:easeFunc.easeIn});
timeline.to(element,duration,{rotation:minDegree,ease:easeFunc.easeOut});
timeline.to(element,duration,{rotation:0,ease:easeFunc.easeIn});
playButton.addEventListener('click',onPlayClick,false);
pauseButton.addEventListener('click',onPauseClick,false);
slowButton.addEventListener('click',onSlowClick,false);
function onPlayClick(){timeline.timeScale(1).play();}
function onPauseClick(){timeline.timeScale(1).pause();}
function onSlowClick(){
timeline.pause().timeScale(.5);
if(timeline.progress()<.25){
timeline.tweenTo(0);
}else if(timeline.progress()>=.25&&timeline.progress()<.75){
timeline.tweenTo(timeline.duration()*.5);
}else{
timeline.tweenTo(timeline.duration());
}
}
#pause, #play, #slow {
display: inline-block;
padding: 5px 30px;
background: lightgrey;
border-radius: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.17.0/TweenMax.min.js"></script>
<img id="element" src="https://dl.dropboxusercontent.com/u/39131788/palmed.png" class="swing">
<br>
<br>
<div id="pause">pause</div>
<div id="play">play</div>
<div id="slow">slow</div>
目的是为您提供一个替代方案,如果您有兴趣的话。希望对你有帮助。
我会在这里使用手动动画。浏览器负责其 CSS 动画,并且以完美同步的位置和速度进行干预将具有挑战性。
由于动画不是很复杂,我们可以简单地设置我们自己的矩阵,或使用辅助方法,以及使用停止时半径减小的正弦函数。
当按下停止按钮时,我们减小了半径以使其看起来像是在停止。我们可以反其道而行之,重新开始。好处是我们可以随时停下来,自然休息。如果你想偏移角度,你可以在减小半径的同时插值到那个角度。
通过使用 requestAnimationFrame
和变换,我们将获得像 CSS 一样流畅的动画。
主要功能是:
angle = Math.sin(time) * radius; // sin=[-1,1] radius => angle
然后在停止时,减小半径,最终会变成角度:
radius *= 0.99;
例子
var img = $("img"), btn = $("button"),
angle, maxRadius = 10, radius = maxRadius,
playing = true, time= 0;
(function loop() {
angle = Math.sin(time) * radius; // calc current angle
setTransform(img, angle);
if (playing) {
if (radius < maxRadius) radius *= 1.03; // increase 3% each frame upto max
} else {
radius *= 0.99; // reduce 1% each frame
}
time += 0.1;
requestAnimationFrame(loop) // loop, can be stopped when radius < n
})();
function setTransform(img, angle) {
img.css("transform", "rotate(" + angle + "deg)");
img.css("-webkit-transform", "rotate(" + angle + "deg)");
}
btn.on("click", function() {
playing = !playing;
if (playing && radius < 0.1) radius = 0.1; // give some meat in case =0
});
img {
transform-origin: top center; -webkit-transform-origin: top center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<img src="http://i.stack.imgur.com/Vma8v.png"><br>
<button>Start / Stop smoothly</button>
您可能希望实现一种机制,当半径小于 n 时也能中止循环,然后在需要时启动循环。为此使用一个单独的标志,以便消除启动多个 rAF 的风险。