element transtion动画效果

117 阅读3分钟

此处仅解析collapse 展开折叠的实现

  • element 通过构造一个Transition类对象 设置了VUE内置transition组件对应的js钩子函数

  • 通过render方法 返回一个Vue内置的transition组件并通过on:new transition()的形式构造一个动画类绑定对应事件函数

  • element自定义的过渡组件 组件名称为ElCollapseTransition

  • 关于组件配置了name属性 对应官方文档 类名替换规则

  • 关于类名及js钩子的执行时机官方说明 (总结:恰当时机)

源码

element ui 通过 jsx 语法实现源码
没有什么好备注的 思路很清晰

class Transition {
  beforeEnter(el) {
    addClass(el, 'collapse-transition');
    // js原生dom添加类名方法
    // document.querySelector('.class').classList.add('collapse-transition')
    // document.querySelector('.class').classList.remove('collapse-transition')
    if (!el.dataset) el.dataset = {};
    // 将dom上的style设置的padding样式存起来 后面设置dom出现的时候用
    
    el.dataset.oldPaddingTop = el.style.paddingTop;
    el.dataset.oldPaddingBottom = el.style.paddingBottom;
    //
    el.style.height = '0';
    el.style.paddingTop = 0;
    el.style.paddingBottom = 0;
  }

  enter(el) {
    el.dataset.oldOverflow = el.style.overflow;
    if (el.scrollHeight !== 0) {
      el.style.height = el.scrollHeight + 'px';
      el.style.paddingTop = el.dataset.oldPaddingTop;
      el.style.paddingBottom = el.dataset.oldPaddingBottom;
    } else {
      el.style.height = '';
      el.style.paddingTop = el.dataset.oldPaddingTop;
      el.style.paddingBottom = el.dataset.oldPaddingBottom;
    }

    el.style.overflow = 'hidden';
  }

  afterEnter(el) {
    // for safari: remove class then reset height is necessary
    removeClass(el, 'collapse-transition');
    el.style.height = '';
    el.style.overflow = el.dataset.oldOverflow;
  }

  beforeLeave(el) {
    if (!el.dataset) el.dataset = {};
    el.dataset.oldPaddingTop = el.style.paddingTop;
    el.dataset.oldPaddingBottom = el.style.paddingBottom;
    el.dataset.oldOverflow = el.style.overflow;

    el.style.height = el.scrollHeight + 'px';
    el.style.overflow = 'hidden';
  }

  leave(el) {
    if (el.scrollHeight !== 0) {
      // for safari: add class after set height, or it will jump to zero height suddenly, weired
      addClass(el, 'collapse-transition');
      el.style.height = 0;
      el.style.paddingTop = 0;
      el.style.paddingBottom = 0;
    }
  }

  afterLeave(el) {
    removeClass(el, 'collapse-transition');
    el.style.height = '';
    el.style.overflow = el.dataset.oldOverflow;
    el.style.paddingTop = el.dataset.oldPaddingTop;
    el.style.paddingBottom = el.dataset.oldPaddingBottom;
  }
}
export default {
  name: 'ElCollapseTransition',
  functional: true,
  render(h, { children }) {
    const data = {
      on: new Transition()
    };
    // 声明了组件的
    return h('transition', data, children);
  }
};

对应的css
根据不同的动画方式 设置专门的动画类 这里只写一种 el-zoom-in-top(往上缩放)
其他效果(往左、往右、往下、中间)通过改变 transform-origin 值 即可 不做解释

   // 设置动画时间、动画属性
   .collapse-transition {
           transition: 0.3s height ease-in-out, 0.3s padding-top ease-in-out, 0.3s padding-bottom ease-in-out;
   }
   .el-zoom-in-top-enter-active,
   .el-zoom-in-top-leave-active {
       opacity: 1;
       transform: scaleY(1);
       transition: 0.3s;
       transform-origin: center top;
   }

   .el-zoom-in-top-enter,
   .el-zoom-in-top-leave-active {
       opacity: 0;
       transform: scaleY(0);
   }

动画通过原生js实现思路

  • 出现之前 beforeEnter
    • 给dom节点上加 .collapse-transition 类名( 设置参与动画的属性和动画时间 不论何种动画方式都要设置这个所以单独抽离出来一个类名公用了-----我推测的 )
    • 给dom节点添加上 el-zoom-in-top-enter 出现前样式(设置元素"opacity:0")。注:不设置为零 即使设置了外层div高度为0,但内部元素仍然会显现 但不占据页面高度会和下面的元素重叠
    • 通过scrollHeight获取dom元素的高度
    • 通过dom.style.paddingTop、style.paddingBottom获取元素的行内样式上的padding值
    • 在原生节点上设置dataset对象。将刚获取的数据存起来
    • 然后通过 el.style.height、style.paddingTop、style.paddingBottom 将元素的高、内边距设置为零
  • 出现时 enter
    • 将节点的 overflow 属性值保存起来 dom.dataset.oldOverflow = dom.style.overflow;
    • 将存在dataset中的节点的 高和padding恢复
    • 设置节点的overflow值为hidden
  • 出现后 afterEnter
    • 移除出现前设置的 .collapse-transition 类名
    • 设置 el.style.height = ''; 行内样式就不会有 出现时 设置的height高数据了
    • 出现时更改了overflow值 改回去
// js原生dom添加类名方法
    // document.querySelector('.class').classList.add('collapse-transition')
    // document.querySelector('.class').classList.remove('collapse-transition')

有文章说有bug