我该如何解决这个跳跃过渡?
How can I fix this jumping transition?
我正在尝试构建某种淡入淡出滑块,但在转换之间出现奇怪的跳跃,已经尝试了 的解决方案,但它在我的情况下不起作用。我不明白为什么会这样,为了清楚起见,请看一下这个 gif
如果可能的话,我想知道是哪个部分造成的,以及我该如何解决。任何形式的帮助将不胜感激。请看看我的脚本..提前致谢!
<template>
<header class="hero">
<div class="container">
<div class="textbox">
<transition name="fade" v-for="(slide, i) in slides" :key="slide">
<h1 class="punchline" v-if="current == i">{{ slide }}</h1>
</transition>
<NuxtLink class="link" to="/">
<div class="icon-wrapper">
<Icon icon="chevron-right" />
</div>
</NuxtLink>
</div>
<div class="indicator">
<span
v-for="(slide, i) in slides"
:key="slide"
:class="{ active: current == i }"
class="item"
@click="animateTo(i)"
/>
</div>
</div>
<Vector :class="color" class="stick-overlay stick-top" file="model-stick" />
<Vector
:class="color"
class="stick-overlay stick-bottom"
file="model-stick"
/>
</header>
</template>
<script>
export default {
data() {
return {
current: 0,
slides: ['Slide 001', 'Slide 002', 'Slide 003']
}
},
computed: {
color() {
if (this.current == 1) return 'blue'
if (this.current == 2) return 'purple'
return 'default'
}
},
methods: {
animateTo(index) {
this.current = index
}
}
}
</script>
<style lang="scss" scoped>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.hero {
@apply flex;
@apply justify-center;
@apply items-center;
@apply text-white;
@apply relative;
height: 810px;
background: url('/images/img-hero.png');
background-position: center;
background-size: cover;
background-repeat: no-repeat;
.textbox {
@apply relative;
@apply text-center;
@apply mb-20;
.punchline {
@apply text-3xl;
@apply font-semibold;
@apply leading-relaxed;
@apply mb-10;
}
.link {
@apply flex;
@apply justify-center;
.icon-wrapper {
@apply h-16 w-16;
@apply border;
@apply border-white;
@apply flex;
@apply justify-center;
@apply items-center;
@apply rounded-full;
}
}
}
.indicator {
@apply flex;
@apply justify-between;
@apply items-center;
@apply px-20;
.item {
@apply inline-block;
@apply h-2 w-2;
@apply border;
@apply border-white;
@apply rounded-full;
@apply transition-colors;
@apply duration-300;
&.active {
@apply bg-white;
}
}
}
.stick-overlay {
@apply absolute;
@apply z-0;
&.stick-top {
top: -75%;
left: -75%;
transform: scale(0.6) rotate(180deg);
}
&.stick-bottom {
@apply block;
bottom: -80%;
left: -5%;
transform: scale(0.6);
}
&::v-deep svg path {
@apply transition-all;
@apply duration-1000;
}
&.default ::v-deep svg path {
fill: rgba(0, 40, 255, 0.5);
}
&.blue ::v-deep svg path {
fill: rgba(33, 167, 252, 0.3);
}
&.purple ::v-deep svg path {
fill: rgba(119, 41, 251, 0.3);
}
}
}
</style>
如果这是带有 Vue 路由器的 Vue3,如果你想为每个页面转换,请确保将其用作主路由器视图:
<router-view v-slot="{ Component }">
<transition>
<component :is="Component"></component>
</transition>
</router-view>
对于这些,我使用这个 CSS [编辑:我看到你已经有了这些]:
.v-enter-active {
transition: opacity 0.8s;
}
.v-enter-from {
opacity: 0;
}
跳跃通常发生在一个元素离开(淡出-离开-活跃)和一个进入时,所以它有助于在两个方向上隐藏它们中的每一个,像这样:
.fade-leave-active,
.fade-enter-active {
transition: opacity 0.8s;
}
.fade-leave-to,
.fade-enter-from {
opacity: 0;
}
它有助于使 v-for 等更繁重的逻辑远离过渡元素,它可能会导致意外的内容移动。我尝试尽可能地将事情分开,并且 运行 在过渡的外部或内部循环。它不像对页面上的所有内容都使用一个过渡那么干净,但根据我的经验,为了避免您遇到的问题类型,这在某种程度上是必要的。
<transition name="fade">
<top-notification class="error" v-if="isError && topNotification">{{ topNotification }}</top-notification>
</transition>
<transition name="fade">
<top-notification v-if="!isError && topNotification">{{ topNotification }}</top-notification>
</transition>
最后,如果您使用 Vuex,则必须进行一些试验以使您的状态尽可能干净,并避免在页面上发生逻辑时瞬间引入双状态。例如,如果 v-if 有多个条件,那么在其中一个变为假之前,两个条件同时为真。这可能会导致元素停留一小会儿而不是直接消失。因此,请保持您的 Vue 检查器面板打开并监视状态。
好的,我知道了!
抱歉,我找不到 Nuxt + SCSS + Tailwind 工作的工作 codesandbox,所以我几乎在我自己的 Nuxt 项目中做了一个页面,这意味着我必须更新一些样式,因为我没有所有默认的 Tailwind 属性我的配置文件。
如果您愿意,可以随意放弃所有样式,因为错误不是来自样式。这是您问题的完整代码的实际答案(减去我没有给出的组件)。
<template>
<header class="hero">
<div class="container">
<div class="textbox">
<transition name="fade">
<h1 class="punchline">{{ slides[current] }}</h1> <!-- this is working ! -->
</transition>
<NuxtLink class="link" to="/">
<div class="icon-wrapper">
tastyicon
<!-- <Icon icon="chevron-right" /> -->
</div>
</NuxtLink>
</div>
<div class="indicator">
<span
v-for="(slide, i) in slides"
:key="slide"
:class="{ active: current == i }"
class="item"
@click="animateTo(i)"
/>
</div>
</div>
<!-- <Vector :class="color" class="stick-overlay stick-top" file="model-stick" /> -->
<!-- <Vector :class="color" class="stick-overlay stick-bottom" file="model-stick" /> -->
</header>
</template>
<script>
export default {
data() {
return {
current: 0,
slides: ['Slide 001', 'Slide 002', 'Slide 003'],
}
},
computed: {
color() {
if (this.current === 1) return 'blue'
if (this.current === 2) return 'purple'
return 'default'
},
},
methods: {
animateTo(index) {
this.current = index
},
},
}
</script>
<style lang="scss" scoped>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.hero {
@apply flex justify-center items-center text-white relative;
background-position: center;
background-repeat: no-repeat;
background-size: cover;
height: 810px;
background: url('https://images.unsplash.com/photo-1612712779378-58eb8b588761?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1650&q=80');
.textbox {
@apply relative text-center mb-20;
.punchline {
@apply text-3xl font-semibold leading-relaxed mb-8;
}
.link {
@apply flex justify-center;
.icon-wrapper {
@apply h-16 w-16 border border-white flex justify-center items-center rounded-full;
}
}
}
.indicator {
@apply flex justify-between items-center px-20;
.item {
@apply inline-block h-2 w-2 border border-white rounded-full transition-colors duration-300;
&.active {
@apply bg-white;
}
}
}
.stick-overlay {
@apply absolute z-0;
&.stick-top {
left: -75%;
top: -75%;
transform: scale(0.6) rotate(180deg);
}
&.stick-bottom {
@apply block;
bottom: -80%;
left: -5%;
transform: scale(0.6);
}
&::v-deep svg path {
@apply transition-all duration-1000;
}
&.default ::v-deep svg path {
fill: rgba(0, 40, 255, 0.5);
}
&.blue ::v-deep svg path {
fill: rgba(33, 167, 252, 0.3);
}
&.purple ::v-deep svg path {
fill: rgba(119, 41, 251, 0.3);
}
}
}
</style>
此处的问题是您如何处理根据其状态显示在视图上的实际选定元素。你真的不应该在 for 循环中依赖 index
,因为它会产生意想不到的行为。
v-if="current == i"
让您了解了这一点。尝试使用索引以外的其他元素显示元素,这些元素实际上可以“移动”并且 DOM 将删除它们,然后以糟糕的方式将它们带回,因此您的问题中出现了丑陋的过渡。
因为你有current
,所以直接使用它就像slides[current]
!
PS: 不确定你的项目是哪种配置,但你完全可以像我一样写你的样式(在@apply
的一行)。或者直接进入你的 template
但我猜你不喜欢这样。
我终于通过将 <h1>
元素包装在 <template>
标签内并对其进行循环而不是直接从 <transition>
标签进行循环来修复它。注意我在<transition>
标签里也加了mode="out-in"
,不然还是跳
<transition name="fade" mode="out-in">
<template v-for="(slide, i) in slides">
<h1 class="punchline" :key="slide" v-if="current == i">
{{ slide }}
</h1>
</template>
</transition>
跳转的原因是因为当我检查元素时,新内容已加载,而旧元素仍然存在,因此它在完全删除之前只是呈现在旧元素下方。不确定为什么会发生这种情况,但可能是因为 <transition>
不应该循环。
<transition-group>
像这样:
<transition-group name="fade" mode="out-in">
<h1
class="punchline"
v-for="(slide, i) in slides"
:key="slide"
v-show="current == i">
{{ slide }}
</h1>
</transition-group>
也不适用于此条件,因为它会产生相同的行为。 (或者我用错了?请告诉我)
我正在尝试构建某种淡入淡出滑块,但在转换之间出现奇怪的跳跃,已经尝试了
如果可能的话,我想知道是哪个部分造成的,以及我该如何解决。任何形式的帮助将不胜感激。请看看我的脚本..提前致谢!
<template>
<header class="hero">
<div class="container">
<div class="textbox">
<transition name="fade" v-for="(slide, i) in slides" :key="slide">
<h1 class="punchline" v-if="current == i">{{ slide }}</h1>
</transition>
<NuxtLink class="link" to="/">
<div class="icon-wrapper">
<Icon icon="chevron-right" />
</div>
</NuxtLink>
</div>
<div class="indicator">
<span
v-for="(slide, i) in slides"
:key="slide"
:class="{ active: current == i }"
class="item"
@click="animateTo(i)"
/>
</div>
</div>
<Vector :class="color" class="stick-overlay stick-top" file="model-stick" />
<Vector
:class="color"
class="stick-overlay stick-bottom"
file="model-stick"
/>
</header>
</template>
<script>
export default {
data() {
return {
current: 0,
slides: ['Slide 001', 'Slide 002', 'Slide 003']
}
},
computed: {
color() {
if (this.current == 1) return 'blue'
if (this.current == 2) return 'purple'
return 'default'
}
},
methods: {
animateTo(index) {
this.current = index
}
}
}
</script>
<style lang="scss" scoped>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.hero {
@apply flex;
@apply justify-center;
@apply items-center;
@apply text-white;
@apply relative;
height: 810px;
background: url('/images/img-hero.png');
background-position: center;
background-size: cover;
background-repeat: no-repeat;
.textbox {
@apply relative;
@apply text-center;
@apply mb-20;
.punchline {
@apply text-3xl;
@apply font-semibold;
@apply leading-relaxed;
@apply mb-10;
}
.link {
@apply flex;
@apply justify-center;
.icon-wrapper {
@apply h-16 w-16;
@apply border;
@apply border-white;
@apply flex;
@apply justify-center;
@apply items-center;
@apply rounded-full;
}
}
}
.indicator {
@apply flex;
@apply justify-between;
@apply items-center;
@apply px-20;
.item {
@apply inline-block;
@apply h-2 w-2;
@apply border;
@apply border-white;
@apply rounded-full;
@apply transition-colors;
@apply duration-300;
&.active {
@apply bg-white;
}
}
}
.stick-overlay {
@apply absolute;
@apply z-0;
&.stick-top {
top: -75%;
left: -75%;
transform: scale(0.6) rotate(180deg);
}
&.stick-bottom {
@apply block;
bottom: -80%;
left: -5%;
transform: scale(0.6);
}
&::v-deep svg path {
@apply transition-all;
@apply duration-1000;
}
&.default ::v-deep svg path {
fill: rgba(0, 40, 255, 0.5);
}
&.blue ::v-deep svg path {
fill: rgba(33, 167, 252, 0.3);
}
&.purple ::v-deep svg path {
fill: rgba(119, 41, 251, 0.3);
}
}
}
</style>
如果这是带有 Vue 路由器的 Vue3,如果你想为每个页面转换,请确保将其用作主路由器视图:
<router-view v-slot="{ Component }">
<transition>
<component :is="Component"></component>
</transition>
</router-view>
对于这些,我使用这个 CSS [编辑:我看到你已经有了这些]:
.v-enter-active {
transition: opacity 0.8s;
}
.v-enter-from {
opacity: 0;
}
跳跃通常发生在一个元素离开(淡出-离开-活跃)和一个进入时,所以它有助于在两个方向上隐藏它们中的每一个,像这样:
.fade-leave-active,
.fade-enter-active {
transition: opacity 0.8s;
}
.fade-leave-to,
.fade-enter-from {
opacity: 0;
}
它有助于使 v-for 等更繁重的逻辑远离过渡元素,它可能会导致意外的内容移动。我尝试尽可能地将事情分开,并且 运行 在过渡的外部或内部循环。它不像对页面上的所有内容都使用一个过渡那么干净,但根据我的经验,为了避免您遇到的问题类型,这在某种程度上是必要的。
<transition name="fade">
<top-notification class="error" v-if="isError && topNotification">{{ topNotification }}</top-notification>
</transition>
<transition name="fade">
<top-notification v-if="!isError && topNotification">{{ topNotification }}</top-notification>
</transition>
最后,如果您使用 Vuex,则必须进行一些试验以使您的状态尽可能干净,并避免在页面上发生逻辑时瞬间引入双状态。例如,如果 v-if 有多个条件,那么在其中一个变为假之前,两个条件同时为真。这可能会导致元素停留一小会儿而不是直接消失。因此,请保持您的 Vue 检查器面板打开并监视状态。
好的,我知道了!
抱歉,我找不到 Nuxt + SCSS + Tailwind 工作的工作 codesandbox,所以我几乎在我自己的 Nuxt 项目中做了一个页面,这意味着我必须更新一些样式,因为我没有所有默认的 Tailwind 属性我的配置文件。
如果您愿意,可以随意放弃所有样式,因为错误不是来自样式。这是您问题的完整代码的实际答案(减去我没有给出的组件)。
<template>
<header class="hero">
<div class="container">
<div class="textbox">
<transition name="fade">
<h1 class="punchline">{{ slides[current] }}</h1> <!-- this is working ! -->
</transition>
<NuxtLink class="link" to="/">
<div class="icon-wrapper">
tastyicon
<!-- <Icon icon="chevron-right" /> -->
</div>
</NuxtLink>
</div>
<div class="indicator">
<span
v-for="(slide, i) in slides"
:key="slide"
:class="{ active: current == i }"
class="item"
@click="animateTo(i)"
/>
</div>
</div>
<!-- <Vector :class="color" class="stick-overlay stick-top" file="model-stick" /> -->
<!-- <Vector :class="color" class="stick-overlay stick-bottom" file="model-stick" /> -->
</header>
</template>
<script>
export default {
data() {
return {
current: 0,
slides: ['Slide 001', 'Slide 002', 'Slide 003'],
}
},
computed: {
color() {
if (this.current === 1) return 'blue'
if (this.current === 2) return 'purple'
return 'default'
},
},
methods: {
animateTo(index) {
this.current = index
},
},
}
</script>
<style lang="scss" scoped>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.hero {
@apply flex justify-center items-center text-white relative;
background-position: center;
background-repeat: no-repeat;
background-size: cover;
height: 810px;
background: url('https://images.unsplash.com/photo-1612712779378-58eb8b588761?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1650&q=80');
.textbox {
@apply relative text-center mb-20;
.punchline {
@apply text-3xl font-semibold leading-relaxed mb-8;
}
.link {
@apply flex justify-center;
.icon-wrapper {
@apply h-16 w-16 border border-white flex justify-center items-center rounded-full;
}
}
}
.indicator {
@apply flex justify-between items-center px-20;
.item {
@apply inline-block h-2 w-2 border border-white rounded-full transition-colors duration-300;
&.active {
@apply bg-white;
}
}
}
.stick-overlay {
@apply absolute z-0;
&.stick-top {
left: -75%;
top: -75%;
transform: scale(0.6) rotate(180deg);
}
&.stick-bottom {
@apply block;
bottom: -80%;
left: -5%;
transform: scale(0.6);
}
&::v-deep svg path {
@apply transition-all duration-1000;
}
&.default ::v-deep svg path {
fill: rgba(0, 40, 255, 0.5);
}
&.blue ::v-deep svg path {
fill: rgba(33, 167, 252, 0.3);
}
&.purple ::v-deep svg path {
fill: rgba(119, 41, 251, 0.3);
}
}
}
</style>
此处的问题是您如何处理根据其状态显示在视图上的实际选定元素。你真的不应该在 for 循环中依赖 index
,因为它会产生意想不到的行为。
v-if="current == i"
让您了解了这一点。尝试使用索引以外的其他元素显示元素,这些元素实际上可以“移动”并且 DOM 将删除它们,然后以糟糕的方式将它们带回,因此您的问题中出现了丑陋的过渡。
因为你有current
,所以直接使用它就像slides[current]
!
PS: 不确定你的项目是哪种配置,但你完全可以像我一样写你的样式(在@apply
的一行)。或者直接进入你的 template
但我猜你不喜欢这样。
我终于通过将 <h1>
元素包装在 <template>
标签内并对其进行循环而不是直接从 <transition>
标签进行循环来修复它。注意我在<transition>
标签里也加了mode="out-in"
,不然还是跳
<transition name="fade" mode="out-in">
<template v-for="(slide, i) in slides">
<h1 class="punchline" :key="slide" v-if="current == i">
{{ slide }}
</h1>
</template>
</transition>
跳转的原因是因为当我检查元素时,新内容已加载,而旧元素仍然存在,因此它在完全删除之前只是呈现在旧元素下方。不确定为什么会发生这种情况,但可能是因为 <transition>
不应该循环。
<transition-group>
像这样:
<transition-group name="fade" mode="out-in">
<h1
class="punchline"
v-for="(slide, i) in slides"
:key="slide"
v-show="current == i">
{{ slide }}
</h1>
</transition-group>
也不适用于此条件,因为它会产生相同的行为。 (或者我用错了?请告诉我)