【问题标题】:Animation cannot take place between different router views in Vue.js动画不能在 Vue.js 中的不同路由器视图之间发生
【发布时间】:2021-04-01 10:18:19
【问题描述】:

总结 我正在寻找一种解决方案来在 Vue 中跨两个不同的路由器视图创建动画。我遇到的问题是,当浏览器切换到另一个路由器视图时,它会重新渲染页面。所以当浏览器重新渲染时动画会消失。

动画
基本上,该应用程序有两个不同的页面,homepageaboutpage。当用户点击蓝色菜单按钮时,一个绿色的导航菜单会从左向右滑动直到覆盖整个屏幕。此菜单有两个链接:abouthome。如果用户点击about,一个白色的覆盖层会从左边移动并覆盖整个屏幕,然后它会移动到右边然后消失。关于页面消失后显示。 这是演示我想要实现的动画效果的codepen示例(此演示没有使用路由器)。 https://codepen.io/wl1664209734/pen/qBamXEL
下面是codepen中vue模板中重新创建动画的代码

  <template>
  <div class="wrap">
    <!--Transition Overlay -->
    <transition
      name="page-shift"
      enter-active-class="animate__animated animate__slideInLeft animate__faster"
      leave-active-class="animate__animated animate__slideOutRight animate__faster"
    >
      <div v-if="showOverlay" class="overlay"></div>
    </transition>

    <!--Navigation Menu -->
    <transition
      name="slide"
      enter-active-class="animate__animated animate__slideInLeft animate__faster"
      leave-active-class="animate__animated animate__slideOutLeft animate__faster"
    >
      <div v-if="menuOpen" class="navigation">
        <div
          class="container h-100 d-flex justify-content-center align-items-center"
        >
          <div class="text-center">
            <h3 class="font-weight-bold text-light" @click="pageShiftToHome">
              HOME
            </h3>
            <h3 class="font-weight-bold text-light" @click="pageShiftToAbout">
              ABOUT
            </h3>
          </div>
        </div>
      </div>
    </transition>

    <!--NavBar -->
    <div class="navbar d-flex justify-content-end">
      <div
        @click="menuOpen = !menuOpen"
        class="nav-link font-weight-bold mt-3 btn btn-primary"
      >
        Menu
      </div>
    </div>

    <!--Home Page -->
    <div
      v-if="showHomePage"
      class="homepage d-flex justify-content-center align-items-center"
    >
      <h1 class="text-light">HOME</h1>
    </div>

    <!--About Page -->
    <div
      v-if="showAboutPage"
      class="aboutpage d-flex justify-content-center align-items-center"
    >
      <h1 class="text-light">ABOUT</h1>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      menuOpen: false,
      showHomePage: true,
      showAboutPage: false,
      showOverlay: false
    };
  },
  methods: {
    pageShiftToHome: function () {
      this.showOverlay = !this.showOverlay;

      const _this = this;
      setTimeout(function () {
        _this.menuOpen = !_this.menuOpen;
        _this.showAboutPage = false;
        _this.showHomePage = true;
      }, 500);
      setTimeout(function () {
        _this.showOverlay = !_this.showOverlay;
      }, 1000);
    },

    pageShiftToAbout: function () {
      this.showOverlay = !this.showOverlay;

      const _this = this;
      setTimeout(function () {
        _this.menuOpen = !_this.menuOpen;
        _this.showHomePage = false;
        _this.showAboutPage = true;
      }, 500);
      setTimeout(function () {
        _this.showOverlay = !_this.showOverlay;
      }, 1000);
    }
  }
};
</script>

<!-- Use preprocessors via the lang attribute! e.g. <style lang="scss"> -->
<style lang="scss">
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  background: #292929;
}

.wrap {
  width: 100%;
  height: 100vh;
  background: #292929;

  .overlay {
    width: inherit;
    height: inherit;
    background: #fff;
    position: absolute;
    z-index: 999;
  }

  .navigation {
    width: inherit;
    height: inherit;
    background: #3ecc28;
    position: absolute;
    z-index: 111;

    h3 {
      cursor: pointer;
    }
  }

  .navbar {
    width: 100%;
    z-index: 333;
    .nav-link {
      color: #fff;
      cursor: pointer;
      border: 0;
    }
  }

  .homepage {
    width: inherit;
    height: inherit;
    position: absolute;
    top: 0;
    left: 0;
  }

  .aboutpage {
    @extend .homepage;
  }
}
</style>

我的代码解释
这是codeSandbox链接,我在这个沙箱中重现了问题,我认为这将有助于人们更好地理解我的问题。 https://codesandbox.io/s/vue-page-shift-anime-yp1yf
在这个沙箱中,我使用了一个 vue 模板。它包括一个主要组件:App.vue,以及 5 个其他组件:Homepage.vue、Aboutpage.vue、Background.vue、Navbar.vue、Navigation.vueBackground 包含 navbarNavigation 以形成应用程序的静态元素。 HomepageAboutpage 都有 Background 和一个独特的标题。 Homepage 的标题是 HOME,Aboutpage 的标题是 ABOUT。然后,我在 main.js 中配置路由器,并将 router-view 标签放在 App.vue 中。我还将路由器链接添加到 Navigation.vue。我在路由链接中添加了一个点击事件(page-shift),这个事件被发送到 Background.vue。所以当用户点击链接时,它会调用Background.vue中的pageShift方法,导致动画发生。


这是重要组件的代码

*Main.js

import Vue from "vue";
import App from "./App.vue";
import VueRouter from "vue-router";
import "bootstrap/dist/css/bootstrap.css";
import "animate.css";

import Homepage from "./components/Homepage";
import Aboutpage from "./components/Aboutpage";

Vue.use(VueRouter);
Vue.config.productionTip = false;

const router = new VueRouter({
  routes: [
    {
      path: "*",
      component: Homepage
    },
    {
      path: "/about",
      component: Aboutpage
    }
  ]
});

new Vue({
  render: (h) => h(App),
  router
}).$mount("#app");

*App.vue

<template>
  <div id="app">
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: "App",
};
</script>

<style>
body {
  background: #292929;
}

#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
</style>

*Background.vue

<template>
  <div class="wrap">
    <transition
      name="page-shift"
      enter-active-class="animate__animated animate__slideInLeft animate__faster"
      leave-active-class="animate__animated animate__slideOutRight animate__faster"
    >
      <div v-if="showOverlay" class="overlay"></div>
    </transition>
    <navigation :menuOpen="menuOpen" @page-shift="pageShift" />
    <navbar @open-menu="menuOpen = !menuOpen" />
    <div>
      <div class="page d-flex justify-content-center align-items-center">
        <h1 class="text-light">{{ header }}</h1>
      </div>
    </div>
  </div>
</template>

<script>
import Navbar from "./Navbar";
import Navigation from "./Navigation";

export default {
  name: "background",
  props: ["header"],
  data: function () {
    return {
      menuOpen: false,
      showOverlay: false,
    };
  },
  components: {
    Navbar,
    Navigation,
  },
  methods: {
    pageShift: function () {
      this.showOverlay = !this.showOverlay;
      const _this = this;
      setTimeout(function () {
        _this.menuOpen = !_this.menuOpen;
      }, 500);
      setTimeout(function () {
        _this.showOverlay = !_this.showOverlay;
      }, 1000);
    },
  },
};
</script>

<style lang="scss" scoped>
.wrap {
  width: 100%;
  height: 100vh;

  .overlay {
    width: inherit;
    height: inherit;
    background: #fff;
    position: absolute;
    z-index: 999;
  }

  .page {
    width: 100%;
    height: 100vh;
    position: absolute;
    top: 0;
    left: 0;
  }
}
</style>

*Navbar.vue

<template>
  <div class="navbar d-flex justify-content-end">
    <div
      @click="$emit('open-menu')"
      class="nav-link font-weight-bold mt-3 btn btn-primary"
    >
      Menu
    </div>
  </div>
</template>

<script>
export default {
  name: "navbar",
};
</script>

<style lang="scss">
.navbar {
  width: 100%;
  z-index: 333;
  .nav-link {
    color: #fff;
    cursor: pointer;
    border: 0;
  }
}
</style>

*导航.vue

<template>
  <transition
    name="slide"
    enter-active-class="animate__animated animate__slideInLeft animate__faster"
    leave-active-class="animate__animated animate__slideOutLeft animate__faster"
  >
    <div v-if="menuOpen" class="navigation">
      <div
        class="container h-100 d-flex justify-content-center align-items-center"
      >
        <div class="text-center">
          <router-link to="/*" class="font-weight-bold text-light"
            ><h3 @click="$emit('page-shift')">HOME</h3></router-link
          >
          <router-link to="/about" class="font-weight-bold text-light"
            ><h3 @click="$emit('page-shift')">ABOUT</h3></router-link
          >
        </div>
      </div>
    </div>
  </transition>
</template>

<script>
export default {
  name: "navigation",
  props: ["menuOpen"],
};
</script>

<style lang="scss" scoped>
.navigation {
  width: 100%;
  height: 100%;
  background: #3ecc28;
  position: absolute;
  z-index: 111;
  top: 0;
  left: 0;

  a:hover {
    text-decoration: none;
  }

  span {
    cursor: pointer;
  }
}
</style>

*主页.vue

<template>
  <background :header="'HOME'" />
</template>

<script>
import Background from "./Background";

export default {
  name: "homepage",
  data: function () {
    return {
      menuOpen: false,
    };
  },
  components: {
    Background,
  },
};
</script>

<style lang="scss" scoped>
</style>

关于页面.vue

<template>
  <background :header="'ABOUT'" />
</template>

<script>
import Background from "./Background";

export default {
  name: "aboutpage",
  data: function () {
    return {
      menuOpen: false,
    };
  },
  components: {
    Background,
  },
};
</script>

<style lang="scss" scoped>
</style>

我遇到的问题 我发现如果我留在同一个路由器视图中,动画就会发生。如果我转到另一个视图,页面将重新渲染,导致一切都重新加载,所以动画根本不会发生。例如,在沙盒中,如果您当前在家,并且单击主页,则会发生动画;但是如果你从 home 切换到 about,页面会重新加载,动画会失败。

我尝试过的 好吧,我是 vue 的新手。我认为一种解决方案是在不同视图之间切换时防止页面呈现。我不知道如何实现。我尝试在 App.vue 中的 router-view 标记周围使用过渡标记,但它没有实现理想的动画效果。我也试过keep-alive,它根本不起作用。希望我的解释有意义,我期待您的解决方案!谢谢!

【问题讨论】:

    标签: javascript vue.js animation vue-router vue-cli-3


    【解决方案1】:

    问题是您在视图中包含导航,通过Background.vue。不要那样做。将其包含在App.vue 中。

    当路由更改时,&lt;router-view /&gt; 的内容将填充该路由组件的全新实例(设置在 router/index.ts 中的 routes 数组中(或 .js))。所以,而不是:

    <template>
      <div id="app">
        <router-view></router-view>
      </div>
    </template>
    

    ...您的App.vue 应包含:

    <template>
      <div id="app">
        <navigation />
        <router-view />
      </div>
    </template>
    

    如果您不想在页面更改时重新呈现页面的任何其他部分(一些侧边栏,可能是页脚),也将它们包含在 App.vue 中。因此,使用页脚和侧边栏,它可能看起来像:

    <template>
      <div id="app">
        <header>
          <navigation />
        </header>
        <main>
          <sidebar />
          <router-view />
        </main>
        <footer />
      </div>
    </template>
    

    当路线改变时,只有&lt;router-view /&gt; 被重新渲染,并且在您的路线上设置的动画将相应地执行。其他直接放在App.vue 中的布局元素不会重新渲染。显然,如果您根据当前路由动态更改它们的内容,这些部分将更新,但它们的基本 DOM 元素将保持不变。

    您应该隐式地从您的视图中删除 &lt;navigation /&gt;,以及您决定直接放置在 App.vue 中的任何其他内容。

    【讨论】:

    • 嗨,涛,非常感谢!您的解决方案解决了我的问题。就像你提到的那样,问题是因为我在router-view 中包含了动画组件,所以它每次都重新渲染。我只是将它从router-view 移出,现在效果很好!
    • @Liang,这是重新组织你所拥有的东西的尝试:codesandbox.io/s/vue-page-shift-anime-forked-bxw0c?file=/src/…。问题是我真的不知道它应该如何表现,所以......无论如何,希望它有所帮助。
    • 感谢您制作这个。真的很有帮助!顺便说一句,动画对我来说真的很酷;)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-02-12
    • 1970-01-01
    • 2020-05-20
    • 2022-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多