CSS改变动画持续时间而不跳跃

时间:2017-11-30 16:39:36

标签: javascript jquery html css animation

我有一个简单的车轮旋转动画。我试图使用滑块(输入范围)控制旋转轮的速度。我已设法做到这一点,但每次我更改动画时动画重新启动(它跳转)。寻找一种解决方案,以创造一个平稳的速度增长。当用户增加滑块的值时,轮子以增加的速度旋转。

在下面的代码中,#loading是旋转轮。



$(document).on('input', '#slider', function() {
  var speed = $(this).val();
  $('#speed').html(speed);
  $("#loading").css("animation-duration", 50 / speed + "s");
});

#loading {
  position: absolute;
  left: 0;
  right: 0;
  margin: auto;
  transform-origin: 50% 50%;
  animation: rotateRight infinite linear;
  animation-duration: 0;
}

@keyframes rotateRight {
  100% {
    transform: rotate(360deg);
  }
}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<img id="loading" src="//placehold.it/100">
<input type="range" min="0" max="100" value="0" class="slider" id="slider">
<p>Speed: <span id="speed"></span></p>
&#13;
&#13;
&#13;

4 个答案:

答案 0 :(得分:2)

经典问题
(跳跃......现在还没有)

带有jQuery的版本

var lasttime = 0,  lastduration = 0,  angle = 0;
$(document).on('input', '#slider', function(event) {
  var speed = $(this).val();
  $('#speed').html(speed);
  
  var el = $("#loading");
  var duration = (speed > 0) ? 50 / speed : 0;
  var currenttime = event.originalEvent.timeStamp / 1000;
  var difftime = currenttime - lasttime;
  el.removeClass("enable_rotate").show();
  
  if (!lastduration && duration)
      el.css("transform", "");
  else
      angle += (difftime % lastduration) / lastduration;
      
  if (duration){     
    el.css("animation-duration", duration + "s")
    .css("animation-delay", -duration * angle + "s")    
    .addClass("enable_rotate");
    }
  else
    el.css("transform", "rotate(" + 360 * angle + "deg)");
  
  angle -= angle | 0; //use fractional part only
  lasttime = currenttime;
  lastduration = duration;
});
.anime_object {
  position: absolute;
  left: 0;
  right: 0;
  margin: auto;
}

.enable_rotate {
  animation: rotateRight infinite linear;
}

@keyframes rotateRight {
  100% {
    transform: rotate(360deg);
  }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<img id="loading" class="anime_object" src="//placehold.it/100">
<input type="range" min="0" max="100" value="0" id="slider">
<p>Speed: <span id="speed"></span></p>

工作草案

保存currentanimation的变量
http://www.w3.org/TR/css-animations-1/#interface-animationevent-attributes

  1. SO
  2. css-tricks.com

答案 1 :(得分:0)

如果没有不使用CSS动画的不同方法,我不能完全确定这是可行的。问题是,无论何时更改速度,动画都不会正常化。它总是动画从动画的0%到动画的100%。每次调整animation-duration时,您都会使用动画中的当前位置重新插值。

换句话说,如果您在animation-duration: 25处从50更改为t=12,那么动画 中途完成(180度);现在它只完成了四分之一(90度)。但是,您无法控制t,这是浏览器的标识。如果可以,您可能希望将t设置为插值中的位置,在此示例中为t=25,以便您保持与动画完全相同的百分比,但是你延长剩余的时间。

我稍稍修改了你的脚本,试着展示我所描述的更好一点。它会在速度0到5之间每秒将速度提高0.25。您可以看到问题是浏览器控制t是问题所在。

你可以重写这个以便用JavaScript控制t,但我认为你必须放弃CSS动画。

再谈谈这个浏览器控制的t变量,请看看有关CSS-Tricks的这篇文章:Myth Busting: CSS Animations vs. JavaScript

  

某些浏览器允许您暂停/恢复CSS关键帧动画,但是   那是关于它的。你不能寻找一个特定的地方   动画,你也不能顺利地逆转或改变   时间刻度或在某些位置添加回调或将它们绑定到富人   一组播放事件。 JavaScript提供了很好的控制,如图所示   下面的演示。

这是您的问题,您希望能够更改动画的持续时间,但也可以在动画中寻找正确的位置。

&#13;
&#13;
$(function() {
  var speed = parseInt($("#slider").val(), 10);
  $("#speed").html(speed);
  $("#loading").css("animation-duration", 50 / speed + "s");

  var forward = true;
  setInterval(function() {
    speed += (forward ? 0.25 : -0.25);
    if (speed >= 5) {
      forward = false;
    } else if (speed <= 0) {
      forward = true;
    }
    $("#loading").css("animation-duration", 50 / speed + "s");
    $("#slider").val(speed);
    $("#speed").html(speed);
  }, 1000);
});
&#13;
#loading {
  position: absolute;
  left: 0;
  right: 0;
  margin: auto;
  transform-origin: 50% 50%;
  animation: rotateRight infinite linear;
  animation-duration: 0;
}

@keyframes rotateRight {
  100% {
    transform: rotate(360deg);
  }
}
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<img id="loading" src="//placehold.it/100">
<input type="range" min="0" max="100" value="0" step=".25" class="slider" id="slider" value="0">
<p>Speed: <span id="speed"></span></p>
&#13;
&#13;
&#13;

答案 2 :(得分:0)

TL; DR

不,你不能(根据我的测试)

  • 首先,让我们从你的CSS中删除动画声明并将其移动到Javascript,这样动画循环就不会启动(即使你无法直观地看到它正在运行)。

您是否注意到即使您从最初位置移动滑块,该框似乎从随机位置开始?这是因为动画循环实际上已经在运行。

  • 现在,您可以在动画的任何给定时间实际获取应用于您的方框的当前变换值,使用getComputedStyle(loadingElement).getPropertyValue('transform');这将返回一个矩阵,它不会给你很多,但我们可以从该矩阵计算旋转角度:

(使用一些数学解释here

Math.round(Math.atan2(b, a) * (180/Math.PI));

现在我们有了这个值,我们必须将它标准化为只有角度的正值,然后我们可以将此角度应用为transform: rotate(Xdeg)

的基值

到目前为止一切顺利,您可以看到这在代码片段中工作,但是即使您这样做,并递增/递减速度值,动画循环已经在运行设定时间刻度,您无法重置此循环。

到目前为止,我的回答是其他对动画循环有更深入了解的人可以从构建,也许会想出一个可行的代码。

如果您还在阅读本文,您可能会想到,只需使用loadingElement.style.removeProperty('animation')删除动画并再次分配,尝试它不起作用。然后用setInterval(...,0)再次启动动画,以便它在下一个循环中运行,也不会工作。

&#13;
&#13;
$(document).on('input', '#slider', function() {
	var speed = $(this).val();
	var loadingElement = document.querySelector("#loading")
	$('#speed').html(speed);
	//get the current status of the animation applied to the element (this is a matrix)
	var currentCss = getComputedStyle(loadingElement).getPropertyValue('transform'); 

	if (currentCss !== 'none'){
		//parse each value we need from the matrix (there is a total of 6)
		var values = currentCss.split('(')[1];
		values = values.split(')')[0];
		values = values.split(',');

		var a = values[0];
		var b = values[1];
		var c = values[2];
		var d = values[3];

		//here we make the actual calculation
		var angle = Math.round(Math.atan2(b, a) * (180/Math.PI)); 

		//normalize to positive values
		angle = angle < 0 ? angle + 360 : angle;

		loadingElement.style.removeProperty('animation'); //remove the property for testing purposes
		$("#loading").css('transform', 'rotate(' + angle + 'deg)');
	}
	else{ //else for testing purposes, this will change the speed of the animation only on the first slider input change
		$("#loading").css('animation', 'rotateRight infinite linear'); //see how the animation now actually starts from the initial location
		$("#loading").css("animation-duration", 50 / speed + "s");
	}

});
&#13;
#loading {
  position: absolute;
  left: 0;
  right: 0;
  margin: auto;
  transform-origin: 50% 50%;
  /*I removed the initialization of the animation here*/
}

@keyframes rotateRight {
  100% {
    transform: rotate(360deg);
  }
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<img id="loading" src="https://placehold.it/100">
<input type="range" min="0" max="100" value="0" class="slider" id="slider">
<p>Speed: <span id="speed"></span></p>
&#13;
&#13;
&#13;

答案 3 :(得分:0)

是的,我们可以!

以下是我们采取的方法:

  1. 输入更改时,获取新的速度值
  2. 抓取元素的当前transform值,该值以matrix()
  3. 的形式返回
  4. matrix()转换为rotate度数
  5. 删除现有动画
  6. 根据当前速度和旋转值创建新动画
  7. 将新动画应用于元素
  8. 我们需要克服的主要问题是根据当前旋转值创建新的动画关键帧 - 我们需要创建一个新动画,其起始值等于当前旋转值,并且结束值等于当前旋转值价值+ 360。

    换句话说,如果我们的元素被轮换90deg并且速度发生了变化,我们需要创建一个新的@keyframes

    @keyframes updatedKeyframes {
      0% {
        transform: rotate(90deg);
      },
      100% {
        transform: rotate(450deg); // 90 + 360 
      }
    }
    

    要创建动态动画,请使用jQuery.keyframes插件创建动态@keyframes

    虽然这个解决方案有效,但我根据jQuery.keyframes插件的工作方式,我不相信它的超级性能。对于每个新动画关键帧,插件都会附加内联<style>。这导致可能定义了数十,数百甚至数千keyframesspeed。在下面的示例中,我使用@keyframe变量创建@keyframes名称,因此最多可创建100个唯一$(document).on('input', '#slider', function() { var speed = $(this).val(); $('#speed').html(speed); var transform = $("#loading").css('transform'); var angle = getRotationDegrees(transform); $("#loading").css({ "animation": "none" }); $.keyframe.define([{ name: `rotateRight_${speed}`, "0%": { "transform": "rotate(" + angle + "deg)" }, "100%": { 'transform': "rotate(" + (angle + 360) + "deg)" } }]); if (speed === "0") { $("#loading").css("transform", "rotate(" + angle + "deg)"); } else { $("#loading").playKeyframe({ name: `rotateRight_${speed}`, duration: 50 / speed + "s", timingFunction: "linear", iterationCount: "infinite" }); } }); function getRotationDegrees(matrix) { if (matrix !== 'none') { var values = matrix.split('(')[1].split(')')[0].split(','); var a = values[0]; var b = values[1]; var angle = Math.round(Math.atan2(b, a) * (180 / Math.PI)); } else { var angle = 0; } return angle; }个样式。我们可以在这里进行一些优化,但这不属于此解决方案的范围。

    以下是工作示例:

    &#13;
    &#13;
    #loading {
      position: absolute;
      left: 0;
      right: 0;
      margin: auto;
      transform-origin: 50% 50%;
    }
    &#13;
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
    <script src="https://rawgit.com/jQueryKeyframes/jQuery.Keyframes/master/jquery.keyframes.js"></script>
    <img id="loading" src="http://via.placeholder.com/100x100">
    <input type="range" min="0" max="100" value="0" class="slider" id="slider">
    <p>Speed: <span id="speed"></span></p>
    &#13;
    {{1}}
    &#13;
    &#13;
    &#13;