为了账号安全,请及时绑定邮箱和手机立即绑定

Vuejs - 一次一个项目的过渡组轮播动画

Vuejs - 一次一个项目的过渡组轮播动画

扬帆大鱼 2023-10-14 15:57:23
我正在尝试使用 Vue 转换组来实现轮播/幻灯片效果:他们每次都会对一个列表项(步骤)进行动画处理,其中上一个步骤会上升,而当前的步骤会随着时间轴的内容而上升。我不确定我想要完成的任务是否可以使用transition-group,因为整个父块都会动画,而不是子节点。如果是这样的话,如果我至少可以为父块设置动画,我会很高兴。另一个需要注意的是,当我使用transition-groupwithoutv-if或没有过滤列表时,默认情况下会呈现所有步骤,这不好。这是我的 HTML 结构:<transition-group class="steps-viewport" name="steps" tag="div">  <div v-for="step in currentStep" :key="step.order" class="step-wrapper">    <h3 class="is-size-5 mb-6 has-text-grey-light">      Passo {{ step.order }}    </h3>    <h1 class="is-size-3">{{ step.title }}</h1>    <h2 class="is-size-4 mt-2 has-text-grey">{{ step.headline }}</h2>    <component      class="mt-5"      v-bind:is="step.component"      @status-changed="handleStatusChange($event)"    ></component>  </div></transition-group>这是我的 CSS:.component-wrapper {  width: 100%;  .steps-viewport {    height: calc(100vh - 10rem);    overflow: hidden;    display: flex;    flex-direction: column;    .step-wrapper {      flex: 0 0 calc(100vh - 10rem);      display: flex;      justify-content: center;      flex-direction: column;    }  }}最后但并非最不重要的一点是我的组件的脚本:import ProductInfo from "./ProductInfo";export default {  components: {    ProductInfo  },  props: {    defaultActiveStep: {      type: Number,      default: 1    }  },  watch: {    activeStep() {      this.$emit("step-changed", this.activeStep);    }  },  computed: {    currentStep() {      return this.steps.filter(s => s.order === this.activeStep);    }  },  data: () => {    return {      activeStep: 1,      steps: [        {          order: 1,          title: "Title 1?",          headline:            "Headline 1",          component: "product-info"        },        {          order: 2,          title: "Title 2",          headline:            "Headline 2.",          component: "product-info"        },
查看完整描述

1 回答

?
凤凰求蛊

TA贡献1825条经验 获得超4个赞

您需要定义特殊的类来针对转换的不同阶段,在本例中为.steps-enter-active(中间转换状态)和.steps-enter-to(最终状态)。

为了使其在页面加载时发生,您还需要传递该appear属性。


如果你想要整个order块转换,你可以这样做:

new Vue({

  el: '#app',


  computed: {

    currentStep() {

      return this.steps.filter(s => s.order === this.activeStep);

    }

  },


  data: () => {

    return {

      activeStep: 1,

      steps: [{

          order: 1,

          title: "Title 1?",

          headline: "Headline 1",

          component: "product-info"

        },

        {

          order: 2,

          title: "Title 2",

          headline: "Headline 2.",

          component: "product-info"

        },

        {

          order: 3,

          title: "Title 3",

          headline: "Headline 3.",

          component: "product-info"

        },

        {

          order: 4,

          title: "Title 4!",

          headline: "Headline 4",

          component: "product-info"

        }

      ]

    };

  },

});


Vue.config.productionTip = false;

Vue.config.devtools = false;

.component-wrapper {

  width: 100%;

}


.steps-viewport {

  height: calc(100vh - 10rem);

  /* overflow: hidden */

  display: flex;

  flex-direction: column;

}


.step-wrapper {

  flex: 0 0 calc(100vh - 10rem);

  display: flex;

  justify-content: center;

  flex-direction: column;

}


.steps-enter-active {

  opacity: 0;

  transform: translateY(100%);

  transition: all 0.4s;

}


.steps-enter-to {

  opacity: 1;

  transform: translateY(0);

}

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>


<div id="app">

  <transition-group class="steps-viewport" name="steps" tag="div" appear>

    <div v-for="step in currentStep" :key="step.order" class="step-wrapper">

      <h3 class="is-size-5 mb-6 has-text-grey-light">

        Passo {{ step.order }}

      </h3>

      <h1 class="is-size-3">{{ step.title }}</h1>

      <h2 class="is-size-4 mt-2 has-text-grey">{{ step.headline }}</h2>

    </div>

  </transition-group>

</div>

如果您希望其中的每个元素进行过渡,您可以这样做,添加transition-delay

new Vue({

  el: '#app',


  computed: {

    currentStep() {

      return this.steps.filter(s => s.order === this.activeStep);

    }

  },


  data: () => {

    return {

      activeStep: 1,

      steps: [{

          order: 1,

          title: "Title 1?",

          headline: "Headline 1",

          component: "product-info"

        },

        {

          order: 2,

          title: "Title 2",

          headline: "Headline 2.",

          component: "product-info"

        },

        {

          order: 3,

          title: "Title 3",

          headline: "Headline 3.",

          component: "product-info"

        },

        {

          order: 4,

          title: "Title 4!",

          headline: "Headline 4",

          component: "product-info"

        }

      ]

    };

  },

});


Vue.config.productionTip = false;

Vue.config.devtools = false;

.component-wrapper {

  width: 100%;

}


.steps-viewport {

  height: calc(100vh - 10rem);

  /* overflow: hidden */

  display: flex;

  flex-direction: column;

}


.step-wrapper {

  flex: 0 0 calc(100vh - 10rem);

  display: flex;

  justify-content: center;

  flex-direction: column;

}


.steps-enter-active {

  opacity: 0;

  transform: translateY(100%);

  transition: all 0.4s;

}


.steps-enter-to {

  opacity: 1;

  transform: translateY(0);

}

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>


<div id="app">

  <div v-for="step in currentStep" :key="step.order" class="step-wrapper">

    <transition-group class="steps-viewport" name="steps" tag="div" appear>

      <h3 class="is-size-5 mb-6 has-text-grey-light" key="1">

        Passo {{ step.order }}

      </h3>

      <h1 class="is-size-3" style="transition-delay: 0.1s" key="2">{{ step.title }}</h1>

      <h2 class="is-size-4 mt-2 has-text-grey" style="transition-delay: 0.2s" key="3">{{ step.headline }}</h2>

    </transition-group>

  </div>

</div>

要也转出,您需要使用transition相反,这样您就可以使用mode="out-in"它允许元素在下一个进入之前先转出。

您还需要使用 来定位 CSS 中过渡元素的子元素.steps-enter-active > *。然后,只需添加一个.steps-leave-to定义要离开的状态的类:

new Vue({

  el: '#app',


  computed: {

    currentStep() {

      return this.steps.filter(s => s.order === this.activeStep);

    }

  },


  methods: {

    nextStep() {

      if (this.activeStep !== this.steps.length) {

        this.activeStep++;

      } else {

        this.activeStep = 1;

      }

    }

  },


  data: () => {

    return {

      activeStep: 1,

      steps: [{

          order: 1,

          title: "Title 1?",

          headline: "Headline 1",

          component: "product-info"

        },

        {

          order: 2,

          title: "Title 2",

          headline: "Headline 2.",

          component: "product-info"

        },

        {

          order: 3,

          title: "Title 3",

          headline: "Headline 3.",

          component: "product-info"

        },

        {

          order: 4,

          title: "Title 4!",

          headline: "Headline 4",

          component: "product-info"

        }

      ]

    };

  },

});


Vue.config.productionTip = false;

Vue.config.devtools = false;

.component-wrapper {

  width: 100%;

}


.steps-viewport {

  height: calc(100vh - 10rem);

  /* overflow: hidden */

  display: flex;

  flex-direction: column;

}


.step-wrapper {

  flex: 0 0 calc(100vh - 10rem);

  display: flex;

  justify-content: center;

  flex-direction: column;

}


.step-wrapper,

.step-wrapper>* {

  transition: all 0.4s;

}


.step-wrapper>h1 {

  transition-delay: 0.1s;

}


.step-wrapper>h2 {

  transition-delay: 0.2s;

}


.steps-enter-active>* {

  opacity: 0;

  transform: translateY(100%);

}


.steps-leave-to>* {

  opacity: 0;

  transform: translateY(-100%);

}


.steps-enter-to>* {

  opacity: 1;

  transform: translateY(0);

}

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>


<div id="app">

  <transition name="steps" mode="out-in" duration="600" appear>

    <div v-for="step in currentStep" :key="step.order" class="step-wrapper">

      <h3 class="is-size-5 mb-6 has-text-grey-light">

        Passo {{ step.order }}

      </h3>

      <h1 class="is-size-3">{{ step.title }}</h1>

      <h2 class="is-size-4 mt-2 has-text-grey">{{ step.headline }}</h2>

    </div>

  </transition>

  <button @click="nextStep()">Next</button>

</div>

最后,为了在添加新元素后让所有内容顺利向上移动,您可以将初始字段包装在 div 中,将新元素包装在过渡中,并将第一个 div 的高度减少新元素的高度。

您还需要转换高度并配置时间(转换延迟和持续时间属性)以正确匹配:

new Vue({

  el: '#app',


  computed: {

    currentStep() {

      return this.steps.filter(s => s.order === this.activeStep);

    }

  },


  methods: {

    nextStep() {

      this.$refs.addStep.disabled = false;

      this.extraStep = false;

      this.$refs.addAnotherStep.disabled = false;

      this.anotherExtraStep = false;


      if (this.activeStep !== this.steps.length) {

        this.activeStep++;

      } else {

        this.activeStep = 1;

      }

    },

    addStep() {

      const initial = document.querySelector('.step-initial');

      const input = document.querySelector('.step-input');


      // 52px = input height + margin + border

      initial.style.maxHeight = initial.offsetHeight - 52 + 'px';


      if (!this.extraStep) {

        this.$refs.addStep.disabled = true;

        this.extraStep = true;

      } else {

        this.$refs.addAnotherStep.disabled = true;

        this.anotherExtraStep = true;

      }

    }

  },


  data: () => {

    return {

      extraStep: false,

      anotherExtraStep: false,

      activeStep: 1,

      steps: [{

          order: 1,

          title: "Title 1?",

          headline: "Headline 1",

          component: "product-info"

        },

        {

          order: 2,

          title: "Title 2",

          headline: "Headline 2.",

          component: "product-info"

        },

        {

          order: 3,

          title: "Title 3",

          headline: "Headline 3.",

          component: "product-info"

        },

        {

          order: 4,

          title: "Title 4!",

          headline: "Headline 4",

          component: "product-info"

        }

      ]

    };

  },

});


Vue.config.productionTip = false;

Vue.config.devtools = false;

#app {

  position: relative;

  height: calc(300px + 52px);

}


.component-wrapper {

  width: 100%;

}


.steps-viewport {

  /* height: calc(100vh - 10rem); */

  /* overflow: hidden */

  display: flex;

  flex-direction: column;

}


.step-wrapper,

.step-wrapper * {

  transition: all 0.2s;

}


.step-wrapper * {

  margin: 0;

}


.step-initial {

  display: flex;

  justify-content: space-evenly;

  flex-direction: column;

  height: 300px;

  max-height: 300px;

}


.step-initial *:nth-child(2) {

  transition-delay: 0.05s;

}


.step-initial *:nth-child(3) {

  transition-delay: 0.1s;

}


.steps-enter-active .step-initial * {

  opacity: 0;

  transform: translateY(100%);

}


.steps-leave-to .step-initial *,

.steps-leave-to .step-input {

  opacity: 0;

  transform: translateY(-100%);

}


.steps-leave-to .step-input:nth-of-type(2) {

  transition-delay: 0.2s;

}


.steps-leave-to .step-input:nth-of-type(3) {

  transition-delay: 0.3s;

}


.steps-enter-to .step-initial * {

  opacity: 1;

  transform: translateY(0);

}


.step-input {

  margin: 20px 0;

  height: 30px;

}


.steps-input-enter-active {

  opacity: 0;

  transform: translateY(100%);

}


.steps-input-leave-to {

  opacity: 0;

  transform: translateY(-100%);

}


.steps-input-enter-to {

  opacity: 1;

  transform: translateY(0);

}


.step-btns {

  position: absolute;

  bottom: 10px;

}

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>


<div id="app">

  <transition name="steps" mode="out-in" duration="350" appear>

    <div v-for="step in currentStep" :key="step.order" class="step-wrapper">

      <div class="step-initial">

        <h3 class="is-size-5 mb-6 has-text-grey-light">

          Passo {{ step.order }}

        </h3>

        <h1 class="is-size-3">{{ step.title }}</h1>

        <h2 class="is-size-4 mt-2 has-text-grey">{{ step.headline }}</h2>

      </div>

      <transition name="steps-input">

        <div v-if="extraStep" class="step-input">

          <input />

        </div>

      </transition>

      <transition name="steps-input">

        <div v-if="anotherExtraStep" class="step-input">

          <input />

        </div>

      </transition>

    </div>

  </transition>

  <div class="step-btns">

    <button @click="nextStep()">Next</button>

    <button @click="addStep()" ref="addStep">Add Step</button>

    <button @click="addStep()" ref="addAnotherStep">Add Another Step</button>

  </div>

</div>


查看完整回答
反对 回复 2023-10-14
  • 1 回答
  • 0 关注
  • 297 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号