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

图片描述

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

阅读 26.1k
评论
    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;
        }

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

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

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

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

                    撰写回答

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

                    相似问题
                    推荐文章