停止CSS在未知关键帧上平滑旋转动画

时间:2015-06-08 14:57:19

标签: javascript css css3 jquery-animate css-animations

我有一个使用CSS旋转动画摆动的图像。 我想顺利地停止它(当点击另一个元素时将其恢复到原始位置但没有得到跳跃的停止感觉)。 似乎预期的行为只发生在动画的第一次迭代中,而不是发生在即将发生的行为上(这是在前两秒点击"慢"按钮时)

以下是示例代码 https://jsfiddle.net/pbarrientos/5v3xwak6/

我已经尝试过添加animation-iteration-count:1; 和添加/删除类。

<UIButton: 0x7fded8c9df60; frame = (185 3; 25 25); 
opaque = NO; autoresize = RM+BM; tag = 4; 
animations = { position=<CABasicAnimation: 0x7fded8ce4eb0>; }; 
layer = <CALayer: 0x7fded8c98920>>

线索任何人?

3 个答案:

答案 0 :(得分:2)

我会在这里使用手动动画。浏览器负责其CSS动画,并且以完全同步的位置和速度进行干预将具有挑战性。

由于动画不是很复杂,我们可以简单地设置我们自己的矩阵,或者使用辅助方法,以及使用正弦函数,其中半径在停止时减小。

当按下停止按钮时,我们减小半径以使其看起来停止。我们可以反过来重新开始。好处是我们可以在任何时候停下来并自然休息。如果要偏移角度,可以在减小半径的同时插入该角度。

通过使用requestAnimationFrame和变换,我们将获得与CSS一样的平滑动画。

主要功能是:

angle = Math.sin(time) * radius;  // sin=[-1,1] radius => angle

然后停止时,减小半径,最终将成为角度:

radius *= 0.99;      

实施例

&#13;
&#13;
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
});
&#13;
img {
  transform-origin: top center; -webkit-transform-origin: top center;
  }
&#13;
<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>
&#13;
&#13;
&#13;

当radius低于n时,你可能想要实现一个中止循环的机制,然后在需要时启动循环。使用单独的标志,以便消除启动多个rAF的风险。

答案 1 :(得分:1)

尝试在matrix设置为#element之前保存animation-play-state的{​​{1}}位置,将paused的已保存matrix位置添加到#element动画暂停后应用于css;在#element animation-iteration-count事件中将infinite重置为#play - 例如,之前点击click的位置,将#slow设置为animation-iteration-count

&#13;
&#13;
1
&#13;
$(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");
    // });
  });
});
&#13;
.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;
}
&#13;
&#13;
&#13;

jsfiddle https://jsfiddle.net/5v3xwak6/5/

答案 2 :(得分:1)

如果您使用TweenMax,那会很有趣。

jsFiddle

<强> 段:

{{type}}
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;
}

如果您有兴趣,目的是为您提供替代方案。希望它有所帮助。