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

图片描述

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

阅读 29k
评论
    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>

      给你个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>

        用动画试试看:

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

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

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

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

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

                  变量控制状态:

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

                    撰写回答

                    登录后参与交流、获取后续更新提醒

                    相似问题
                    推荐文章