1

我有一组元素显示/隐藏/重新排列过渡组,没什么特别的。

.cross-fade-leave-active {
    transition: transform $fadeSpeed ease-in-out, opacity $fadeSpeed ease-in-out;
    position: absolute;
}
.cross-fade-enter-active {
    transition: opacity $fadeSpeed ease-in-out $fadeSpeed;
}
.cross-fade-enter-from,
.cross-fade-leave-to {
    opacity: 0;
}  
.cross-fade-move{
    transition: transform $fadeSpeed ease-in $fadeSpeed, opacity $fadeSpeed ease-in-out;
}

这很好用,除非在动态高度父容器是元素流的一部分的情况下。父级立即捕捉到 FLIP 动画的最后阶段,而过渡组的内容则顺利完成它们的工作。

虽然从功能上讲它为什么会发生是有道理的,但它远非理想。

有没有一种直接的方法可以连接到 Vue FLIP 动画以获取父级的 First 和 Last 属性,这样我就可以设置过渡的最大高度?


我的尝试

<div :ref="`container${i}`" :style="{'max-height': containers[`container${i}`]}">
    <transition-group name="cross-fade"
    @after-enter="clearMaxHeight(`container${i}`)" 
    @after-leave="clearMaxHeight(`container${i}`)" 
    @before-enter="maxHeight(`container${i}`)" 
    @enter="maxHeight(`container${i}`)" 
    @before-leave="maxHeight(`container${i}`)" 
    @leave="maxHeight(`container${i}`)"
    >
    //...conditional elements
    </transition-group>
</div>

maxHeight(ref){
    let container = this.$refs[ref];            
    this.containers[ref] = container.clientHeight + 'px';
},
clearMaxHeight(ref){
    this.containers[ref] = 'none';
},

从理论上讲,我认为before-enter或者before-leave会在转换之前捕获元素的高度,这将锁定父级的第一个高度。然后enterleave将在元素从流中添加/删除后立即捕获新元素,并给出 Last 高度。最后,当动画完成后,只需将最大高度设置回无,这样它就可以正常运行。

但这不是那样的。即使过渡时间增加到 5 秒,我也看到 max-height 在一瞬间被设置为一个数字,然后立即回到“无”。也许我误解了 Vue 动画钩子的生命周期,但是文档在确切的执行上似乎有点稀疏。

4

1 回答 1

0

免责声明

这有点小错误,但我认为过渡组(或尝试并未能做到这一点的未记录功能)实际上可能存在问题。基本上, transition-group 容器之外的一些元素肯定会-move通过 Vue 中的某些东西添加到它们的变换矩阵和类,这很烦人。

但是,这基本上完成了我最初打算做的事情,也许你可以改进它。或者也许我会深入研究 Vue3 源代码并强制它运行。


设置:

    const firstAni = {};
    const aniContainer = {};
    
    const aniFirst = (i) => {
        let container = aniContainer[i];
        firstAni[i] = container.offsetHeight;
    };
    const aniLast = (i) => {
        let container = aniContainer[i];
        let lastAni = container.offsetHeight;

        const aniClear = (e) => {
            if(e.target !== container){
                return false;
            }
            container.removeEventListener('transitionend', aniClear);
            container.removeEventListener('transitioncancel', aniClear);
            container.style.height = 'auto';
        };

        container.style.height = `${firstAni[i]}px`;
        
        container.addEventListener('transitionend', aniClear);
        container.addEventListener('transitioncancel', aniClear);

        nextTick(() => {
            container.style.height = `${lastAni}px`;
        });
    };
    const setAniRef = (el, i) => {
        aniContainer[i] = el;
    };

模板:

<div class="cross-fade-wrapper" :ref="(el) => setAniRef(el, i)">
    <transition-group name="cross-fade"
        @before-enter="(el) => aniFirst(i, el)"
        @before-leave="(el) => aniFirst(i, el)"
        @enter="(el, done) => aniLast(i, el, done)"
        @leave="(el, done) => aniLast(i, el, done)"
    >
    <!-- all the conditional elements -->
    </transition-group>
</div>
        

风格化:

$fadeSpeed: 0.1s;
.cross-fade-wrapper{
    position: relative;
    transition: height $fadeSpeed ease-out;
}
.cross-fade-leave-active {
    transition: opacity $fadeSpeed / 2 ease-out;
    position: absolute;
}
.cross-fade-enter-active {
    transition: opacity $fadeSpeed / 2 ease-in $fadeSpeed * 0.8;
}
.cross-fade-enter-from,
.cross-fade-leave-to {
    opacity: 0;
}  
.cross-fade-move{
    transition: transform $fadeSpeed ease-out;
}

基本上每个FLIP,排序:

  • 设置对容器的引用。
  • 在进入/离开发生之前获取容器的高度(首先)。
  • 在进入/离开开始时(Last)立即再次获取容器的高度,然后将容器的高度设置为 First 值。
  • 下一个刻度,将容器的高度设置为 Last 值。
  • 让动画做它的事。
  • 动画完成后(通过侦听器观看)将容器高度设置回auto.
于 2022-02-23T03:38:28.943 回答