vuejs如何实现这样的展开收起动画?

zzard
  • 143

图片描述

如图中的是jquery实现的,那么在vue中如何实现呢?
我试着用<transition>,但是用的很笨,望前辈们指教指教,如果能贴个示例代码的话,万分感谢~!

回复
阅读 38.9k
8 个回答

这个组件当初自己也搞了很久,最后因为不知道隐藏层的高度没有使用css实现效果,可以参考element-ui通过overflow获取高度的方法创建了一个函数式组件实现了效果:

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

然后就可以在需要的地方当做组件使用(不再需要css与其它逻辑):

  <!-- 隐藏部分
  -------------------------- --> 
  <collapse-transition>
     <div class="collapse-wrap"
       v-show="isActive">
      <!-- @slot default -->
      <slot></slot>
    </div> 
  </collapse-transition>

补充一种情况: ul>li的情况,容器高度一般没有值(取决于元素的累加高度,或者其他情况),这种情况需要使用js的方式实现,height = 0 ,最终状态可以通过e.scrollHeight求到需要高度,这个属性很关键,宽度未知的情况也类似,参考crollHeight mdn
例子: https://codepen.io/huoguozhan...

不明所以
  • 2
新手上路,请多包涵

上面的都能实现 ,其实大致思路都一样.附上另一种写法

vue部分
<divclass="moreInfo" @click="boxshow = !boxshow">
        展开
</div>
<div :class="boxshow==true?'box':'boxHidden' " ></div>
//boxshow默认为false
css部分
.box {
        height:200px;
        width: 100%;
        background-color:black;
        transition: all 0.5s ease-in-out;
    }
    .boxHidden{
        transition: all 1s ease-in-out;
        height: 0;
        overflow: hidden;
    }

vue transition很容易的说。 用max-height效果最佳

    in .vue:
    <transition name="sub-comments">
    <div>...</div>
    </transition>
    
    
    in css:
   .sub-comments-leave-active,.sub-comments-enter-active {
        transition: max-height 0.3s;
    }
    .sub-comments-enter,.sub-comments-leave-to {
        max-height:0 ;
    }
    .sub-comments-enter-to,.sub-comments-leave {
        max-height: 4rem ;
    }

用动画试试看:

<transition name="router-slid">
   //需要动画的内容
</transition>

css:

.router-slid-enter-active, .router-slid-leave-active {
  transition: all .4s;
}
.router-slid-enter, .router-slid-leave-active {
  transform: translate3d(0, 3rem, 0);
  opacity: 0;
}

可以参考这个,基本就是这样,你稍微调下样式就可以了

<template>
    <div class="panel">
        <!-- body -->
        <transition name="panel-fade" 
            @enter="enter"
            @before-leave="beforeLeave"
            @leave="leave"
        >
            <div class="panel__body" v-show="ifShowBody">
                <slot></slot>
            </div>
        </transition>
        <!-- footer -->
        <div class="panel__footer" >
            <slot name="header">更多</slot>
            <i v-if="showIcon" class="panel__header__icon" :class="{[`arrow--${iconClass}`]: true}" @click="iconClick"></i>
        </div>

    </div>
</template>
<script>
import velocity from 'velocity-animate'

export default {
    name: "ezy-panel",
    props: {
        showIcon: {
            type: Boolean,
            default: true
        }
    },
    data() {
        return {
            iconClass: 'up',
            bodyHeight: 0
        }
    },
    computed: {
        ifShowBody() {
            var c = true;
            switch (this.iconClass) {
                case 'up':
                    c = true
                    break;
                case 'down':
                    c = false
                    break;
            }
            return c
        }
    },
    methods: {
        iconClick() {
            var self = this;
            switch (this.iconClass) {
                case 'up':
                    this.iconClass = 'down';

                    break;
                case 'down':
                    this.iconClass = 'up';

                    break;
            }
        },
        enter(el, done){
            var self = this;
            velocity(el, { height: self.bodyHeight + 'px' }, { duration: 500 , complete: done})
        },
        beforeLeave(el,done){
            this.bodyHeight = el.clientHeight;
        },
        leave(el, done) {
            el.style.height = el.clientHeight + 'px';
            velocity(el, { height: '0px' }, { duration: 500 , complete: done})
        }
    }
}

用的时候,直接写主体部分可以,尾部的展开收缩,在组件里有了。

<panel>
/* 直接写主体部分
 *  <div>...</div>
 */
</panel>

给你个demo吧

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .box{
            height:500px;
            background-color:black;  
             overflow: hidden;                       
        }
        .mybox-leave-active,.mybox-enter-active{
            transition:  all 1s ease; 
        }
        .mybox-leave-active,.mybox-enter{
            height:0px !important;
        }
        .mybox-leave,.mybox-enter-active{
            height: 500px;
        }
    </style>
</head>
<body>
<div id="box">
    <transition name="mybox">
        <div class="box"  v-show="boxshow"></div>
    </transition>
    <button @click="togglebox">按钮</button>
</div>
</body>
<script src="../bower_components/vue/dist/vue.js"></script>
<script>
    new Vue({
        el:'#box',
        data:{
            boxshow:false
        },
        methods:{
           
            togglebox:function(){
                this.boxshow = !this.boxshow;
            }
        }      
    });
</script>
</html>

变量控制状态:

外部控制height,overflow:hidden
内部控制一个transform:translateY

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
你知道吗?

宣传栏