vue利用javascript钩子函数实现动画无效

qngyun1029
  • 797

想封装一个简单的动画组件,用css实现了一遍,效果是可行的,如下:

<template>
    <div>
        <transition name="bar">
            <slot></slot>
        </transition>
    </div>
</template>

<style lang="stylus" scoped>
.bar-enter-active, .bar-leave-active{
    transition: all .3s;
}

.bar-leave-active{
    transform translateY(200px)
}

.bar-enter{
    transform translateY(200px)
}
</style>

因为很多需要动态计算,所以想改为javascript 钩子函数来实现,但是发现怎么尝试都没有效果:

<template>
    <div>
        <transition
            v-on:before-enter="beforeEnter"
            v-on:enter="enter"
            v-on:leave="leave"
        >
            <slot></slot>
        </transition>
    </div>
</template>
<script>
export default {
    name: 'animation',
    methods: {
        beforeEnter: function (el) {
            el.style.transform = 'translate-y(200px)'
        },
        // 此回调函数是可选项的设置
        // 与 CSS 结合时使用
        enter: function (el, done) {
            el.style.transitionDuration = "3s"
            el.style.transitionProperty = "all"
            done()
        },

        // 此回调函数是可选项的设置
        // 与 CSS 结合时使用
        leave: function (el, done) {
            el.style.transform = 'translate-y(200px)'
            // el.style.transition = 'all 3s'
            el.style.transitionDuration  = "3s"
            el.style.transitionProperty = "all"
            done()
        },
    }
}
</script>

问题出在哪儿呢?

回复
阅读 3.5k
3 个回答

题主可能没太理解 javascript钩子动画的用法。
你第一种是用 class类名切换通过css3自身实现的过渡动画,但是钩子函数实现动画是另一种概念,需要通过 自己用定时器实现,当然也可以配合css使用,这个官方有说明,下面简单写一个 opacity 过渡demo,
题主可以参考理解一下,如有疑问,欢迎再次讨论。

<template>
    <transition
        v-on:before-enter="beforeEnter"
        v-on:enter="enter"
        v-on:leave="leave"
    >
        <slot></slot>
    </transition>
</template>
<script>
export default {
    name: 'animation',
    data() {
        return {
            enterTimer: null,
            leaveTimer: null
        }
    },
    methods: {
        beforeEnter: function(el) {
            el.style.opacity = 0
            el.style.transform = 'translateX(0px)'
            el.style.transformOrigin = 'left'
        },
        // 此回调函数是可选项的设置
        // 与 CSS 结合时使用
        enter: function(el, done) {
            clearInterval(this.leaveTimer)
            let _v = 0
            this.enterTimer = setInterval(() => {
                if (_v < 1) {
                    _v += 0.01
                } else {
                    _v = 1
                    clearInterval(this.enterTimer)
                    done()
                }
                el.style.opacity = _v
                el.style.transform = `translateX(${_v * 100}px)`
            }, 30)
        },

        // 此回调函数是可选项的设置
        // 与 CSS 结合时使用
        leave: function(el, done) {
            clearInterval(this.enterTimer)
            let _v = 1
            this.leaveTimer = setInterval(() => {
                if (_v > 0) {
                    _v -= 0.01
                } else {
                    _v = 0
                    clearInterval(this.leaveTimer)
                    done()
                }
                el.style.opacity = _v
                el.style.transform = `translateX(${_v * 100}px)`
            }, 30)
        }
    }
}
</script>
enter: function (el, done) {
   this.$nextTick(()=>{
            el.style.transitionDuration = "3s"
            el.style.transitionProperty = "all"
            })
            done()
        },

这样试试

qngyun1029
  • 797

@donglegend 你好,谢谢,基本上可以,但是这种方式感觉有缺陷,如下:

//假设我想在1s中完成动画(滑动250px)
//那么我需要函数执行多少次呢?
//很显然,如果我想动画看上去越顺滑,那必须每次移动的像素就越小,所以函数执行越频繁;
//相反,如果函数间隔时间越大,动画会非常难看(一次移动的像素太多)

问题:假设我滑动的距离足够大(1000px),执行的时间足够短(0.3s),看上去动画要足够顺滑(每次移动5px),那么函数需要多长时间执行一次呢? 。0.3 / (1000 / 5) ,0.3s要执行200次函数,每 0.0015s 执行一次,这么频繁的执行函数到底有没有问题呢?如果每次只能移动1px呢?

再者就是这些计算麻烦,不如css3动画来得方便。

我用css实现的动画和js钩子实现的作比较,都是0.3s,总感觉css的动画要快,要顺滑。

<template>
    <transition
        v-on:before-enter="beforeEnter"
        v-on:enter="enter"
        v-on:leave="leave"
    >
        <slot></slot>
    </transition>
</template>
<script>
export default {
    name: 'animation',
    data() {
        return {
            enterTimer: null,
            leaveTimer: null
        }
    },
    methods: {
        beforeEnter: function(el) {
            //设置滑块的初始位置(滑块的宽度)
            el.style.bottom = '-250px'
        },
        enter: function(el, done) {
            clearInterval(this.leaveTimer)
            let _v = 0
            //假设我想在1s中完成动画(滑动250px)
            //那么我需要函数执行多少次呢?
            //很显然,如果我想动画看上去越顺滑,那必须每次移动的像素就越小,所以函数执行越频繁;
            //相反,如果函数间隔时间越大,动画会非常难看(一次移动的像素太多)
            this.enterTimer = setInterval(() => {
                if (_v < 250) {
                    _v += 2.5
                } else {
                    _v = 250
                    clearInterval(this.enterTimer)
                    done()
                }
                el.style.opacity = _v
                el.style.transform = `translateY(-${_v}px)`
            }, 3)
        },
        leave: function(el, done) {
            clearInterval(this.enterTimer)
            let _v = 250
            this.leaveTimer = setInterval(() => {
                if (_v > 0) {
                    _v -= 2.5
                } else {
                    _v = 0
                    clearInterval(this.leaveTimer)
                    done()
                }
                el.style.opacity = _v
                el.style.transform = `translateY(-${_v}px)`
            }, 3)
        }
    }
}
</script>

之前一直以为官方价绍了两种方式是等价的,并且两种方式对应的demo不是等效的,感觉这是巨大的一个坑

宣传栏