【问题标题】:How can I fix this jumping transition?我该如何解决这种跳跃过渡?
【发布时间】:2021-05-11 15:27:27
【问题描述】:

我正在尝试构建某种淡入淡出滑块,但在转换之间出现了奇怪的跳跃,已经尝试过来自 this question 的解决方案,但它不适用于我的情况。我不知道为什么会发生这种情况,为了清楚起见,请看一下这个 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.js nuxt.js tailwind-css


    【解决方案1】:

    我终于通过将&lt;h1&gt; 元素包装在&lt;template&gt; 标记中并在其上执行循环而不是直接从&lt;transition&gt; 标记执行它来修复它。请注意,我还在&lt;transition&gt;标签中添加了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>
    

    跳转的原因是因为当我检查元素时,新内容被挂载,而旧元素仍然存在,所以它只是呈现在旧元素下方,然后才被完全删除。不知道为什么会发生这种情况,但可能是因为 &lt;transition&gt; 不应该被循环。

    &lt;transition-group&gt; 像这样:

    <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>
    

    也不适用于这种情况,因为它会产生相同的行为。 (或者我用错了?请告诉我)

    【讨论】:

    • 干得好!实际上,如果它是组件,它可能已经完成,这要感谢&lt;component :is="..."&gt;,但在常规元素上,拥有v-for + v-if 确实很棘手。供参考:vuejs.org/v2/guide/…
    【解决方案2】:

    如果这是带有 Vue Router 的 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-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-link组件,所以Vue2因为Nuxt3还没有发布。
    【解决方案3】:

    好的,我明白了!
    抱歉,我找不到 Nuxt + SCSS + Tailwind 工作的代码框,所以我几乎在我自己的 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,但我猜你不喜欢这种方式。

    【讨论】:

    • 感谢您的回答,解释得很好。它确实适用于跳跃,但当我尝试它时它不再褪色。
    • 嗯,确实,我看到了这个问题。会尽量找到解决办法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-05
    • 1970-01-01
    • 1970-01-01
    • 2021-12-30
    相关资源
    最近更新 更多