当前位置: > > > Vue.js - 元素展开、收起动画效果组件(附:二级菜单的展开、收缩动画效果)

Vue.js - 元素展开、收起动画效果组件(附:二级菜单的展开、收缩动画效果)

    子菜单的展开、收缩功能在许多系统上都很常见,如果想要在打开收起时带有动画效果,过去常常会通过 jQuery 实现。而在 Vue 项目中,我们可以单独封装一个动画组件,方便使用。

1,效果图

  • 点击一级菜单时,子菜单会从上往下逐渐展开。
  • 再次点击一级菜单,子菜单又会从下往上收起。
            

2,创建动画组件(collapseTransition.js)

组件代码如下,其内容是通过 overflow 获取高度的方法创建了一个函数式组件实现展开、收起的动画效果。
/* 视图伸缩动画效果组件 */
/* jshint esversion: 6 */
const elTransition = '0.3s height ease-in-out, 0.3s padding-top ease-in-out, 0.3s padding-bottom ease-in-out';
const Transition = {
  'before-enter' (el) {
    el.style.transition = elTransition;
    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';
  },

  'after-enter' (el) {
    el.style.transition = '';
    el.style.height = '';
    el.style.overflow = el.dataset.oldOverflow;
  },

  'before-leave' (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) {
      el.style.transition = elTransition;
      el.style.height = 0;
      el.style.paddingTop = 0;
      el.style.paddingBottom = 0;
    }
  },

  'after-leave' (el) {
    el.style.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: 'collapseTransition',
  functional: true,
  render (h, { children }) {
    const data = {
      on: Transition
    };
    return h('transition', data, children);
  }
};

3,使用样例

使用时我们只需引入这个动画组件,包裹在需要显示、隐藏的元素外部即可(不再需要 css 与其它逻辑)
<template>
  <div id="navigation" class="flex flex-direction-column">
    <!-- 一级菜单 开始 -->
    <template v-for="firstLevel in menus">
      <div  class="menu-item first-level" @click='changeExpand(firstLevel)'>
        <img :src="firstLevel.icon" alt="">
        <span>{{ firstLevel.title }}</span>
      </div>
      <!-- 二级菜单 开始 -->
      <collapse-transition>
        <div class="second-level-container" v-show="firstLevel.isExpand">
          <div class="menu-item second-level" v-for="secondLevel in firstLevel.children">
            <img src="../assets/images/dot_unselected.png" alt="">
            {{ secondLevel.title }}
          </div>
        </div>
      </collapse-transition>
      <!-- 二级菜单 结束 -->
    </template>
    <!-- 一级菜单 结束 -->
  </div>
</template>

<script>
import axios from 'axios'

// 引入伸缩效果组件
import collapseTransition from './collapseTransition'

export default {
  data () {
    return {
      isActive:false,
      menus:[{
      	"title": "终端",
        "icon": "/static/images/icon_1.png",
        "isExpand": true,
      	"children": [{
      		"title": "智能物联网边缘计算"
      	}, {
      		"title": "终端融合能力"
      	}]
      }, {
      	"title": "网络",
        "icon": "/static/images/icon_2.png",
        "isExpand": true,
      	"children": [{
      		"title": "客户侧内部组织网"
      	}]
      }]
    }
  },
  components: {
    collapseTransition
  },
  methods:{
    //展开收起按钮点击 
    changeExpand(menu) {
      menu.isExpand = !menu.isExpand;
      console.log(menu.isExpand);
    }
  }
}
</script>
<style scoped>
  #navigation {
    background-color: #e7e7e7;
  }

  .menu-item {
    width: 327px;
    height: 49px;
    line-height: 49px;
    font-size: 14px;
    border-bottom: solid 1px #c4c4c4;
    color: #3d3d3d;
    cursor:pointer;
  }

  .first-level {
    background: url(../assets/images/first_level_menu_bg.png);
    font-weight: bold;
  }

  .first-level img:first-child {
    vertical-align:middle;
    margin-left: 20px;
    margin-right: 15px;
  }
  
  .first-level span {
    width: 230px;
    display: inline-block;
  }
  
  .second-level {
    width: 320px;
    border-left: solid 7px #e7e7e7;
  }
  
  .second-level:hover {
    background-color: #ffffff;
    color: #13a3a9;
    border-left: solid 7px #f6ab36;
  }

  .second-level img:first-child {
    vertical-align:middle;
    margin-left: 61px;
    margin-right: 10px;
  }
</style>
评论0