别再说Transition 没法过渡display了

Vue Transition

事件原由

因需求关系,要求作到添加购物车时,有个小球从点击添加的按钮经过运动轨迹到达购物车位置, 由于是须要获取动态位置, 经过css渲染动画爱莫能助, 很幸运的是, vue-transition内置组件提供js钩子函数, 为我提供完成的动画的保障javascript

实践

首先, 让咱们了解下vue transition的一些特性:css

Props:
    name - string,用于自动生成 CSS 过渡类名。例如:name: 'fade' 将自动拓展为.fade-enter,.fade-enter-active等。默认类名为 "v"
    appear - boolean,是否在初始渲染时使用过渡。默认为 false。
    css - boolean,是否使用 CSS 过渡类。默认为 true。若是设置为 false,将只经过组件事件触发注册的 JavaScript 钩子。
    type - string,指定过渡事件类型,侦听过渡什么时候结束。有效值为 "transition""animation"。默认 Vue.js 将自动检测出持续时间长的为过渡事件类型。
    mode - string,控制离开/进入的过渡时间序列。有效的模式有 "out-in""in-out";默认同时生效。
    duration - number | { enter: number, leave: number } 指定过渡的持续时间。默认状况下,Vue 会等待过渡所在根元素的第一个 transitionend 或 animationend 事件。
    enter-class - string leave-class - string appear-class - string enter-to-class - string leave-to-class - string appear-to-class - string enter-active-class - string leave-active-class - string appear-active-class - string 事件: before-enter before-leave before-appear enter leave appear after-enter after-leave after-appear enter-cancelled leave-cancelled (v-show only) appear-cancelled 复制代码

具体的一些使用方法,由于与本文内容不是很贴边,在这里不详细讲了 剩余的就不在这里说了, 开始主题html

举个例子更加让人了解问题

不知道你们在写原生js的时候,有没有遇到过这样的一个问题前端

一个DOM元素从display: none变为display: block的时候,想要它想vue的transition同样,利用动画过渡过来, 你颇有可能会这么写vue

<div class="c-transition__container" style="display: none;"></div>
复制代码
.c-transition__container {
        width: 100px;
        height: 100px;
        background: red;
        transition-duration: .5s;
        transition-property: transform;
    }
复制代码
const oDiv = document.getElementsByClassName('c-transition__container')[0]
    document.onclick = function() {
        oDiv.style.transform = 'translate3d(100px, 0, 0)'
        oDiv.style.display = 'block'
    }
复制代码

事实老是难以接受, 这么写是不会有平滑过渡的效果,而是直接在最终的位置显示java

沮丧的你今后弃坑,发誓不再碰前端:laughing:web

别着急, 下面讲解如何实现从0到1的平滑浏览器

首先咱们要了解浏览器的渲染时机app

for(let i = 0; i < 100; i++) {
        oDiv.style.transform = 'translate(100px, 0, 0)'
    }
复制代码

以上的for循环语句里操做了100次的div, 但实际上浏览器并不会渲染100次, 根据上文函数

你理解错误的Vue nextTick

咱们能够得知, 浏览器的UI Render是在每一个macroTask最后清空microTask队列后才会触发一次, 可是浏览器会根据实际状况来肯定是否须要渲染, 一般在每隔16.7ms的状况下会渲染一次, 在此期间,浏览器会将全部的DOM操做推入到队列中,在进行渲染的时候会一个一个取出,直到清空队列。

浏览器渲染过程:

JavaScript:JavaScript 实现动画效果,DOM 元素操做等。(Cpu)

Style(计算样式):肯定每一个 DOM 元素应该应用什么 CSS 规则。(Cpu)

Layout(布局):计算每一个 DOM 元素在最终屏幕上显示的大小和位置。因为 web 页面的元素布局是相对的,因此其中任意一个元素的位置发生变化,都会联动的引发其余元素发生变化,这个过程叫 reflow。(每一个DOM对应一个渲染层)(Cpu)

Paint(绘制):在多个层上绘制 DOM 元素的的文字、颜色、图像、边框和阴影等, 这个过程叫作repaint。(Cpu)

Composite(渲染层合并):按照合理的顺序合并图层而后显示到屏幕上。(进入GPU)(render Tree 解析渲染)
复制代码

理解了以上部分, 那么接下来的问题就好办了

咱们逐句分析刚才的案例:

  1. 首先咱们初始化了div的一些基本状态, 包括宽高, transition等一些css样式
  2. 在点击document的时候,咱们将div的style复制为了translate(100px, 0, 0),而且更新了状态display: block, 注意这个时候, 也就是我刚才提到的浏览器, 并无进行直接进行渲染,而是将它推入到了队列里, 但记得一句, DOM Tree是实时更新的, 此时虽然没有渲染,可是DOM Tree里的状态已经更新了,这个时候DOM transform已经变成了100px, 当真正渲染的时候, 是从100px变为100px,并非从0变成100px,因此咱们看到的是没有过渡效果的。

解决方案

从以上的分析,咱们能够基本了解为何会是这种现象, 下面我来说下解决方案:

  1. 首先就是你们所熟知的setTimeout,这个的确能够解决以上问题, 可是setTimeout属于macroTask, 每一个task的最后都会触发一次UI Render, 以上操做会形成屡次渲染, 因此并不推荐使用

  2. (推荐) 强制浏览器清空队列进行渲染操做

    在获取布局信息操做的时候, 会强制浏览器清空队列进行渲染, API有如下几个: innerHeight、scrollTop、offsetHeight、getBoundingClientRect等等

    这个时候, 浏览器从新进行回流重绘渲染操做, 这个时候触发DOM操做均可以获得有效回馈

总结

Vue transitionjs钩子函数其实就是原生js写的动画, 一样从无到有进行平滑, 你学会了吗?

相关文章
相关标签/搜索