一,过渡动画的实现
1,无过渡,直接变化
<template> <div class="testBox"> <div class="testCon" :style="{ width: width + 'px' }"></div> <div class="testBtn" @click="clickBtn">点击按钮</div> </div> </template> <script setup> import {
ref } from 'vue'; const width = ref(10); const clickBtn = () => (width.value += 100); </script> <style lang="scss" scoped> .testBox {
overflow: hidden; height: 100vh; padding: 100px 50px; box-sizing: border-box; .testCon {
height: 200px; background: pink; } .testBtn {
margin-top: 10px; height: 60px; line-height: 60px; border: 1px solid #35aeff; background: #35aeff; border-radius: 10px; } } </style>
为了好讲述,只看一次点击。也就是开始时width:10px,结束时width:110px。在没有设置过渡的时候,变化是突兀的,生硬的。如下图:
2,设置过渡,设置时间和速率
过渡的实现。可以理解为:记录起始状态,然后添加一个从开始到结束的时间和速度。
主要就是利用css3的transition来设置。需要监听谁的属性变化,就写在谁的样式中:
.testCon {
height: 200px; background: pink; transition: width 2s linear;//监听这个元素的width属性的变化, }
于是就会监听这个width属性,从开始到结束的变化,会被拉长到2s来线性变化。
当想要监听该元素所有元素的变动的时候,只要把属性改成all即可:
<template> <div class="testBox"> <div :class="['testCon', btnClick ? 'btnClick' : '']"></div> <div class="testBtn" @click="clickBtn">点击按钮</div> </div> </template> <script setup> import {
ref } from 'vue'; const btnClick = ref(false); const clickBtn = () => {
btnClick.value = true; }; </script> <style lang="scss" scoped> .testBox {
overflow: hidden; height: 100vh; padding: 100px 50px; box-sizing: border-box; .testCon {
width: 100px; height: 100px; background: pink; } .btnClick {
height: 200px; width: 200px; background: red; transition: all 2s linear; } .testBtn {
margin-top: 10px; height: 60px; line-height: 60px; border: 1px solid #35aeff; background: #35aeff; border-radius: 10px; } } </style>
主要是从这个样式:
.testCon {
width: 100px; height: 100px; background: pink; }
变成:
.btnClick {
height: 200px; width: 200px; background: red; transition: all 2s linear; }
这样一来,就会给该元素所有变动的属性增加过渡变化的效果:
也就是说,过渡主要是transition这个css样式产生的效果,
transition :transition-property(执行变换的属性) || transition-duration(执行变换的时间) || transition-timing-function(执行变换的速率) || transition-delay(延迟变换的时间);
transition-property(执行变换的属性)
transition-property | 可取的值 |
---|---|
none | 没有属性会获得过渡效果 |
all | 所有属性都将获得过渡效果,也是其默认值 |
property | CSS 属性名称列表,逗号隔开 |
transition-duration(执行变换的时间
过渡的持续时间,取值time为数值,单位为s(秒)或者ms(毫秒),其默认值是0,也就是变换时是即时的。
transition-timing-function(执行变换的速率)
值 | 描述 |
---|---|
linear | 相同速度开始至结束 |
ease | 慢速开始,然后变快,然后慢速结束 |
ease-in | 慢速开始 |
ease-out | 慢速结束 |
ease-in-out | 慢速开始和结束 |
cubic-bezier(n,n,n,n) | 在 cubic-bezier 函数中定义自己的值。可能的值是 0 至 1 之间的数值。 |
transition-delay(延迟变换的时间)
二,animation 和 keyframe 的组合实现动画
过渡动画,只能定义首尾两个状态。而animation可以自定义多个帧的状态之间的过渡效果。从而实现真正的动画。
1,animation的取值
值 | 描述 |
---|---|
animation-name | 规定需要绑定到选择器的 keyframe 名称 |
animation-duration | 规定完成动画所花费的时间,以秒或毫秒计 |
animation-timing-function | 规定动画的速度曲线 |
animation-delay | 规定在动画开始之前的延迟,默认值为0 |
animation-iteration-count | 规定动画应该播放的次数,默认值为1 |
animation-direction | 规定是否应该轮流反向播放动画,默认值是正向 |
2,keyframe
@keyframes 就是让程序员设置一定序列的动画帧,然后提供给animation使用的。
使用方法:
当使用百分比用法时:
@keyframes 动画名字{
0% {
top: 0; left: 0px} 50% {
top: 30px; left: 20px; } 100% {
top: 0; left: 30px;} }
使用的方法如下:
<template> <div class="testBox"> <div class="testCon"></div> </div> </template> <script setup></script> <style lang="scss" scoped> .testBox {
overflow: hidden; height: 100vh; padding: 100px 50px; box-sizing: border-box; .testCon {
animation: testAni 3s ease-in-out infinite; } } @keyframes testAni {
0% {
width: 50px; height: 50px; background: #cd4a48; border-radius: 50px; } 50% {
width: 100px; height: 100px; background: #e72365; border-radius: 0; } 100% {
width: 50px; height: 50px; background: #04aef1; border-radius: 50px; } } </style>
实现的效果:
当使用from-to用法时:
@keyframes preloader {
from {
transform: rotate(0deg);//其实这里写的就是支持过渡动画的属性啦 } to {
transform: rotate(360deg); } }
不同于transition,使用animation自动触发,无需事件触发。
三,transform变换
在制作动画的时候,常常要使用到属性变化。transform就是变形,主要包括旋转rotate、扭曲skew、缩放scale和移动translate以及矩阵变形matrix。
transform: none || transform-functions
none:表示不进么变换;transform-function表示一个或多个变换函数,用空格分开。
transform-function变换函数有以下几种:
1,旋转rotate
通过指定的角度参数对原元素指定一个2D rotation(2D 旋转),需先有transform-origin属性的定义(默认dom元素的中心)。transform-origin定义的是旋转的基点,其中angle是指旋转角度,如果设置的值为正数表示顺时针旋转,如果设置的值为负数,表示逆时针旋转。 如:transform:rotate(30deg):
<template> <div class="testBox"> <div class="testCon"></div> </div> </template> <script setup></script> <style lang="scss" scoped> .testBox {
overflow: hidden; height: 100vh; padding: 100px 50px; box-sizing: border-box; .testCon {
width: 100px; height: 100px; animation: testAni 3s ease-in-out infinite; } } @keyframes testAni {
0% {
background: #04aef1; border-radius: 50px; } 50% {
background: #e72365; border-radius: 0; transform: rotate(180deg); } 100% {
background: #04aef1; border-radius: 50px; } } </style>
2,移动translate
移动translate我们分为三种情况:translate(x,y)水平方向和垂直方向同时移动(也就是X轴和Y轴同时移动);translateX(x)仅水平方向移动(X轴移动);translateY(Y)仅垂直方向移动(Y轴移动),如果是负值,则是反方向移动,当然,也可以用transform-origin来控制基准。
@keyframes testAni {
0% {
background: #04aef1; border-radius: 50px; } 50% {
background: #e72365; border-radius: 0; transform: rotate(180deg); transform: translate(50px, 50px); } 100% {
background: #04aef1; border-radius: 50px; } }
实现的便是这样的效果:
3,scale缩放
scale(x,y)使元素水平方向和垂直方向同时缩放(也就是X轴和Y轴同时缩放);scaleX(x)元素仅水平方向缩放(X轴缩放);scaleY(y)元素仅垂直方向缩放(Y轴缩放),但它们具有相同的缩放中心点和基数,其中心点就是元素的中心位置,缩放基数为1,如果其值大于1元素就放大,反之其值小于1,元素缩小。同样的,它可以用transform-origin来控制基准。
50% {
background: #e72365; border-radius: 0; transform: scale(1.5, 2); }
4,扭曲skew
扭曲skew和translate、scale一样同样具有三种情况:skew(x,y)使元素在水平和垂直方向同时扭曲(X轴和Y轴同时按一定的角度值进行扭曲变形);skewX(x)仅使元素在水平方向扭曲变形(X轴扭曲变形);skewY(y)仅使元素在垂直方向扭曲变形(Y轴扭曲变形)。
50% {
background: #e72365; border-radius: 0; transform: skewX(30deg); }
单个的动画看起来有些死板,但是多种结合在一起,就会有神效:
<div id="preloader"> <span></span> <span></span> <span></span> <span></span> </div> #preloader {
position: relative; width: 42px; height: 42px; animation: preloader 5s infinite linear; } #preloader span {
width: 20px; height: 20px; position: absolute; background: red; // display: block; animation: preloader_6_span 1s infinite linear; } #preloader span:nth-child(1) {
background: #2ecc71; } #preloader span:nth-child(2) {
left: 22px; background: #9b59b6; animation-delay: 0.2s; //延迟0.2s后开始动画 } #preloader span:nth-child(3) {
top: 22px; background: #3498db; animation-delay: 0.4s; //延迟0.4s后开始动画 } #preloader span:nth-child(4) {
top: 22px; left: 22px; background: #f1c40f; animation-delay: 0.6s; //延迟0.6s后开始动画 } @keyframes preloader {
from {
transform: rotate(0deg); } to {
transform: rotate(360deg); } } @keyframes preloader_6_span {
0% {
transform: scale(1); } 50% {
transform: scale(0.5); } 100% {
transform: scale(1); } }
四,vue中的动画效果
1,普通的过渡动画
Vue 3 中提供了一些动画的封装,使用内置的 transition 组件来控制组件的动画。
按照官网的说法,就是被transition包裹的组件,会有这六种class的切换:
1,v-enter-from:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。 2,v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。 3,v-enter-to:定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter-from 被移除),在过渡/动画完成之后移除。 4,v-leave-from:定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。 5,v-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。 6,v-leave-to:离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave-from 被移除),在过渡/动画完成之后移除。
具体 class 的名字,Vue 的官网有一个图给出了很好的解释,图里的 v-enter-from 中的 v,就是我们设置的 name 属性。
实际上,这个过程就是过渡动画的体现。当元素进入的那一帧,会添加v-enter-from的class,设置了初始状态。而v-enter-to则是元素开始进入到完成会添加的class,用来设置元素过渡的结束状态。v-enter-active则是整个过渡时间内会存在的class。
也就是上面那张图实际上是这样的:
于是,v-enter-active和v-leave-active就可以被用来定义进入过渡的过程时间,延迟和曲线函数。因为这两个类在过渡的时间内一直存在,过渡完成后才删除。
而v-enter-from和v-leave-to则可以用来设置动画的开始和结束。
另外两个可以不使用,而用元素自身的class替代:
<template> <div id="demo"> <button @click="data.noActivated = !data.noActivated">Toggle</button> <transition name="fade"> <p v-if="data.noActivated" class="test">hello</p> </transition> </div> </template> <script setup> import {
reactive } from "vue"; const data = reactive({
noActivated: false }); </script> <style lang="scss" scoped> .test {
color: green; opacity: 1; } .fade-enter-active, .fade-leave-active {
transition: all 5s ease; } .fade-enter-from, .fade-leave-to {
color: black; opacity: 0; } </style>
实现的效果:
也就是在元素渲染的那段动画中:
1,fade-enter-from设置了初始状态 2,fade-enter-active设置了过渡的动画和时间 3,元素本身的class:test充当设置了最终状态。
1,元素本身的class:test充当设置了初始状态。 2,fade-leave-active设置了过渡的动画和时间 3,fade-leave-to设置了最终状态。
2,列表的进入/离开过渡动画
transition 元素作为单个元素/组件的过渡效果。transition 只会把过渡效果应用到其包裹的内容上,而不会额外渲染 DOM 元素,也不会出现在可被检查的组件层级中。它的这六个class就是放在这个子元素上的。所以列表渲染就不用这个,而是使用transition-grou来包裹,至于六种class则是添加在item上。用法和上文说的一样,我习惯用元素本身的class,来取代v-enter-to和v-leave-from。所以没设置这两个。
<template> <div id="demo"> <button @click="clickBtn">增加按钮</button> <transition-group name="fade" tag="ul"> <li v-for="(item, index) in data.testList" :key="index" class="test"> {
{
item.content }} </li> </transition-group> </div> </template> <script setup> import {
reactive } from "vue"; const data = reactive({
testList: [ {
content: "第一个" } ] }); function clickBtn() {
data.testList.push({
content: "下一个" }); } </script> <style lang="scss" scoped> .test {
color: red; font-size: 18px; } .fade-enter-active, .fade-leave-active {
transition: all 2s ease; } .fade-enter-from, .fade-leave-to {
color: black; opacity: 0; transform: translateX(30px); } </style>
3,js动画配合transition
<template> <div class="title"> <input type="text" @keypress.enter="add" v-model="title" /> <button @click="clear">清理</button> </div> <div class="content"> <transition-group name="flip-list" tag="ul"> <li v-for="(item, index) in todoList" :key="item.content"> <input type="checkbox" v-model="item.done" /> <span :class="[item.done ? 'inactive' : 'active']"> {
{
item.content }} </span> <span class="remove-btn" @click="removeTodo($event, index)">❌</span> </li> </transition-group> </div> <span class="dustbin">🗑</span> <div class="animate-wrap"> <transition @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter" > <div class="animate" v-show="animate.show">📋</div> </transition> </div> </template> <script setup> import {
ref, reactive } from "vue"; //把这个useTodo解耦出来 function useTodos() {
let title = ref(""); let todoList = ref([ {
content: "五湖四海皆一望", done: false }, {
content: "千江有水千江月", done: true } ]); function add() {
const obj = {
content: title.value, done: false }; todoList.value.push(obj); title.value = ""; } function clear() {
todoList.value = todoList.value.filter((v) => !v.done); } return {
title, todoList, add, clear }; } //其实你可以把组件内部的任何一段代码,从组件文件里抽离出一个独立的文件进行维护。 //再在这个组件中使用,于是这些东西就不依赖于this上下文了,这里再解构赋值出来 let {
title, todoList, add, clear } = useTodos(); const {
animate, beforeEnter, enter, afterEnter, removeTodo } = useAnimation(); function useAnimation() {
let animate = reactive({
show: false, el: null }); const dustbin = {
el: null, pos: [], init(queryStr) {
this.el = document.querySelector(queryStr); this.getPos(); }, getPos() {
const {
left, top } = this.el.getBoundingClientRect(); this.pos[0] = left; this.pos[1] = top; } }; function beforeEnter(el) {
let dom = animate.el; let rect = dom.getBoundingClientRect(); const aniEl = document.querySelector(".animate"); //动画元素 调整到dustbin的位置,也可以css直接写精准位置 aniEl.style.left = `${
dustbin.pos[0]}px`; aniEl.style.top = `${
dustbin.pos[1]}px`; //计算并赋值偏移量 let dx = dustbin.pos[0] - rect.left; let dy = dustbin.pos[1] - rect.top; el.style.transform = `translate(-${
dx}px, ${
dy * -1}px)`; } function enter(el, done) {
document.body.offsetHeight; el.style.transform = `translate(0,0)`; el.addEventListener("transitionend", done); } function afterEnter(el) {
animate.show = false; el.style.display = "none"; } function removeTodo(e, i) {
animate.el = e.target; animate.show = true; todoList.value.splice(i, 1); dustbin.init(".dustbin"); } return {
animate, beforeEnter, enter, afterEnter, removeTodo }; } </script> <style lang="scss" scoped> .active {
color: v-bind(color); } .flip-list-move {
transition: transform 0.8s ease; } .flip-list-enter-active, .flip-list-leave-active {
transition: all 1s ease; } .flip-list-enter-from, .flip-list-leave-to {
opacity: 0; transform: translateX(30px); } .dustbin {
position: absolute; top: 0; right: 0; } .animate-wrap .animate {
position: fixed; right: 10px; top: 10px; z-index: 100; transition: all 0.5s linear; } </style>
实现的效果:
版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/qdvuejs/11044.html