Vue.js 中的不同路由器视图之间无法发生动画
Animation cannot take place between different router views in Vue.js
总结
我正在寻找一种在 Vue 中跨两个不同路由器视图创建动画的解决方案。我遇到的问题是,当浏览器切换到另一个路由器视图时,它会 re-render 页面。所以当浏览器re-render.
时动画就会消失
动画
基本上,该应用有两个不同的页面,homepage 和 aboutpage。当用户点击蓝色菜单按钮时,一个绿色的导航菜单会从左向右滑动,直到覆盖整个屏幕。此菜单有两个 link:about 和 home。如果用户点击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 link,我在这个沙箱中重现了这个问题,我认为这会帮助人们更好地理解我的问题。
https://codesandbox.io/s/vue-page-shift-anime-yp1yf
在这个沙盒中,我使用了一个 vue 模板。它包括一个主要组件:App.vue,以及 5 个其他组件:Homepage.vue、Aboutpage.vue、Background.vue、 Navbar.vue、Navigation.vue。 Background 包含 navbar 和 Navigation 以构成应用程序的静态元素。 Homepage 和 Aboutpage 都有 Background 和独一无二的 header。 主页的header是HOME,Aboutpage的header是ABOUT。然后,我在 main.js 中配置路由器,并在 App.vue 中放置 router-view 标签。我还将 router-link 添加到 Navigation.vue。我向 router-link 添加了一个点击事件 (page-shift),这个事件被发送到 Background.vue。所以当用户点击link时,它会调用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>
*Navigation.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>
*Homepage.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>
Aboutpage.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>
我遇到的问题
我发现如果我留在同一个路由器视图中,动画就会发生。如果我转到另一个视图,该页面将 re-render 导致所有内容重新加载,因此动画根本不会发生。例如,在沙盒中,如果您当前在家,然后单击“家”,就会出现动画;但是如果你从主页切换到关于,页面将重新加载并且动画失败。
我试过的
好吧,我是vue的新手。我认为一种解决方案是在不同视图之间切换时防止页面呈现。我不知道如何实现。我试过在 App.vue 中的 router-view 标签周围使用过渡标签,但它没有实现理想的动画效果。我也试过keep-alive,根本不行。希望我的解释有意义,我期待着您的解决方案!谢谢!
问题是您通过 Background.vue
在视图中包含了导航。不要那样做。将其包含在 App.vue
.
中
当路由更改时,<router-view />
的内容会填充该路由组件的全新实例(在 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>
当路线改变时,只有 <router-view />
被重新渲染,并且在您的路线上设置的动画将相应地执行。其他直接放在 App.vue
中的布局元素将不会被重新渲染。显然,如果您根据当前路线动态更改它们的内容,这些部分将更新,但它们的基础 DOM 元素将保持不变。
隐含地,您应该从您的视图中删除 <navigation />
,以及您决定直接放置在 App.vue
.
中的任何其他内容
总结 我正在寻找一种在 Vue 中跨两个不同路由器视图创建动画的解决方案。我遇到的问题是,当浏览器切换到另一个路由器视图时,它会 re-render 页面。所以当浏览器re-render.
时动画就会消失动画
基本上,该应用有两个不同的页面,homepage 和 aboutpage。当用户点击蓝色菜单按钮时,一个绿色的导航菜单会从左向右滑动,直到覆盖整个屏幕。此菜单有两个 link:about 和 home。如果用户点击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 link,我在这个沙箱中重现了这个问题,我认为这会帮助人们更好地理解我的问题。
https://codesandbox.io/s/vue-page-shift-anime-yp1yf
在这个沙盒中,我使用了一个 vue 模板。它包括一个主要组件:App.vue,以及 5 个其他组件:Homepage.vue、Aboutpage.vue、Background.vue、 Navbar.vue、Navigation.vue。 Background 包含 navbar 和 Navigation 以构成应用程序的静态元素。 Homepage 和 Aboutpage 都有 Background 和独一无二的 header。 主页的header是HOME,Aboutpage的header是ABOUT。然后,我在 main.js 中配置路由器,并在 App.vue 中放置 router-view 标签。我还将 router-link 添加到 Navigation.vue。我向 router-link 添加了一个点击事件 (page-shift),这个事件被发送到 Background.vue。所以当用户点击link时,它会调用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>
*Navigation.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>
*Homepage.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>
Aboutpage.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>
我遇到的问题 我发现如果我留在同一个路由器视图中,动画就会发生。如果我转到另一个视图,该页面将 re-render 导致所有内容重新加载,因此动画根本不会发生。例如,在沙盒中,如果您当前在家,然后单击“家”,就会出现动画;但是如果你从主页切换到关于,页面将重新加载并且动画失败。
我试过的 好吧,我是vue的新手。我认为一种解决方案是在不同视图之间切换时防止页面呈现。我不知道如何实现。我试过在 App.vue 中的 router-view 标签周围使用过渡标签,但它没有实现理想的动画效果。我也试过keep-alive,根本不行。希望我的解释有意义,我期待着您的解决方案!谢谢!
问题是您通过 Background.vue
在视图中包含了导航。不要那样做。将其包含在 App.vue
.
当路由更改时,<router-view />
的内容会填充该路由组件的全新实例(在 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>
当路线改变时,只有 <router-view />
被重新渲染,并且在您的路线上设置的动画将相应地执行。其他直接放在 App.vue
中的布局元素将不会被重新渲染。显然,如果您根据当前路线动态更改它们的内容,这些部分将更新,但它们的基础 DOM 元素将保持不变。
隐含地,您应该从您的视图中删除 <navigation />
,以及您决定直接放置在 App.vue
.