Vue中实现指令式动态追加小球动画组件

570 阅读1分钟

1. 小球组件

我们希望可以封装一个通用的小球动画组件,这个组件可以在任何地方调动,而且小球组件可以通过this.$ball({...props})这样的方式调用,让他在用法上接近element-ui

template模板

<template>
  <div class="ball-wrapper">
    <transition @before-enter="beforeEnter"
                @enter="enter"
                @after-enter="afterEnter"
                name="ball">
      <div class="ball" ref="ball" v-show="ballShow">
        <div class="inner">
          <div class="cube"></div>
        </div>
      </div>
    </transition>
  </div>

</template>
  1. 小球的组成主要是分为外层,内层以及内容层
  2. 内层控制小球的x方向,外层y方向移动

props

props: {
     el: {
       type: MouseEvent
     },
   },

把点击事件的对象传入小球中

主要核心js

beforeEnter(el) {
       const x = this.rect.left - window.innerWidth / 2
       const y = -(window.innerHeight - this.rect.top - 140)
       el.style.display = 'block'
       el.style.transform = `translate3d(0,${y}px,0)`
       const inner = el.querySelector('.inner')
       inner.style.transform = `translate3d(${x}px,0,0)`
     },
    enter(el, done) {
       // 触发重绘
       document.body.offsetHeight
       el.style.transform = 'translate3d(0,0,0)'
       const inner = el.querySelector('.inner')
       inner.style.transform = `translate3d(0,0,0)`
       el.addEventListener('transitionend', done)

     },
      afterEnter(el) {
       this.ballShow = false
       el.style.display = 'none'
       this.remove()
     },
      show() {
       const dom = this.el.target
       this.rect = dom.getBoundingClientRect()
       this.ballShow = true
     },

beforeEnter, enter, afterEnter是transition组件的三个钩子函数对应动画开始前,动画开始,动画结束三个阶段.

beforeEnter

这个钩子的主要作用就是计算动画的开始位置

enter

这里有个一个坑,在这里我们需要手动触发浏览器的重绘,这里因为通过js修改的style不会及时更新,组件的display属性还是none所以不会有任何过渡.重绘后这里的display就是block了,transition可以正常过渡.

afterEnter

过渡动画结束,并且销毁整个小球的实例

其实如果要让组件更加通用需要初始化过渡目标的坐标,在这里代码就不贴了,思路和初始化小球一样

2. 挂载小球动画

要触发小球组件就必须调用小球组件的show方法,调用show方法的唯一途径就是获取小球组件的实例. 这样问题就变成如何在vm上绑定小球实例.Vue中有两种方式可以获取组件实例一种是extend另外一种为render函数,

create函数

function create(comp, props) {
  const vm = new Vue({
  	// h就是createElement,组件生成vdom
    render: h => h(comp, {props})
  }).$mount()
  // 追加真实dom
  document.body.appendChild(vm.$el)
  // 由于使用的是新的Vue实例,所以children的第0个就是comp实例化后的组件
  const component = vm.$children[0]
  // 组件挂载销毁方法
  component.remove = function() {
    document.body.removeChild(vm.$el)
    vm.$destroy()
  }
  // 返回组件实例
  return component
}

protoType

export default (Vue) => {
  Vue.prototype.$ball = props => {
    create(BallAnimation, props).show()
  }
}

这里相当于提供了一个install方法,然后再main方法中use,全局挂载会更美观,这里小球挂载基本上已经全部完成了

使用

ballAnitmation(el) {
       this.$ball({
         el
       })
     }

最终效果