子菜单的展开、收缩功能在许多系统上都很常见,如果想要在打开收起时带有动画效果,过去常常会通过 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>