[译] 在项目中集成第三方动画库 —— 第二部分

过关斩将

在为本文查资料时,我发现第三方动画库中最多见的动画类型就是过渡动画(并不是 CSS transition)了。有些简单的动画应用于元素出入页面的过程。现代单页应用中最司空见惯的模式就是让一个元素进入页面,顶替另外一个离开页面的元素。想一想这种状况:第一个元素淡出而第二个元素淡入。这个动画能够用于新旧数据交替、按顺序移动面板或切换路由到另外一个页面。Sarah DrasnerGeorgy Marchuk 都列举了不少此类过渡动画的优秀案例。javascript

大多数状况下,动画库不支持在过渡动画中添加或删除元素。用额外的 JavaScript 代码实现的库也许支持这样的功能,但毕竟这样的库不多见,所以咱们如今就来讨论如何实现该功能。css

插入单个元素

对于本例,咱们继续使用 Animate.css,并会用到 fadeInDown 动画。html

在 DOM 中插入元素有不少种方式,在此再也不赘述。我仅会展现如何用动画优雅流畅地插入元素,而非让元素生硬地突现。对于 Animate.css(或其余相似的库)来讲,这个功能十分简单。前端

let insertElement = document.createElement('div');
insertElement.innerText = 'this div is inserted';
insertElement.classList.add('animated', 'fadeInDown');

insertElement.addEventListener('animationend', function () {
  this.classList.remove('animated', 'fadeInDown');
});

document.body.append(insertElement);
复制代码

你如何建立该元素不重要,关键在于确保元素插入以前添加了所需的类。而后,此元素就会以优雅的动画登场。我还监听了 animationend 事件,用于移除动画类。一般来讲,实现此效果的方式不一而足,而这种方式是最直接的方式了。移除动画类是为了方便实现退场效果。咱们不想让退场动画和进场动画相互冲突。vue

移除单个元素

移除单个元素和插入大致相似。目标元素已经存在了,咱们添加所需的动画类就好。而后在 animationend 事件触发时,咱们把该元素从 DOM 中移除。在本例中,咱们会使用 Animate.css 提供的 fadeOutDown 动画,由于它和 fadeInDown 动画是相互呼应的。java

let removeElement = document.querySelector('#removeElement');

removeElement.addEventListener('animationend', function () {
  this.remove();
});

removeElement.classList.add('animated', 'fadeOutDown');
复制代码

如你所见,锁定目标元素、添加类以及在动画结束时移除元素,一鼓作气。react

这个过程会有一个问题,那就是随着目标元素的插入或移除,其余元素将会在页面中重排。咱们须要去考虑使用 CSS 技术和一些布局方式去规避这个问题。android

你方唱罢我登场

如今咱们要切换两个元素,一进一出。条条大路通罗马,但我举的例子是上文中两个例子的结合。ios

参见 CodePen 上来自 Travis Almand(@talmand)的代码示例:第三方动画库:双元素过渡git

我会把 JavaScript 代码分块来说解其原理。首先,咱们建立 button、container 变量分别存储对应的两个 DOM 节点。而后,咱们建立 box一、box2 来存储在 container 中要交换的两个元素。

let button = document.querySelector('button');
let container = document.querySelector('.container');
let box1 = document.createElement('div');
let box2 = document.createElement('div');
复制代码

我写了一个通用函数,用来在每次触发 animationEnd 时移除动画类。

let removeClasses = function () {
  box1.classList.remove('animated', 'fadeOutRight', 'fadeInLeft');
  box2.classList.remove('animated', 'fadeOutRight', 'fadeInLeft');
}
复制代码

第二个函数则是切换功能的核心。首先,咱们肯定当前显示的是哪一个元素。借此咱们能够推断出哪一个元素切入,哪一个切出。切出元素用 switchElements 函数监听,预先移除动画类,避免陷入动画循环。而后,等切出元素的动画完成,咱们将其从容器中移除。接下来,给切入元素添加动画类,并将其插入容器,让它以动画登场。

let switchElements = function () {
  let currentElement = document.querySelector('.container .box');
  let leaveElement = currentElement.classList.contains('box1') ? box1 : box2;
  let enterElement = leaveElement === box1 ? box2 : box1;
  
  leaveElement.removeEventListener('animationend', switchElements);
  leaveElement.remove();
  enterElement.classList.add('animated', 'fadeInLeft');
  container.append(enterElement);
}
复制代码

咱们须要给两个盒子作一些通用设置。接着,将第一个盒子插入到容器中。

box1.classList.add('box', 'box1');
box1.addEventListener('animationend', removeClasses);
box2.classList.add('box', 'box2');
box2.addEventListener('animationend', removeClasses);

container.appendChild(box1);
复制代码

最后,给触发切换的按钮添加点击事件监听。这一系列事件的启动顺序取决于你。在本例中,我打算从按钮点击事件开始。我肯定了正在显示的盒子 —— 即将切出的盒子,给它添加对应的类让它以动画切出。而后我监听切出元素的 animationEnd 事件,触发该事件会调用切实操纵切换的函数 —— 上面列出的 switchElements 函数。

button.addEventListener('click', function () {
  let currentElement = document.querySelector('.container .box');
  
  if (currentElement.classList.contains('box1')) {
    box1.classList.add('animated', 'fadeOutRight');
    box1.addEventListener('animationend', switchElements);
  } else {
    box2.classList.add('animated', 'fadeOutRight');
    box2.addEventListener('animationend', switchElements);
  }
}
复制代码

这个例子眼下有个问题:其代码是专门为这一状况写死的。固然了,它也很易于扩展,也能适应不一样场景。故此,该例子只是用来理解如何实现这一功能的。还好,一些诸如 MotionUI 这样的动画库支持用 JavaScript 操纵元素的过渡动画。另外,像 VueJS 这类 JavaScript 框架也支持切换元素的过渡动画。

我在另外一个例子中展现了一个更灵活的系统。它由一个容器构成,该容器存放着用 data 属性引用的切入和切出动画。容器中的两个元素按照命令切换位置。这个例子的原理是,经过 JavaScript 控制 data 属性能够轻松改变更画。Demo 中还有两个容器,一个用的是 Animate.css 实现动画;另外一个用的则是 Animista。这个例子代码量较大,我将不在本文中讲解,但这个例子的注释很充足,感兴趣的话能够看看。

参见 CodePen 上来自 Travis Almand(@talmand)的代码示例:第三方动画库:自定义动画示例

停下来思考一下……

是全部人都想看到这些动画吗?可能有些人会以为这些动画浮夸,没什么必要,而另外一些人会认为这些动画会致使一些问题。就在前不久,WebKit 为了解决 Vestibular Spectrum Disorder 问题,引入了 prefers-reduced-motion 媒体查询功能。Eric Bailey 也针对该媒体查询功能发表了一篇详尽的说明文章,和一篇关于最佳实践的跟进文章。务必看看这些资料。

那么,你选择的动画库支持 prefers-reduced-motion 吗?若是官方文档没说能支持,那你最好假设不支持。就算官方文档语焉不详,你还能够查看动画库的代码来肯定是否支持,这也容易。例如,Animate.css 在 _base.scss 文件中就有关于媒体查询的代码。

@media (print), (prefers-reduced-motion) {
  .animated {
    animation: unset !important;
    transition: none !important;
  }
}
复制代码

若是你选择的动画库不支持媒体查询,那么看到这里的代码,你也会知道如何本身动手写一个补丁。若是该库使用一个通用类 —— 好比 Animate.css 的 animated 类 —— 那你以这个通用类为目标就好了。若是没有这样一个通用类,那你能够选某个特定的动画类或者本身写一个来实现。

.scale-up-center {
  animation: scale-up-center 0.4s cubic-bezier(0.390, 0.575, 0.565, 1.000) both;
}

@keyframes scale-up-center {
  0% { transform: scale(0.5); }
  100% { transform: scale(1); }
}

@media (print), (prefers-reduced-motion) {
  .scale-up-center {
    animation: unset !important;
    transition: none !important;
  }
}
复制代码

能够看到,我比照 Animate.css 中的代码改造了 Animista 中的动画类。记住,你得把所选库中每一个动画类都作这样的处理。在 Eric 的文章中,他建议对全部动画都作渐进加强,以此减小代码并提升用户体验。

让框架帮你干力气活

在不少方面,React、Vue 这些五花八门的框架,让第三方 CSS 动画库比原生 JavaScript 更加易用,由于你不须要手动接驳那些类或者 animationend 事件。你能够用框架提供的现成功能实现所需效果。使用框架的便利之处在于框架还提供多种操纵动画的方式,知足多种项目需求。下面的例子展现的只是冰山一角。

hover 效果

对于 hover 效果,我建议用 CSS(一如上文中的建议)来设置会比较好。若是你确实须要在 Vue 之类的框架中用 JavaScript 实现,将会是这样:

<button @mouseover="over($event, 'tada')" @mouseleave="leave($event, 'tada')">
  tada
</button>
复制代码
methods: {
  over: function(e, type) {
    e.target.classList.add('animated', type);
  },
  leave: function (e, type) {
    e.target.classList.remove('animated', type);
  }
}
复制代码

和上面列举的原生 JavaScript 方案没有太多不一样。 一样地,有不少办法实现该效果。

夺目动画(Attention seekers)

设置吸引用户注意力的动画效果很是容易。咱们只需添加类名便可,仍然用 Vue 为例:

<div :class="{animated: isPulse, pulse: isPulse}">pulse</div>

<div :class="[{animated: isBounce, bounce: isBounce}, 'infinite']">bounce</div>
复制代码

在 pulse 效果中,当布尔变量 isPulse 的值为 true 时,就会给元素添加两个类名。在 bounce 效果中,当布尔变量 isBounce 的值为 true 时,就会给元素添加 animated 类和 bounce 类。infinite 这个类是默认启用的,这样在布尔变量 isBounce 的值变为 false 以前,bounce效果会一直持续。

过渡动画

幸亏,Vue 的过渡组件能够经过自定义过渡类轻松支持第三方动画类。其余库 —— 好比 React —— 也有相似的功能或插件。要在 Vue 中使用动画类,咱们只需在过渡组件中实现便可。

<transition enter-active-class="animated fadeInDown" leave-active-class="animated fadeOutDown" mode="out-in" >
  <div v-if="toggle" key="if">if example</div>
  <div v-else key="else">else example</div>
</transition>
复制代码

使用 Animate.css 时,咱们仅仅使用必要的类便可。要实现 enter-active 效果,咱们使用 animatedfadeInDown 便可。要实现 leave-active 效果,咱们使用 animatedfadeOutDown 便可。在过渡过程当中,这些类会在适当的时候插入。Vue 会替咱们控制类的插入和移除。

想要了解关于在 JavaScript 框架中使用第三方动画库的更复杂的例子,请点击下方连接:

参见 CodePen 上来自 Travis Almand(@talmand)的代码示例:KLKdJy

来狂欢吧!

这只是一次小小的尝试,还有更多第三方 CSS 动画库等你去探索。有详细完备的,有别出心裁的,有特点鲜明的,有口味偏重的,也有直接明了的。还有针对复杂的 JavaScript 动画的库,例如 GreensockAnime.js。甚至还有专门针对字母动画的库。

真心但愿这些库可以激发你的灵感,创造你本身的 CSS 动画。

若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索