博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
vue 动画监听简略分析
阅读量:7250 次
发布时间:2019-06-29

本文共 7516 字,大约阅读时间需要 25 分钟。

前言

在几年前 jQuery 流行的时候大家都通过js去操作dom元素的css来实现以及监听动画,甚至出现了很多通过js去监听动画的动画库。

前端时间在写 vue 的时候发现 vue 中实现动画效果,并没有通过 js 去不停的操作css样式,那么在css中是怎么去监听dom元素的动画效果呢?

纯js动画监听示例

实现下图中的动画效果监听:

clipboard.png

#demo {    width: 200px;    height: 200px;    background: red;    opacity: 1;    margin-bottom: 20px;    transition: opacity 1s;}#demo.hide {    opacity: 0;}#demo.show {    opacity: 1;}
opacity
(function() {    var $target = document.getElementById('demo');    var transitions = {        'transition': 'transitionend',        'OTransition': 'oTransitionEnd',        'MozTransition': 'transitionend',        'WebkitTransition': 'webkitTransitionEnd'    }    var eventName = undefined;    for(t in transitions){        if( $target.style[t] !== undefined ){            eventName = transitions[t];            break;        }    }        eventName && $target.addEventListener(eventName, function() {        alert('Transition end!');    });        runAction = function() {        if (eventName) {            var className = $target.className;            $target.className = className.indexOf('hide') == -1 ? 'hide' : 'show';        } else {            console.warn('您的浏览器不支持transitionend事件');        }    }})();

代码很简单,就是通过js中的 transitionend 来监听动画执行效果,如果是帧动画的话,需要使用 animationend。

万变不离其宗,vue中实现动画监听也是基于 transitionend 来进行操作的。
效果传送门:

实现效果

clipboard.png

公共样式长这样

.demo {    height: 120px;    position: relative;    div {        position: absolute;        background: red;        width: 100px;        height: 100px;        left: 0;        top: 0;    }}

vue transitionend

export default {    data() {        return {            needAnim: false,        };    },    mounted() {        setTimeout(() => {            this.needAnim = true;        }, 0);    },    methods: {        actionEnd() {            alert('demo-1 action end');        },    },};

同样的道理,帧动画需要使用 animationend, 后面不再说明。

我们来看一下vue中是如何做到的(代码太多,部分代码用“...”省略)。
关键代码: src/core/instance/state.js

function initMethods (vm: Component, methods: Object) {  for (const key in methods) {    // 将事件绑定到虚拟Dom上    vm[key] = methods[key] == null ? noop : bind(methods[key], vm)    // ...  }}

与click事件的绑定无异,初始化的时候就把“transitionend”绑定到“VDom”上,以达到动画监听效果。

transition

有两种用法,一种是通过css控制动画效果

.demo-3 {    div { top: 20px; }    /* 定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除 */    .anim-enter { left: 0px; }    /* 定义进入过渡生效时的状态。在整个进入过渡的阶段中应用 */    /* 在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数 */    .anim-enter-active { transition: left 2s; }    /* 定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter 被移除),在过渡/动画完成之后移除 */    .anim-enter-to { left: 200px; }    /* 定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除 */    .anim-leave { left: 200px; }    /* 定义离开过渡生效时的状态。在整个离开过渡的阶段中应用 */    /* 在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数 */    .anim-leave-active { transition: left 2s; }    /* 定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave 被删除),在过渡/动画完成之后移除 */    .anim-leave-to { left: 0px; }}
demo-3
export default {    data() {        return { anim: false };    },};

用 vue 官方文档上有一张图说明整个生命周期

clipboard.png

另一种是通过脚本控制动画效果

.demo-3, .demo-4 {    div { top: 20px; }}
demo-4
export default {    data() {        return { anim: false };    },    methods: {        beforeEnter(el) {            console.warn('beforeEnter');            el.style = 'transition: left 2s;';        },        // 当与 CSS 结合使用时,回调函数 done 是可选的        enter(el, done) {            console.warn('enter');            setTimeout(() => { el.style = 'transition: left 2s; left: 200px'; });            setTimeout(() => done(), 2000);        },        afterEnter(el) {            console.warn('afterEnter');            el.style = 'left: 200px;';        },        enterCancelled(el) {            console.warn('enterCancelled');        },        beforeLeave(el) {            console.warn('beforeLeave');            el.style = 'left: 200px;';        },        // 当与 CSS 结合使用时        // 回调函数 done 是可选的        leave(el, done) {            console.warn('leave');            el.style = 'transition: left 2s;';            setTimeout(() => done(), 2000);        },        afterLeave(el) {            console.warn('afterLeave');            el.style = 'left: 0px;';        },        // leaveCancelled 只用于 v-show 中        leaveCancelled(el) {            console.warn('leaveCancelled');        },    },};

这种做法通过我们在 transition 元素上绑定不同的事件,通过控制回调中提供的 done方法 达到监听效果。

transition 元素

transition 元素在vue中并不会生成 div 元素 有点像 template。

关键代码: src/platforms/web/runtime/components/transition.js

export default {  name: 'transition',  props: transitionProps,  abstract: true,  render (h: Function) {    // ... 省略很多代码         const rawChild = children[0]    // ... 省略很多代码    return rawChild  }}

在 render 中直接返回了第一个子元素来渲染,具体的 patch 逻辑这里不做说明。

transition 动画控制源码

上面我们展示了 transition 的两种监听动画的方法,下面看几段关键代码

src/platforms/web/runtime/modules/transition.js

const autoCssTransition: (name: string) => Object = cached(name => {  return {    enterClass: `${name}-enter`,    leaveClass: `${name}-leave`,    appearClass: `${name}-enter`,    enterToClass: `${name}-enter-to`,    leaveToClass: `${name}-leave-to`,    appearToClass: `${name}-enter-to`,    enterActiveClass: `${name}-enter-active`,    leaveActiveClass: `${name}-leave-active`,    appearActiveClass: `${name}-enter-active`  }})function resolveTransition (def?: string | Object): ?Object {  // ... 省略很多代码  extend(res, autoCssTransition(def.name || 'v'))}

拼装 class 类名,以我们传入的 name 属性 或者 v 开头,并且 name 与 v 后面的类名是固定的。

export function enter (vnode: VNodeWithData, toggleDisplay: ?() => void) {  const el = vnode.elm  // ... 省略很多代码  const startClass = isAppear ? appearClass : enterClass  const activeClass = isAppear ? appearActiveClass : enterActiveClass  const toClass = isAppear ? appearToClass : enterToClass  const beforeEnterHook = isAppear ? (beforeAppear || beforeEnter) : beforeEnter  const enterHook = isAppear ? (typeof appear === 'function' ? appear : enter) : enter  // ... 省略很多代码  // 标记是否使用自定义样式控制css  const expectsCSS = css !== false && !isIE9  // 标记用户是是否需要自己控制动画监听,也就是enter事件是否存在  const userWantsControl =    enterHook && (enterHook._length || enterHook.length) > 1  // done 回调,用来手动结束动画效果  const cb = el._enterCb = once(() => {    if (expectsCSS) {      removeTransitionClass(el, toClass)      removeTransitionClass(el, activeClass)    }    if (cb.cancelled) {      if (expectsCSS) {        removeTransitionClass(el, startClass)      }      enterCancelledHook && enterCancelledHook(el)    } else {      afterEnterHook && afterEnterHook(el)    }    el._enterCb = null  })  if (!vnode.data.show) {    // 插入元素时通过注入插入钩子, 调用enter事件    mergeVNodeHook(vnode.data.hook || (vnode.data.hook = {}), 'insert', () => {      // ... 省略很多代码      // enterHook 调用的是在transition 传入的 enter 方法      enterHook && enterHook(el, cb)    }, 'transition-insert')  }  beforeEnterHook && beforeEnterHook(el)  // 使用样式控制的时候把 v-before-enter 与 v-enter样式加到dom元素上  if (expectsCSS) {    addTransitionClass(el, startClass)    addTransitionClass(el, activeClass)    nextFrame(() => {      addTransitionClass(el, toClass)      removeTransitionClass(el, startClass)      if (!cb.cancelled && !userWantsControl) {        // 在元素上添加 transitionend监听        // 方法位于 transition-util.js 中        whenTransitionEnds(el, type, cb)      }    })  }  // ... 省略很多代码}

使用样式控制样式监听时通过添加和改变 dom 样式名以及 transitionend 达到监听效果。

手动监听动画时在元素插入时添加钩子提供回调函数以达到监听效果。
与 enter 对应的 leave 逻辑其实都差不多,这里不做过多讲解。

其他

以上篇幅只是一个初步简略分析,时间有限,很多细节并未深究。

以上内容鉴于 vue 2.18 版本,其他版本可能会有所改动。

参考资料

转载地址:http://llhbm.baihongyu.com/

你可能感兴趣的文章
什么是Floating (浮动)规则?
查看>>
分布式文件系统-FastDFS
查看>>
HTML5 rotate 做仪表盘
查看>>
为什么说荆州松滋刘氏采穴堂是刘开七、刘广传的后裔
查看>>
React中使用Ant Table组件
查看>>
第四篇 快速、轻量、可扩展、易于使用的EmEditor
查看>>
MySQL删除小写记录
查看>>
用shell脚本收集查询IP信息的网站
查看>>
shiro整合oauth
查看>>
超级网管员——网络管理
查看>>
AjaxControltoolkit(工具包)安装步骤说明
查看>>
利用组策略进行的一次Windows主机安全整改
查看>>
Ruby语法学习笔记(1)
查看>>
Windows Phone 7 使用选择器(Chooser)
查看>>
QOS 之 WRED
查看>>
ASP.NET MVC5 知识点整理
查看>>
CCNP 640-892知识点中文精简解释
查看>>
listview适配器与加载过程详解
查看>>
SDN控制器列表
查看>>
LeetCode - 8. String to Integer (atoi)
查看>>