vue组件中如何让元素随动画出现和消失?

How to make elements appear and disappear with animation in a vue component?

我正在使用 quasar vue.js 框架并且有这个组件:

<template>
  <div class="landing-section">
    <div class="landing-section__text-box">
      <p class="landing-section__text-box__title">Albert Pike</p>
      <p class="landing-section__text-box__description">
        Albert Pike (December 29, 1809 – April 2, 1891) was an American author,
        poet, orator, editor, lawyer, jurist, and prominent member of the
        Freemasons. Pike was a senior officer of the Confederate States Army who
        commanded the District of Indian Territory in the Trans-Mississippi
        Theater of the American Civil War.
      </p>
      <q-btn
        class="q-mt-md"
        :to="{ name: 'AboutUs' }"
        padding="0.8rem 2.5rem"
        outline
        rounded
        color="white"
        label="Know More"
      />
    </div>
  </div>
</template>

<style scoped>
.landing-section {
  width: 100%;
  height: 37rem;
  background: url("https://getwallpapers.com/wallpaper/full/9/a/6/163230.jpg");
  background-repeat: no-repeat;
  background-size: cover;
  background-position: 20% -1.2rem;
  display: flex;
  align-items: center;
}
.landing-section__text-box {
  width: 33rem;
  margin-left: 5%;
  margin-right: 5%;
  margin-bottom: 5rem;
}
.landing-section__text-box__title {
  color: white;
  font-size: 2rem;
  font-weight: 700;
}
.landing-section__text-box__description {
  color: white;
  font-size: 1rem;
  font-weight: 500;
}
.landing-section__text-box__learn-more-button {
  color: white;
  font-size: 1rem;
}
::v-deep(.block) {
  font-weight: 700;
}
</style>

这是最终结果:

现在我想为以下元素添加动画: landing-section__text-box__title landing-section__text-box__description q-btn

  1. 这 3 个元素应该每 3 秒更改一次以显示新内容,尤其是标题和描述。
  2. 此更改应无限循环。

关于我该怎么做的任何帮助?

Transitioning Between Elements

const vm = new Vue({
  el: '#app',
  data() {
    return {
      sections: [{
          title: "Section A",
          description: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        },
        {
          title: "Section B",
          description: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
        }
      ],
      activeSectionIndex: 0,
    }
  },
  computed: {
    activeSection() {
      return this.sections[this.activeSectionIndex]
    }
  },
  methods: {
    nextSection() {
      this.activeSectionIndex++
      if(this.activeSectionIndex >= this.sections.length) this.activeSectionIndex = 0
    }
  },
  mounted() {
    this.timer = setInterval(() => this.nextSection(), 3000)
  },
  destroyed() {
    clearInterval(this.timer)
  }  
})
.slide-fade-enter-active {
  transition: all .8s ease;
}
.slide-fade-leave-active {
  transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
}
.slide-fade-enter, .slide-fade-leave-to
/* .slide-fade-leave-active below version 2.1.8 */ {
  transform: translateX(10px);
  opacity: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.14/vue.js"></script>
<div id="app">
  <transition name="slide-fade" mode="out-in">
    <div :key="activeSection.title">
      <p> {{ activeSection.title }} </p>
      <p> {{ activeSection.description }} </p>
    </div>
  </transition>
</div>

这是我的解决方案:

注意:我使用的是quasar vuejs框架

在类星体中启用动画:

quasar.conf.js

animations: "all",

src/store/general/state.js

export default function () {
  return {
    /* Home Page Landing Section data */
    landingSectionItems: [
      {
        title: `Altiyan Childs`,
        description: `Altijan Juric[2] (born 10 June 1975), best known by his stage name Altiyan Childs, is an Australian singer-songwriter.[3] He was the winner of the second season of The X Factor Australia in 2010.`,
        buttonLink: "AboutUs",
        buttonLabel: "Know More",
      },
      {
        title: `Freemasonry`,
        description: `Freemasonry or Masonry refers to fraternal organisations that trace their origins to the local guilds of stonemasons that, from the end of the 13th century, regulated the qualifications of stonemasons and their interaction with authorities and clients.`,
        buttonLink: "AboutUs",
        buttonLabel: "Know More",
      },
      {
        title: `Satanism`,
        description: `Satanism is a group of ideological and philosophical beliefs based on Satan. Contemporary religious practice of Satanism began with the founding of the atheistic Church of Satan in America in 1966, although a few historical precedents exist.`,
        buttonLink: "AboutUs",
        buttonLabel: "Know More",
      },
    ],

    activeLandingSectionItem: 0,
  };
}

src/store/general/mutations.js

export const changeLandingSectionItem = (state) => {
  if (state.activeLandingSectionItem >= state.landingSectionItems.length - 1) {
    state.activeLandingSectionItem = 0;
  } else {
    state.activeLandingSectionItem++;
  }
};

src/components/home/LandingSection.vue

<template>
  <div class="landing-section">
    <div class="landing-section__text-box">
      <transition name="fade-title" mode="out-in">
        <div :key="items[activeItem].title">
          <p
            class="landing-section__text-box__title"
            v-html="items[activeItem].title"
          ></p>
        </div>
      </transition>
      <transition name="fade-description" mode="out-in">
        <div :key="items[activeItem].title">
          <p class="landing-section__text-box__description">
            {{ items[activeItem].description }}
          </p>
        </div>
      </transition>

      <transition name="fade-button" mode="out-in">
        <div :key="items[activeItem].title">
          <q-btn
            class="q-mt-md"
            :to="{ name: items[activeItem].buttonLink }"
            padding="0.8rem 2.5rem"
            outline
            rounded
            color="white"
            :label="items[activeItem].buttonLabel"
          />
        </div>
      </transition>
    </div>
  </div>
</template>

<script>
import { computed, onMounted, onUnmounted } from "vue";
import { useStore } from "vuex";
export default {
  setup() {
    const $store = useStore();
    const items = computed(() => {
      return $store.state.general.landingSectionItems;
    });
    const activeItem = computed(() => {
      return $store.state.general.activeLandingSectionItem;
    });

    const changeItem = () => {
      $store.commit("general/changeLandingSectionItem");
    };

    const changeItemTimer = setInterval(() => {
      changeItem();
    }, 8000);

    onMounted(() => {
      changeItemTimer;
    });

    onUnmounted(() => {
      clearInterval(changeItemTimer);
    });

    return {
      items,
      activeItem,
      changeItem,
    };
  },
};
</script>

<style scoped>
.landing-section {
  width: 100%;
  height: 37rem;
  background: url("/images/landing-section/landing-img.png");
  background-repeat: no-repeat;
  background-size: cover;
  background-position: 20%;
  display: flex;
  align-items: center;
}
.landing-section__text-box {
  width: 33rem;
  margin-left: 5%;
  margin-right: 5%;
  margin-bottom: 2rem;
}
.landing-section__text-box__title,
.landing-section__text-box__description,
.landing-section__text-box__learn-more-button {
  color: white;
}
.landing-section__text-box__title {
  font-size: 2rem;
  font-weight: 700;
}
.landing-section__text-box__description {
  font-size: 1rem;
  font-weight: 500;
}
::v-deep(.block) {
  font-weight: 700;
}

/* Animations */

.fade-title-enter-active {
  animation: fadeInDown 1s;
}
.fade-title-leave-active {
  animation: fadeOutUp 1s;
}

.fade-description-enter-active {
  animation: fadeInRight 1s;
}
.fade-description-leave-active {
  animation: fadeOutRight 1s;
}

.fade-button-enter-active {
  animation: fadeInUp 1s;
}
.fade-button-leave-active {
  animation: fadeOutDown 1s;
}
</style>