Как я могу изменить скорость CSS-анимации?

У меня есть анимация:

img.rotate {
  animation-name: rotate;
  animation-duration: 1.5s;
  animation-iteration-count: infinite;
  animation-timing-function: linear;
}

@keyframes rotate {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

У меня вращающееся изображение на бесконечности. Теперь я хочу изменить скорость вращения с помощью JS. Я пытался:

$('img').css('animation-duration', '2s');

Продолжительность меняет скорость, но анимация перезагружается до фрейма 0% (первый), но я просто хочу, чтобы анимация продолжалась, как будто ничего не произошло; Я не хочу возвращаться к 0%.

Как я могу это сделать?

Ответ 1

Это очень сложный процесс. В настоящее время единственным способом изменения анимации анимации является использование setInterval для приближения текущего процентного содержания ключевых кадров, сохранения свойств этого ключевого кадра, обновления анимации (в вашем случае только скорости), а затем применения новой версии анимация, начиная с сохраненной точки. Я создал аналогичный пример в статье, которую я написал для CSS Tricks под названием Управление анимацией и переходами с помощью Javascript

Как описано в статье, было бы легче изменить скорость на animationIteration, в вашем случае это выглядело бы как this demo, но он все еще далек от хороших

 var pfx = ["webkit", "moz", "MS", "o", ""],
    rotates = $('.rotate'),
    prevent = false;

function PrefixedEvent(element, type, callback) {
    for (var p = 0; p < pfx.length; p++) {
        if (!pfx[p]) type = type.toLowerCase();
        element.addEventListener(pfx[p]+type, callback, false);
    }
}

function listen() {
    for(var i = 0, j = rotates.length; i < j; i ++) {
        PrefixedEvent(rotates[i], "AnimationIteration", function() {
            if(!$('#faster').is(':checked') && !prevent) {
                var el = $(this),  
                   newOne = el.clone(true)
                            .css({'animation':'rotate 2s linear infinite'});
                el.before(newOne);        
                $("." + el.attr("class") + ":last").remove();
                rotates = $('.rotate');
                listen();
                prevent = true;
            }
            else if($('#faster').is(':checked') && prevent) {
                prevent = false;
                var el = $(this),
                   newOne = el.clone(true)
                            .css({'animation':'rotate 1.5s linear infinite'});
                el.before(newOne);
                $("." + el.attr("class") + ":last").remove();
                rotates = $('.rotate');
                listen();
            }
        });
    }
}
listen();

В настоящий момент мы не можем просто перезапустить анимацию с другой функцией синхронизации (даже если вы приостановите ее и запустите ее снова, как сказал Майкл). В результате вам нужно либо использовать setInterval (что дает более высокую задержку), либо клонировать элемент с измененными значениями. Для получения дополнительной информации о подходе, который я использовал, посмотрите на демо, я добавил комментарии ко всем добавляемым javascript

РЕДАКТИРОВАТЬ на основе вашей связанной скрипты в комментариях

Поскольку вы пытаетесь имитировать рулетку, вам совсем не обязательно использовать javascript! Просто измените анимацию rotation, чтобы изменить скорость с течением времени (: Здесь грубая версия о том, как вы это сделаете поэтому, но вы должны добавить больше ключевых кадров, чтобы сделать его более гладким, чем в настоящее время ведет себя

@-webkit-keyframes rotate {
    0%   { -webkit-transform: rotate(   0deg); }
    30%  { -webkit-transform: rotate(2600deg); }
    60%  { -webkit-transform: rotate(4000deg); }
    80%  { -webkit-transform: rotate(4500deg); }
    100% { -webkit-transform: rotate(4780deg); }
}

РЕДАКТИРОВАТЬ 2

Поскольку вам нужно, по-видимому, иметь случайную конечную позицию и, вероятно, запустить ее несколько раз, вы можете сделать что-то вроде этого, который намного проще, использует только CSS-преобразования и невероятно полезный

var img = document.querySelector('img');
img.addEventListener('click', onClick, false);    
var deg = 0;

function onClick() {
    this.removeAttribute('style');        
    deg += 5 * Math.abs(Math.round(Math.random() * 500));        
    var css = '-webkit-transform: rotate(' + deg + 'deg);';        
    this.setAttribute(
        'style', css
    );
}

и CSS

img { -webkit-transition: -webkit-transform 2s ease-out; }

Изменить 3

Это не совсем актуально или не имеет значения, но вы можете делать очень похожие вещи, используя GSAP, например Я сделал в этом проекте, чтобы сделать его более интерактивным/динамическим

Ответ 2

Вы можете сделать это, установив оболочку и скопировав ее.

CSS

.wrapper, .inner {
    position: absolute;
    width: 100px;
    height: 100px;
  -webkit-animation-name: rotate;
  -webkit-animation-iteration-count: infinite;
  -webkit-animation-timing-function: linear;
  animation-name: rotate;
  animation-iteration-count: infinite;
  animation-timing-function: linear;
}
.wrapper {
    left: 40px;
    top: 40px;
  -webkit-animation-duration: 2.5s;
    -webkit-animation-play-state: paused;
    -webkit-animation-direction: reverse;
  animation-duration: 2.5s;
    animation-play-state: paused;
    animation-direction: reverse;
}
.wrapper:hover {
    -webkit-animation-play-state: running;
    animation-play-state: running;
}
.inner {
    display: block;
    background-color: red;
  -webkit-animation-duration: 1.5s;
  animation-duration: 1.5s;
}
@keyframes rotate {
  from { transform: rotate(0deg);  }
  to {    transform: rotate(360deg);  }
}    
@-webkit-keyframes rotate {
  from { -webkit-transform: rotate(0deg);  }
  to {    -webkit-transform: rotate(360deg);  }
}    

демо

(наведите указатель на квадрат, чтобы замедлить его)