Change CSS animation duration without jumping

I have a simple wheel rotation animation. I am trying to control the speed of a spinning wheel using a slider (input range). I managed to do this, but every time I change the animation, the animation restarts (it jumps). Looking for a solution to create a smooth increase in speed. When the user increases the value of the slider, the wheel spins at an increased speed.

In the code below, #load is a spinning wheel.

$(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>
Run codeHide result
+6
source share
4 answers

Yes we can!

Here we take the approach:

  • When changing input, get a new speed value
  • transform , matrix()
  • matrix() rotate
  • ,

, , - , , , + 360.

, 90deg , @keyframes of:

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

, jQuery.keyframes @keyframes.

, , , jQuery.keyframes. <style>. , keyframes. speed @keyframe, 100 @keyframes. , , .

:

$(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;
}
#loading {
  position: absolute;
  left: 0;
  right: 0;
  margin: auto;
  transform-origin: 50% 50%;
}
<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>
Hide result
+1

, , CSS. , , . 0% 100% . , animation-duration, , .

, animation-duration: 25 50 t=12, , (180 ); (90 ). t, , . , t, , , t=25, , , , .

script, , . 0.25 0 5. , , t .

, t JavaScript, , CSS.

t, CSS-: Myth Busting: CSS- JavaScript

/ CSS, . , . JavaScript , .

, .

$(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);
});
#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" step=".25" class="slider" id="slider" value="0">
<p>Speed: <span id="speed"></span></p>
Hide result
0

TL; DR

, ( )

  • , css Javascript, ( ).

, , , ? , .

  • , , getComputedStyle(loadingElement).getPropertyValue('transform');, , , :

( )

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

, , , , transform: rotate(Xdeg)

, , , , / , , reset .

, - , , .

, , loadingElement.style.removeProperty('animation') , , . setInterval (..., 0), , .

$(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");
	}

});
#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);
  }
<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>
Hide result
0


( .. )

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>
Hide result

currentanimation
http://www.w3.org/TR/css-animations-1/#interface-animationevent-attributes

0

All Articles