探究 Animation 与 Transition 动画的局限

411 阅读2分钟

前言

CSS Transitions(过渡)被应用于元素指定的属性变化时,该属性经过一段时间,逐渐地过渡到最终需要的值;而CSS Animations(动画)只是在应用时执行之前定义好的操作,它提供更细粒度的控制。[1]

局限

animation 的局限

CSS animation 需要提前设置好起点、终点的状态,不能很好地的去干涉动画的状态。 例如现在需要异步获取数据,渲染列表,列表高度和数据的个数相关,我们不能提前知道个数,因此下面这段动画存在问题:当行数不足20行,列表滑动完毕后,仍在滑动。

即:我们很难控制 animation 的状态,即使通过 js 的方式,也是繁琐的(参看参考文献[1]探查css 找到对应的 keyframes)

.slidedown-css {
  animation: slidedown 2s infinite;
}
@keyframes slidedown {
  from {
    transform: translateY(0);
  }
  to {
    transform: translateY(2rem * 20); // 每行2rem高度,共20行
  }
}

不过,animation 提供了一个很好的属性,可以进行动画的中断。 例如,鼠标悬停时,终止上述动画:

.slidedown-css:hover {
  animation-play-state: paused; // 兼容时,加上前缀
}

transition 的局限

CSS transition 用 js 控制状态的方式,很好实现。但是目前该方法无法实现动画的中断。 即,通过js方式获取到当前的状态,然后改变对应的属性值,也不能阻止本次动画的执行。所有可以看到,该动画结束后,才能停止到指定的属性值。如:

.slidedown-css {
  transition: transform 2s infinite;
}
{
  start(data) {
    // data has loaded
    this.dom = document.getElementsByClassName('slidedown-css')[0];
    dom.style.transform = `translateY(${2 * data.length}rem)`;
  },
  // 响应鼠标悬浮事件
  onMouseOver() {
    const matrix = document.defaultView.getComputedStyle(this.dom); // matrix(1, 0, 0, 1, 31, 21)
    const y = Number(matrix.substring(7).replace(')', '').split(','));
    // 设置动画时间为 0
    this.dom.style.transition = `transform 0s infinite`;
    this.dom.style.transform = `translateY(${y})`;
  }
}

上面代码意图是:鼠标悬停时,将滚动位置停留在当前鼠标的位置。

但是我们会看到,动画到达终点状态后,才迅速的跳到鼠标移入时的高度。因此出现页面闪动现象。

特性

虽然存在这样的局限,但是有几个时机,我们可以利用。

transition

transition 动画,提供了动画状态事件,可以很好地响应动画状态变化:

const transition = document.querySelector('.transition');
const message = document.querySelector('.message');

transition.addEventListener('transitionrun', function() {
  message.textContent = 'transitionrun 触发了';
});

transition.addEventListener('transitionstart', function() {
  message.textContent = 'transitionstart 触发了';
});

transition.addEventListener('transitionend', function() {
  message.textContent = 'transitionend 触发了';
});

利用上面几个事件,我们可以进行一些操作,如动画结束后,触发另外一个动画的执行。

其中transitionruntransitionstart不同的地方是:

  • transitionrun 在 transition 创建的时候被触发。(或者说在某个 delay 开始的时候)
  • transitionstart 在动画实际开始的时候被触发。 (或者说在某个 delay 结束的时候);[2]

参考文献: