elementUI

elementUI——transitions

2020-04-23  本文已影响0人  videring

说明:本文基于element-ui@2.13.0,源码详见element
官网collapse折叠面板中有一例子:

折叠面板

对应的源码:

<el-collapse v-model="activeNames" @change="handleChange">
  <el-collapse-item title="一致性 Consistency" name="1">
    <div>与现实生活一致:与现实生活的流程、逻辑保持一致,遵循用户习惯的语言和概念;</div>
    <div>在界面中一致:所有的元素和结构需保持一致,比如:设计样式、图标和文本、元素的位置等。</div>
  </el-collapse-item>
  <el-collapse-item title="反馈 Feedback" name="2">
    <div>......</div>
    <div>......</div>
  </el-collapse-item>
  <el-collapse-item title="效率 Efficiency" name="3">
    <div>......</div>
    <div>.......</div>
    <div>......</div>
  </el-collapse-item>
  <el-collapse-item title="可控 Controllability" name="4">
    <div>......</div>
    <div>......</div>
  </el-collapse-item>
</el-collapse>

可以看出截图中每项的标题和内容区,来自于一个个的el-collapse-item,展开面板时,是一个缓慢展开或收起的效果,深入到packages/collapse/src/collapse-item.vue,其实就是套了一个el-collapse-transition组件:

<el-collapse-transition>
      <div
        class="el-collapse-item__wrap"
        v-show="isActive"
        role="tabpanel"
        :aria-hidden="!isActive"
        :aria-labelledby="`el-collapse-head-${id}`"
        :id="`el-collapse-content-${id}`"
      >
        <div class="el-collapse-item__content">
          <slot></slot>
        </div>
      </div>
</el-collapse-transition>

src/transitions/collapse-transition.js中定义了vue 函数式组件el-collapse-transition,通过vue官方内置组件transition实现,并给出了beforeEnter、enter、afterEnter、beforeLeave、leave和afterLeave的实现。

import { addClass, removeClass } from 'element-ui/src/utils/dom';

class Transition {
  beforeEnter(el) {
    addClass(el, 'collapse-transition');
    if (!el.dataset) el.dataset = {};

    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);
  }
};


另外,

顺便提一下el-collapse组件中手风琴(accordion)效果的实现方式,所谓手风琴就是每次只能展开一个面板(默认多个面板都可以打开,没有互斥):

<el-collapse v-model="activeNames" @change="handleChange" accordion>
  <el-collapse-item title="一致性 Consistency" name="1"></el-collapse-item>
  <el-collapse-item title="反馈 Feedback" name="2"></el-collapse-item>
  <el-collapse-item title="效率 Efficiency" name="3"></el-collapse-item>
  <el-collapse-item title="可控 Controllability" name="4"></el-collapse-item>
</el-collapse>
  1. 子组件el-collapse-item中点击展开时触发点击方法,核心就是执行了this.dispatch('ElCollapse', 'item-click', this),在dispatch方法中
    通过向上不断寻找组件名为ElCollapse的组件,并在该组件实例中触发item-click事件;
  2. 然后,父组件(也就是ElCollapse),通过this.$on('item-click', this.handleItemClick)方法,监控到item-click事件,触发handleItemClick;
  3. handleItemClick代码如下,简单说就是activeNames数组中当第一个元素存在,如果值跟(通过上一步dispatch传过来的)子组件的name prop一致,那么清空(展开 -> 关闭的意思),否则activeNames[0]赋值为子组件的name (关闭 ->展开的意思);
setActiveNames(activeNames) {
        activeNames = [].concat(activeNames);
        let value = this.accordion ? activeNames[0] : activeNames;
        this.activeNames = activeNames;
        this.$emit('input', value);
        this.$emit('change', value);
}
handleItemClick(item) {
        if (this.accordion) {
          this.setActiveNames(
            (this.activeNames[0] || this.activeNames[0] === 0) &&
            this.activeNames[0] === item.name
              ? '' : item.name
          );
        }
......
}
  1. 第3步更新完activeNames后,各子组件获取到父组件的实例(见官方provide-inject
    ),并计算自身的name prop是否在activeNames数组中,即计算属性isActive,最后在模板中通过v-show="isActive"方式,控制内容区域的展开/收起。
// collapse
provide() {
      return {
        collapse: this // 将自身实例传给子组件
      };
}
// 子组件collapse-item
computed: {
      isActive() {
        return this.collapse.activeNames.indexOf(this.name) > -1;
      }
}

推荐

dispatch、broadcast的实现方式
ElementUI的结构与源码研究
elementUI——locale,国际化方案
elementUI——directives:mousewheel & repeat-click
elementUI——主题

上一篇 下一篇

猜你喜欢

热点阅读