该系列是为了让看过Vue官方文件或学过Vue但是却不知道怎麽下手去重构现在有的网站而去规画的系列文章,在这边整理了许多我自己使用Vue重构很多网站的经验分享给读者们。
我们在开发网站的时候很常会处理 UI 的动态效果,以前大家会很常使用 JQuery 的 animate 的函式去做动态效果,但是 JQuery 的 animate 效能不是这麽好,再加上它只能使用在DOM上面,多少觉得有些不方便,所以今天要来介绍四种我自己在Vue上面开发网页动态上面很常使用的方式。
在制作网页的时候有许多小地方的动态其实可以不用透过JS的方式来处理,透过 CSS3 的 transition
还有 @keyframes
可以很轻易地达成,也好去把我们动画的逻辑还有程序的业务逻辑给分开,我们来看一下这两个的范例。
codepen 范例 https://codepen.io/MikeCheng1208/pen/NWgRGpp
<script>
import { ref } from 'vue'
export default {
setup(){
const isOpen = ref(false);
const handleMenuOpen = (bool) => {
isOpen.value = bool
}
return {
isOpen,
handleMenuOpen
}
}
};
</script>
<template>
<!-- 开启选单的按钮 -->
<a class="menuBtn" @click="handleMenuOpen(true)">
<i class="fas fa-bars fa-3x"></i>
</a>
<div class="content"></div>
<!-- 选单 -->
<div :class="['menu', {open: isOpen}]">
<!-- 关闭选单的按钮 -->
<a class="closeBtn" @click="handleMenuOpen(false)">
<i class="fas fa-times fa-3x"></i>
</a>
<ul class="nav">
<li><a>abous</a></li>
<li><a>content</a></li>
<li><a>user</a></li>
<li><a>address</a></li>
</ul>
</div>
</template>
<style lang="scss">
#app{
// 以上省略...
.menu{
position: fixed;
top: 0;
right: -350px;
width: 350px;
height: 100%;
z-index: 20;
background-color: #fff;
// 对 right 属性添加 transition 动画
transition: right 0.3s;
&.open{
right: 0px;
}
// 以下省略...
}
}
</style>
我们在这边使用了 css3 的 transition
属性,因为我们的选单是用定位的方式去摆放位置,所以针对 right
这个属性去处理,只要 right
一变动 transition
就会自动把改变的 value 中间给做上补间动画。
关於 transition 的细节我们可以参考 MDN https://developer.mozilla.org/zh-TW/docs/Web/CSS/transition
不过要特别注意一件事情,很多人会为了贪图方便,所以这样写
.menu{
// 对全部的属性添加 transition 动画
transition: all 0.3s;
&.open{
right: 0px;
}
}
用 all
的方式可以对 .menu
全部的属性都给予动画,但是这样不是很好,因为我们并非全部都要使用 transition
,为了可以一眼就看出哪个属性被赋予的动画,也可以避免无谓的效能浪费,建议还是乖乖写上单一属性就好,如果要写多个属性的话可以这样,用 ,
的方式串起来。
transition: right 0.3s, background 0.3s, color 0.3s;
codepen 范例 https://codepen.io/MikeCheng1208/pen/QWgKjVa
<script>
import { ref } from 'vue'
export default {
setup(){
const isError = ref(false);
const handleAlert = (bool) => {
isError.value = bool;
}
return {
isError,
handleAlert
}
}
};
</script>
<template>
<div class="content">
<button @click="handleAlert(true)">开启Alert</button>
<div :class="['alert', {open: isError}]">
<i class="fas fa-times fa-6x" @click="handleAlert(false)"></i>
<h1>发生未知的错误</h1>
</div>
</div>
</template>
<style lang="scss">
@keyframes showAnim {
from {
bottom: -90%;
}
70% {
bottom: 70%;
}
to {
bottom: 50%;
}
}
#app{
// 以下省略 ...
.content{
// 以下省略 ...
.alert{
position: absolute;
bottom: -50%;
left: 50%;
transform: translateX(-50%) translateY(50%);
width: 500px;
height: 400px;
border-radius: 16px;
background-color: #fff;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
box-shadow: 0 0 30px rgba(#000, 0.5);
&.open {
animation-name: showAnim;
animation-duration: 0.45s;
animation-iteration-count: 1;
animation-fill-mode: forwards;
animation-timing-function: ease-in-out;
}
}
}
}
</style>
关於 @keyframes 的细节我们可以参考 MDN https://developer.mozilla.org/zh-CN/docs/Web/CSS/@keyframes
这边我们一样是用切换 class 的方式来控制,只是这次的动画需要更加的处理细节,你会看到我的 demo 有一个弹性的效果,像这样的细节就可以使用 @keyframes
的方式来处理,再搭配 animation
属性来处理,就可以很轻易的处理大部分网页上的动态。
我们现在已经学会了使用 CSS3 来取代以前我们使用 JQuery 的 animate 的动态效果,透过 CSS3 的动画,我们可以获得更顺畅的体验,更好的管理动画的逻辑。
除了透过切换 class 加入动画以外,Vue 提供了一个 transition
的 component,让我们可以用简单的方式可以处理 component 之间的过渡动画,我们只需要把你要执行动画的 component 给包起来,透过 v-if
或是 v-show
就可以使用了。
<transition>
<div v-if="isShow"></div>
</transition>
以下就是我们用 transition
完成之後的样子
codepen 范例 https://codepen.io/MikeCheng1208/pen/mdwrVRM
<script>
import { ref } from 'vue'
export default {
setup(){
const isShow = ref(false);
const handleAlert = (bool) => {
isShow.value = bool;
}
return {
isShow,
handleAlert
}
}
};
</script>
<template>
<div class="content">
<button @click="handleAlert(true)">开启Alert</button>
<transition name="fade">
<div class="alert" v-if="isShow">
<i class="fas fa-times fa-6x" @click="handleAlert(false)"></i>
<h1>发生未知的错误</h1>
</div>
</transition>
</div>
</template>
<style lang="scss">
.fade-enter-active, .fade-leave-active {
transition: opacity 0.5s ease;
}
.fade-enter-from, .fade-leave-to {
opacity: 0;
}
// 以下省略...
</style>
你会看到我在 transition 身上给了一个 name
叫做 fade
,这个时候我就可以定义我的 transition 动画在执行的时候它的 class 的定义,透过 v-if
或是 v-show
,来达到过渡的动态效果。
<transition name="mike"></transition>
// 这些 class 就是看你的 transition 的 name 叫什麽,前面的名称就是你的 name
.mike-enter-active, .mike-leave-active {}
.mike-enter-from, .mike-leave-to {}
在 进场 到 离开 的过程中,会有 6 个 class 的状态 ( 详情请看官方文件 ),Vue2 跟 Vue3 的状态名称有稍微不一样,所以看文件不要看错。
v-enter-from
:开始的动画开始前。v-enter-active
:动画开始的执行过程。v-enter-to
:开始的动画结束时。v-leave-from
:离开的动画开始前。v-leave-active
:离开动画的执行过程。v-leave-to
:离开动画的结束时。既然我们已经知道了 transition 的 6 个状态,那接下来我们来看一下这个范例。
这是一个用 transition
做出来的轮播效果,我们来看一下怎麽做
<script>
import { ref } from 'vue'
export default {
setup(){
const imgIdx = ref(0);
const slidList = ref([
{ id: '1', src: "https://source.unsplash.com/600x400?1" },
{ id: '2', src: "https://source.unsplash.com/600x400?2" },
{ id: '3', src: "https://source.unsplash.com/600x400?3" },
{ id: '4', src: "https://source.unsplash.com/600x400?4" },
{ id: '5', src: "https://source.unsplash.com/600x400?5" },
{ id: '6', src: "https://source.unsplash.com/600x400?6" },
{ id: '7', src: "https://source.unsplash.com/600x400?7" },
{ id: '8', src: "https://source.unsplash.com/600x400?8" },
]);
const handleMenuActive = idx => {
imgIdx.value = idx;
};
return {
imgIdx,
slidList,
handleMenuActive,
}
}
};
</script>
我们会先把图片的资料 slidList
给定义出来,然後还有目前点到的按钮索引 imgIdx
给定义出来。
<template>
<div class="content">
<div class="mid">
<transition name="slids">
<img
v-for="(item, idx) in slidList"
v-show="imgIdx === idx"
:key="item.id"
:src="item.src"
/>
</transition>
</div>
<nav class="nav_menu">
<a
v-for="(item, idx) in slidList"
:key="item.id"
:class="{active: imgIdx === idx}"
@click="handleMenuActive(idx)"
>
{{idx + 1}}
</a>
</nav>
</div>
</template>
<style lang="scss">
.slids-enter-active, .slids-leave-active{
transition: transform .3s ease;
}
.slids-enter-from{
transform: translateX(-550px);
}
.slids-leave-to{
transform: translateX(550px)
}
.content {
width: 600px;
height: 400px;
.mid{
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
margin-bottom: 20px;
img{
position: absolute;
top: 0;
right: 0;
}
}
}
// 其他省略...
</style>
然後对里面的图片去用 v-for
搭配 v-show
来处理切换的部分,在这边要注意一下,图片的部分是透过 position: absolute;
来定位,压在同一个地方,接下来仔细的来看一下 css 的部分
<style lang="scss">
.slids-enter-active, .slids-leave-active{
transition: transform .3s ease;
}
.slids-enter-from{
transform: translateX(-550px);
}
.slids-leave-to{
transform: translateX(550px)
}
</style>
动画开始的执行过程( slids-enter-active )
跟离开动画的执行过程 (slids-leave-active)
都给它使用 transition
来处理过场的部分,然後每个图片进来以前 slids-enter-from
,会被 transform 移到 -550px
这个位置 transform: translateX(-550px);
,自然进场的时候就会滑动到原本的地方,再来图片离开的时候slids-leave-to
,就会被 transform 移到 550px
跑出去外面,所以只要能掌握这些状态,你就用 transition 自由的控制你的组件要怎麽动。
但是现在会发生一个问题,就是使用 <transition></transition>
这个动画组件,里面的元件只能有一个,所以透过 v-for
回圈产生出来的 <img/>
会无法执行,所以除了transition
这个动画组件以外,还有另外一个叫做 transition-group
的组件,就是专门处理 v-for
回圈产生出来的 DOM 去跑动态。
关於 transition-group 的官方文件 : https://v3.vuejs.org/api/built-in-components.html#transition-group
transition-group
跟transition
的 API 一模一样,所以不用担心要改其他东西。
<transition-group name="slids">
<img
v-for="(item, idx) in slidList"
v-show="imgIdx === idx"
:key="item.id"
:src="item.src"
/>
</transition-group>
这样一来就可以正常的执行了。
codepen 范例 : https://codepen.io/MikeCheng1208/pen/oNwzxBq
因为我们现在只能朝一个方向滑动,当我今天希望我点击的时候可以左右滑动,像是下面范例这样。
所以我必须要去判断说,我最新点击的按钮跟上一个按钮比,是在左边还是右边,然後我要去切换 transition-group
的 name
,来达到动画切换左右的部分。
首先我先定义一下左右滑的 css
<style lang="scss">
.left-enter-active,
.left-leave-active,
.right-enter-active,
.right-leave-active {
transition: transform .3s ease;
}
.left-enter-from{
transform: translateX(-550px);
}
.left-leave-to{
transform: translateX(550px)
}
.right-enter-from{
transform: translateX(550px)
}
.right-leave-to{
transform: translateX(-550px);
}
</style>
你会看到我的 class 开头被改成了 left
跟right
,所以可想而知,我们的 transition-group
的 name
要切换 left
跟right
。
所以在这边我新定义了两个东西
const prevIdx = ref(0); // 纪录上一个点击的按钮索引
const transType = ref("right"); // 当前的 transition-group 的 name
然後在我们点击按钮的 function 加入一下判断
const handleMenuActive = idx => {
imgIdx.value = idx;
transType.value = idx > prevIdx.value ? "left" : "right";
}
这边我会判断当我点击的索引比上一个索引大的时候,那就是往左滑,不然就右滑,但问题来了,我要什麽时候去纪录上一个索引的值呢? 不能在 click 的时候,不然会同步的去写入,所以有人会说,那等它慢一点在写入,所以写一个 setTimeout 就好了,拜托不要XDDDDD
transition
跟 transition-group
有提供动画的 Lifecycle Hooks,所以这次我们要使用 after-leave
这个 hooks,after-leave
是当你动画执行结束之後会触发,所以我们要在 after-leave
的时候去写入我们的 prevIdx.value
。
const afterLeave = () => {
prevIdx.value = imgIdx.value;
}
return {
afterLeave,
}
定义好了 prevIdx.value
的 callback 的 function 後,直接 on 这个 hooks
<transition-group
:name="transType"
@after-leave="afterLeave"
>
<img
v-for="(item, idx) in slidList"
v-show="imgIdx === idx"
:key="item.id"
:src="item.src"
/>
</transition-group>
这样一来就可以让你在 click 的时候,去切换 transition-group
的 name,以达到左右换图的效果。
<script>
import { ref } from 'vue'
export default {
setup(){
const imgIdx = ref(0);
const prevIdx = ref(0);
const transType = ref("right");
const slidList = ref([
{ id: '1', src: "https://source.unsplash.com/600x400?1" },
{ id: '2', src: "https://source.unsplash.com/600x400?2" },
{ id: '3', src: "https://source.unsplash.com/600x400?3" },
{ id: '4', src: "https://source.unsplash.com/600x400?4" },
{ id: '5', src: "https://source.unsplash.com/600x400?5" },
{ id: '6', src: "https://source.unsplash.com/600x400?6" },
{ id: '7', src: "https://source.unsplash.com/600x400?7" },
{ id: '8', src: "https://source.unsplash.com/600x400?8" },
]);
const handleMenuActive = idx => {
imgIdx.value = idx;
transType.value = idx > prevIdx.value ? "left" : "right";
}
const afterLeave = () => {
prevIdx.value = imgIdx.value;
}
return {
imgIdx,
slidList,
handleMenuActive,
afterLeave,
transType
}
}
};
</script>
<template>
<div class="content">
<div class="mid">
<transition-group
:name="transType"
@after-leave="afterLeave"
>
<img
v-for="(item, idx) in slidList"
v-show="imgIdx === idx"
:key="item.id"
:src="item.src"
/>
</transition-group>
</div>
<nav class="nav_menu">
<a
v-for="(item, idx) in slidList"
:key="item.id"
:class="{active: imgIdx === idx}"
@click="handleMenuActive(idx)"
>
{{idx + 1}}
</a>
</nav>
</div>
</template>
<style lang="scss">
.left-enter-active,
.left-leave-active,
.right-enter-active,
.right-leave-active {
transition: transform .3s ease;
}
.left-enter-from{
transform: translateX(-550px);
}
.left-leave-to{
transform: translateX(550px)
}
.right-enter-from{
transform: translateX(550px)
}
.right-leave-to{
transform: translateX(-550px);
}
// 其他省略...
</style>
codepen 范例 : https://codepen.io/MikeCheng1208/pen/eYRdzVL
所以我们在处理动画的时候不要再依赖 JQuery 的 animate 了,在使用 Vue 上面有更多好用的方式可以处理动态效果。
好啦 ! 今天篇幅也够长了,先介绍两个做法,明天我们在来讨论另外两个动画的处理方式。
Ps. 购买的时候请登入或注册该平台的会员,然後再使用下面连结进入网站点击「立即购课」,这样才可以让我获得更多的课程分润,还可以帮助我完成更多丰富的内容给各位。
我有开设了一堂专门针对Vue3从零开始教学的课程,如果你觉得不错的话,可以购买我课程来学习
https://hiskio.com/bundles/9WwPNYRpz?s=tc
那如果对於JS基础不熟的朋友,我也有开设JS的入门课程,可以参考这个课程
https://hiskio.com/bundles/b9Rovqy7z?s=tc
Mike 的 Youtube 频道
Mike的medium
MIke 的官方 line 帐号,好友搜寻 @mike_cheng
<<: [拯救上班族的 Chrome 扩充套件] 来说说文章走向和目标
>>: 【从零开始的Swift开发心路历程-Day4】Xcode介面基础介绍
上篇介绍了CSS Flex,这篇想来聊聊CSS grid到底是什麽东西 这里想先给大家一个观念: F...
我们需要使用 FRRouting,若还没安装的话,请先安装一下 这次使用的系统为 Ubuntu 20...
Debian/Ubuntu Linux都使用apt,升级时都是: apt-get update ap...
嗨大家我是Andy,今天来到了第二十九天,我们像以前整理一下这几天所学的,然後明天应该就是完赛心得了...
到这里,你已经建立一个具备完整对话体验的Action 也已经藉由GCP建构符合使用情境的架构了 接...