使用 Transition 在 Vuejs 中的 v-if 上设置动画高度
Animate Height on v-if in Vuejs using Transition
我正在使用以下代码通过将高度从 0 像素降低来为 v-if 元素设置动画。动画效果很好。但问题是我必须指定元素的初始高度为 CSS。对于一个元素,这没问题,但我想将此动画应用于多个元素。我怎样才能解决这个问题?这样无论高度如何,动画效果都很好!
<transition name="fadeHeight" mode="out-in">
<div v-if="something">
<p>something over here where the height is not constant</p>
</div>
</transition>
.fadeHeight-enter-active,
.fadeHeight-leave-active {
transition: all 0.2s;
height: 230px;
}
.fadeHeight-enter,
.fadeHeight-leave-to
{
opacity: 0;
height: 0px;
}
您似乎没有发布所有代码,但希望我理解了目标。
尝试将过渡移动到 max-height
属性:
.fadeHeight-enter-active,
.fadeHeight-leave-active {
transition: all 0.2s;
max-height: 230px;
}
.fadeHeight-enter,
.fadeHeight-leave-to
{
opacity: 0;
max-height: 0px;
}
只要您将最大高度设置为大于最高元素,它应该可以满足您的需要。请注意,您可能还想使用 overflow:hidden
。如果元素的实际高度有显着变化,这个解决方案可能不是最好的,因为它会使动画 duration/delay 看起来非常不同。
@ryantdecker 提供了最常见的答案。不过,我更喜欢做更少的代码,而是做 class binding:
<template>
<!-- isShowing either a data or computed... -->
<div class="foo" :class="{'showing': isShowing', 'hidden': !isShowing}">
<p>
something over here where the height is not constant
</p>
</div>
</template>
...
<style>
.foo {
height: auto;
transition: max-height 0.5s;
&.showing {
max-height: 200px; //MUST BE MORE THAN height:auto
}
&.hidden {
max-height: 0px;
}
}
</style>
可以进行一些自定义以获得更多控制:
- 设置
:style="{'max-height': computedHeight}"
- 在
.showing
和 .hidden
class 中分别使用 ease-in
和 ease-out
以及两个不同的 transitions
。
- 对 collapse/expand
的超长内容使用三次贝塞尔曲线过渡速度
当您使用不同的项目时,可以使用上面的第一个自定义项,例如图片、弹性行,其中可以通过开发工具查看高度并计算高度。例如:
computed: {
/**
* @return {string} max height of the container in pixels if shown else zero
*/
calcedHeight()
{
const elHeight = 80;
const maxHeight = this.isShowing ? elHeight * this.elementCount : 0
const maxHeightPx = maxHeight + 'px'
return {
'max-height': maxHeightPx
}
}
}
此时可以很容易地将其制作成具有 isShowing
、elHeight
和 elCount
属性的组件。
三次贝塞尔曲线
我将其作为自己的部分,因为对于疯狂的长元素(想想 5000 像素 max-heights
),它可能是所有需要的部分):
&.showing {
transition: all 0.6s cubic-bezier(1, 0.01, 1, 0.01);
}
&.hidden {
transition: all 0.6s cubic-bezier(0.01, 1, 0.01, 1);
}
如前所述 - 最大高度过渡通常是解决此问题的方法。
但在某些情况下,您可能无法使用最大高度过渡。
对于这些情况,您可以使用包装器容器组件,它会在需要时进行转换。
<template>
<div
class="fluid-wrapper"
:class="{ 'in-transition': transitionState }"
:style="computedDimensions"
@transitionend="transitionState = 0"
>
<slot />
</div>
</template>
<script>
export default {
name: 'FluidContainer',
props: ['trigger'],
data() {
return {
oldRect: {
height: null,
width: null,
},
newRect: {
height: null,
width: null,
},
transitionState: 0,
// 0: no Dimensions, no transition
// 1: oldRect Dimensions, transition is on
// 2: newRect Dimensions, transition is on
};
},
computed: {
computedDimensions() {
if (!this.transitionState) {
return null;
}
return this.transitionState === 1 ? this.oldRect : this.newRect;
},
dimensionsHasChanged() {
return (
this.newRect.height !== this.oldRect.height
|| this.newRect.width !== this.oldRect.width
);
},
},
watch: {
trigger() {
const oldStyle = getComputedStyle(this.$el);
this.oldRect.height = oldStyle.height;
this.oldRect.width = oldStyle.width;
this.$nextTick(() => {
const newStyle = getComputedStyle(this.$el);
this.newRect.height = newStyle.height;
this.newRect.width = newStyle.width;
if (this.dimensionsHasChanged) {
this.transitionState = 1;
window.requestAnimationFrame(() => {
this.transitionState = 2;
});
} else {
this.transitionState = 0;
}
});
},
},
};
</script>
<style lang="scss" scoped>
.fluid-wrapper {
/* overflow: hidden; */
height: fit-content;
width: fit-content;
&.in-transition {
transition: all 0.3s;
}
}
</style>
用法:
<FluidContainer :trigger="some-variable">
<!-- Any Reactive Content -->
</FluidContainer>
'trigger' 道具 - 这是工作所必需的。
它可以是使内部内容发生变化的任何状态。
包装器将监视触发器以检测尺寸何时发生变化并进行转换。
我正在使用以下代码通过将高度从 0 像素降低来为 v-if 元素设置动画。动画效果很好。但问题是我必须指定元素的初始高度为 CSS。对于一个元素,这没问题,但我想将此动画应用于多个元素。我怎样才能解决这个问题?这样无论高度如何,动画效果都很好!
<transition name="fadeHeight" mode="out-in">
<div v-if="something">
<p>something over here where the height is not constant</p>
</div>
</transition>
.fadeHeight-enter-active,
.fadeHeight-leave-active {
transition: all 0.2s;
height: 230px;
}
.fadeHeight-enter,
.fadeHeight-leave-to
{
opacity: 0;
height: 0px;
}
您似乎没有发布所有代码,但希望我理解了目标。
尝试将过渡移动到 max-height
属性:
.fadeHeight-enter-active,
.fadeHeight-leave-active {
transition: all 0.2s;
max-height: 230px;
}
.fadeHeight-enter,
.fadeHeight-leave-to
{
opacity: 0;
max-height: 0px;
}
只要您将最大高度设置为大于最高元素,它应该可以满足您的需要。请注意,您可能还想使用 overflow:hidden
。如果元素的实际高度有显着变化,这个解决方案可能不是最好的,因为它会使动画 duration/delay 看起来非常不同。
@ryantdecker 提供了最常见的答案。不过,我更喜欢做更少的代码,而是做 class binding:
<template>
<!-- isShowing either a data or computed... -->
<div class="foo" :class="{'showing': isShowing', 'hidden': !isShowing}">
<p>
something over here where the height is not constant
</p>
</div>
</template>
...
<style>
.foo {
height: auto;
transition: max-height 0.5s;
&.showing {
max-height: 200px; //MUST BE MORE THAN height:auto
}
&.hidden {
max-height: 0px;
}
}
</style>
可以进行一些自定义以获得更多控制:
- 设置
:style="{'max-height': computedHeight}"
- 在
.showing
和.hidden
class 中分别使用ease-in
和ease-out
以及两个不同的transitions
。 - 对 collapse/expand 的超长内容使用三次贝塞尔曲线过渡速度
当您使用不同的项目时,可以使用上面的第一个自定义项,例如图片、弹性行,其中可以通过开发工具查看高度并计算高度。例如:
computed: {
/**
* @return {string} max height of the container in pixels if shown else zero
*/
calcedHeight()
{
const elHeight = 80;
const maxHeight = this.isShowing ? elHeight * this.elementCount : 0
const maxHeightPx = maxHeight + 'px'
return {
'max-height': maxHeightPx
}
}
}
此时可以很容易地将其制作成具有 isShowing
、elHeight
和 elCount
属性的组件。
三次贝塞尔曲线
我将其作为自己的部分,因为对于疯狂的长元素(想想 5000 像素 max-heights
),它可能是所有需要的部分):
&.showing {
transition: all 0.6s cubic-bezier(1, 0.01, 1, 0.01);
}
&.hidden {
transition: all 0.6s cubic-bezier(0.01, 1, 0.01, 1);
}
如前所述 - 最大高度过渡通常是解决此问题的方法。 但在某些情况下,您可能无法使用最大高度过渡。 对于这些情况,您可以使用包装器容器组件,它会在需要时进行转换。
<template>
<div
class="fluid-wrapper"
:class="{ 'in-transition': transitionState }"
:style="computedDimensions"
@transitionend="transitionState = 0"
>
<slot />
</div>
</template>
<script>
export default {
name: 'FluidContainer',
props: ['trigger'],
data() {
return {
oldRect: {
height: null,
width: null,
},
newRect: {
height: null,
width: null,
},
transitionState: 0,
// 0: no Dimensions, no transition
// 1: oldRect Dimensions, transition is on
// 2: newRect Dimensions, transition is on
};
},
computed: {
computedDimensions() {
if (!this.transitionState) {
return null;
}
return this.transitionState === 1 ? this.oldRect : this.newRect;
},
dimensionsHasChanged() {
return (
this.newRect.height !== this.oldRect.height
|| this.newRect.width !== this.oldRect.width
);
},
},
watch: {
trigger() {
const oldStyle = getComputedStyle(this.$el);
this.oldRect.height = oldStyle.height;
this.oldRect.width = oldStyle.width;
this.$nextTick(() => {
const newStyle = getComputedStyle(this.$el);
this.newRect.height = newStyle.height;
this.newRect.width = newStyle.width;
if (this.dimensionsHasChanged) {
this.transitionState = 1;
window.requestAnimationFrame(() => {
this.transitionState = 2;
});
} else {
this.transitionState = 0;
}
});
},
},
};
</script>
<style lang="scss" scoped>
.fluid-wrapper {
/* overflow: hidden; */
height: fit-content;
width: fit-content;
&.in-transition {
transition: all 0.3s;
}
}
</style>
用法:
<FluidContainer :trigger="some-variable">
<!-- Any Reactive Content -->
</FluidContainer>
'trigger' 道具 - 这是工作所必需的。 它可以是使内部内容发生变化的任何状态。 包装器将监视触发器以检测尺寸何时发生变化并进行转换。