13

CSS/JS:如何实现动画的暂停和播放

 3 years ago
source link: https://zhuanlan.zhihu.com/p/103513500
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

CSS/JS:如何实现动画的暂停和播放

广发证券 技术工程师

如题所示,我遇到一个常见的动画场景: 让一个圆形图框让它转动起来。当然如果单纯这样做很简单,声明一个keyframe,控制transform: rotate(deg)属性从0到360度变化。然后在animation中使用这个keyframe就可以了。

但是如果在此基础上添加一个要求: 增加动画的暂停和重新播放功能。这样的话又该如何实现呢?就类似于音乐软件的那个转盘,想要让它在音乐暂停的时候转动动画也暂停,等到音乐播放的时候再继续转动

v2-739f0185344171e16deb0d9313d838ee_720w.jpg

这时候我发现了一个最为关键的问题:传统的animation属性或者keyframe提供的的播放功能是“一镜到底”的,也就是直接从开始播放到结束,没有中断控制的功能。

正当我感到烦恼的时候我查询到了一个我正需要的CSS属性:animation-play-state。它包含有两个值:pausedrunning。前者可以使animation属性制定的动画处于暂停状态,后者可以使动画处于播放状态。这正是我需要的东西

我们看看怎么来使用它吧,下面我们来演示一下,首先我们用@keyframes声明一个动画帧然后在animation中使用它。

再然后我们给两个类:active类和paused类,分别设为animation-play-state: running; 和animation-play-state: paused;

@keyframes rotateAnimate {
  0% {
    transform: rotate(0deg);
  }

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

.img {
  width: 200px;
  height: 200px;
  border-radius: 50%;
  animation: rotateAnimate 5s linear infinite;
}
.active {
  animation-play-state: running;
}
.paused {
  animation-play-state: paused;
}

最后我们讲会在html中给一个Div动态地切换active和paused类, 并且在按钮点击的时候实现切换,这样就可以实现播放和暂停啦,下面是React的演示代码

import React from "react";
import "./styles.css";

export default class App extends React.Component {
  state = {
    paused: false
  };
  handleClick = () => {
    this.setState(({ paused }) => {
      return {
        paused: !paused
      };
    });
  };
  render() {
    const { paused } = this.state;
    const extraClass = paused ? "paused" : "active";
    return (
      <div>
        <img
          alt="img"
          class={`img ${extraClass}`}
          src="https://s2.ax1x.com/2020/01/09/lWeCsf.jpg"
        />
        <br />
        <button onClick={this.handleClick}>暂停/播放</button>
      </div>
    );
  }
}

但是问题来了,animation-play-state的兼容性怎么样呢,让我们看看,果然问题还是出现在IE浏览器: IE10以下不兼容。

那么问题来了,我们如果想要在IE10以下也实现旋转动画的播放和暂停该怎么办呢?

答案是:自己在JS中手动递归setTimeout改变div的transform: rotate。

你可能会问,为什么我们不用专供动画的requestAnimationFrame呢?那是因为:requestAnimationFrame的兼容性和animation-play-state一个鸟样,都是对IE10以下不兼容。(心塞)

因此我用setTimeout去模拟requestAnimationFrame的效果,我们统一假设帧渲染频率为60HZ,在这种情况下屏幕16.7秒刷新一次,把这作为我的setTimeout里的函数递归调用的间隔时间。如下所示

import React from "react";
import "./styles.css";

export default class App extends React.Component {
  state = {
    play: false,
    deg: 0
  };
  handleAnimation() {
    setTimeout(() => {
      const { play } = this.state;
      let { deg } = this.state;
      if (!play) return;
      deg = (deg + 1) % 360;
      this.setState({ deg });
      this.handleAnimation();
    }, 16.7);
  }

  handleClick = () => {
    const { play } = this.state;
    if (play) {
      this.setState({
        play: false
      });
    } else {
      this.setState({
        play: true
      });
      this.handleAnimation();
    }
  };

  render() {
    const { deg } = this.state;
    const style = {
      transform: `rotate(${deg}deg)`
    };
    return (
      <div>
        <img
          alt="img"
          className={"img"}
          style={style}
          src="https://s2.ax1x.com/2020/01/09/lWeCsf.jpg"
        />
        <br />
        <button onClick={this.handleClick}>暂停/播放</button>
      </div>
    );
  }
}

CSS代码

.img {
  width: 200px;
  height: 200px;
  border-radius: 50%;
}

自己实现当然也有缺点,总结如下

  • 当多种CSS特性复合的时候,实现起来并不太方便
  • 播放速度和效果受限,setTimeout实际是有最短时间的,如果动画确实很快执行完毕的话,效果会不流畅

本文完


附两句霸气的台词

  • the world!
  • 时间,开始流动



About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK