Vue + TypeScript + ElementUI 自定义实现下拉动画效果

刚刚完成的项目中有很多默认不显示,点击下拉出现的功能

一般来讲这种功能纯CSS就可以实现,但是前提要知道高度。那如果内容的高度未知怎么办呢?

ElementUI 有个Collapse 折叠面板的组件 http://element-cn.eleme.io/#/zh-CN/component/collapse

那我可不可以只用下拉的那个效果呢,嗯哼?

我们来看看源码

在node_modules里面的找到element-ui

长这样,看着是不是很眼熟(^_−)☆

然后再找collapse组件

主要看collapse-item.vue这个文件

这里可以注意到一个很特别的组件,用法很像插槽,但是引进来的是个js文件

找到这个文件,看看里面到底是什么东东?呐!在这里

具体内容这里就不贴出来了,看到最下面你会发现,这是一个函数式组件,用render函数渲染出来的

其中引用了下面这个,对dom元素进行操作的方法

import { addClass, removeClass } from 'element-ui/src/utils/dom';

好,下面就开始动手了

建一个dom.ts文件,将dom.js中的内容复制过来,然后修改成下面的样子,其他用不到的方法可以删掉,给参数定义类型(注:这个是ts的写法)

import Vue from 'vue';
/* istanbul ignore next */
function trim(val: string) {
    return (val || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, '');
};
/* istanbul ignore next */
export function hasClass(el: any, cls: string) {
    if (!el || !cls) {return false;}
    if (cls.indexOf(' ') !== -1) {throw new Error('className should not contain space.');}
    if (el.classList) {
        return el.classList.contains(cls);
    } else {
        return (' ' + el.className + ' ').indexOf(' ' + cls + ' ') > -1;
    }
};

/* istanbul ignore next */
export function addClass(el: any, cls: string) {
    if (!el) {return;}
    let curClass = el.className;
    const classes = (cls || '').split(' ');

    for (let i = 0, j = classes.length; i < j; i++) {
        const clsName = classes[i];
        if (!clsName) {continue;}

        if (el.classList) {
            el.classList.add(clsName);
        } else if (!hasClass(el, clsName)) {
            curClass += ' ' + clsName;
        }
    }
    if (!el.classList) {
        el.className = curClass;
    }
};

/* istanbul ignore next */
export function removeClass(el: any, cls: string) {
    if (!el || !cls) {return;}
    const classes = cls.split(' ');
    let curClass = ' ' + el.className + ' ';

    for (let i = 0, j = classes.length; i < j; i++) {
        const clsName = classes[i];
        if (!clsName) {continue;}

        if (el.classList) {
            el.classList.remove(clsName);
        } else if (hasClass(el, clsName)) {
            curClass = curClass.replace(' ' + clsName + ' ', ' ');
        }
    }
    if (!el.classList) {
        el.className = trim(curClass);
    }
}

下面就是我们的关键了,函数式组件

同样新建一个PulldownTransition.ts的文件,将collapse-transition.js内容全部复制过来,然后修改

import { addClass, removeClass } from '@/utils/domUtil';

import Vue, { VNodeData } from 'vue';

const COMPONENT_CLASS = 'pulldown-transition'

export default Vue.extend({
    name: 'PulldownTransition',
    functional: true ,
    render(h, { children }) {
        const data: VNodeData = {
            on: {
                beforeEnter(el: any) {
                    addClass(el, COMPONENT_CLASS);
                    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: any) {
                    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';
                },
            
                afterEnter(el: any) {
                    // for safari: remove class then reset height is necessary
                    removeClass(el, COMPONENT_CLASS);
                    el.style.height = '';
                    el.style.overflow = el.dataset.oldOverflow;
                },
            
                beforeLeave(el: any) {
                    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: any) {
                    if (el.scrollHeight !== 0) {
                        // for safari: add class after set height, or it will jump to zero height suddenly, weired
                        addClass(el, COMPONENT_CLASS);
                        el.style.height = 0;
                        el.style.paddingTop = 0;
                        el.style.paddingBottom = 0;
                    }
                },
            
                afterLeave(el: any) {
                    removeClass(el, COMPONENT_CLASS);
                    el.style.height = '';
                    el.style.overflow = el.dataset.oldOverflow;
                    el.style.paddingTop = el.dataset.oldPaddingTop;
                    el.style.paddingBottom = el.dataset.oldPaddingBottom;
                }
            }
        };

        return h('transition', data, children);
    }
})

关键在于最后一行,render函数在js和ts中的不同写法

js

ts

到此,下拉的函数式组件已经封装完毕,使用的时候就像引用子组件一样就可以了

(ΘoΘ) 重要!重要!重要!要给内部的div设置css样式  transition 否则是没有动画效果的

<template>
  <div class="UserManager">
     <pulldown-transition>
        <div class="user_edit" v-if="showUserEdit === user.id">
            <Edit-User/>
         </div>
     </pulldown-transition>

  </div>
</template>

<script lang="ts">
import PulldownTransition from '@/components/functional/PulldownTransition'

import { Component, Prop, Vue, Watch } from 'vue-property-decorator'
import { Getter, Action,State,Mutation,namespace } from 'vuex-class'

@Component({
  name:'UserManager',
  components: { PulldownTransition },
})
export default class UserManager extends Vue {

  }

</script>

<style scoped>

.user_edit{
  transition: .5s;
}
</style>


 

猜你喜欢

转载自blog.csdn.net/caroline_Luoluo/article/details/81537767