【vue2】实现css动效逐个顺序展示的效果(简陋版)
效果(进入预约里程碑模块后,小人从第一个台阶逐个闪烁出现在当前预约等级之前的台阶并消失,最终停留在当前预约等级的台阶上):
虽然很low但是!就是这么设计的!于是在原本静态的代码上稍加了些修改(为什么,为什么,想问天问大地)
首先是台阶部分的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | <div : class = "$style.reserveBox" > <ul : class = "$style.reserveList" > <li v- for = "(el, index) in list" :key= "index" : class = "[ $style.reserveItem, $style[`item_${index}`], active === index ? $style.active : '', anim === index ? $style.anim : '', activeGif === index ? $style.activeGif : '', ]" > <img :src= "active >= index ? list[index].active : list[index].default" : class = "$style.reserveItemImg" @click= "showPop(index)" /> </li> </ul> <div : class = "$style.reserveProgress" > <img src= "./images/progress.png" : class = "$style.reserveProgressBg" /> <div : class = "$style.reserveProgressLine" :style= "{ width: progress + '% !important' }" ></div> <img src= "./images/line_btn.png" : class = "$style.progress_30" v-show= "active >= 2" /> <img src= "./images/line_btn.png" : class = "$style.progress_50" v-show= "active >= 3" /> <img src= "./images/line_btn.png" : class = "$style.progress_100" v-show= "active >= 4" /> </div> <div : class = "$style.reserveDes" ></div> </div> |
每个台阶都是ul中的一个li元素,li中的img元素是柱子的状态图(分别是未达到和已达到的不同效果)(这不重要),动态的小人和对话框是用伪元素写的,样式代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | .reserveBox { position: absolute; bottom: 15.8%; left: 50%; transform: translateX(-50%); box-sizing: border-box; .reserveDes { position: absolute; right: 0; bottom: -0.76rem; width: 3.3rem; height: 0.25rem; .background-fill( "./images/reserve_des.png" ); } .reserveList { width: 100%; display: flex; justify-content: center; align-items: end; .reserveItem { position: relative; .reserveItemImg { width: 100%; } &.activeGif::before { .size(0.85rem, 1.08rem); .background-fill( "./images/man.png" ); content: "" ; display: block; position: absolute; top: 0; left: 50%; transform: translateX(-50%); z-index: 1; animation: jump 0.9s ease infinite; } &.activeGif::after { .size(1.34rem, 0.82rem); .background-fill( "./images/talk.png" ); content: "" ; display: block; position: absolute; top: 0; left: 44%; z-index: 1; animation: shake 1s ease infinite; } &.anim::before { .size(0.85rem, 1.08rem); .background-fill( "./images/man.png" ); content: "" ; display: block; position: absolute; top: 0; left: 50%; transform: translateX(-50%); z-index: 1; animation: anim 1.5s ease; animation-iteration-count: 1; } } .item_0 { width: 1.66rem; height: 1.746rem; &.activeGif::before { top: -0.25rem; } &.anim::before { top: -0.25rem; } &.activeGif::after { top: -0.75rem; } } .item_1 { width: 2.25rem; height: 2.5rem; margin-left: -0.12rem; &.activeGif::before { top: -0.54rem; } &.anim::before { top: -0.54rem; } &.activeGif::after { top: -1.1rem; } } .item_2 { width: 2.26rem; height: 3.77rem; margin-left: -0.01rem; &.activeGif::before { top: -0.3rem; } &.anim::before { top: -0.3rem; } &.activeGif::after { top: -0.8rem; } } .item_3 { width: 2.4rem; height: 4.79rem; margin-left: -0.08rem; &.activeGif::before { top: -0.04rem; } &.anim::before { top: -0.04rem; } &.activeGif::after { top: -0.46rem; } } .item_4 { width: 2.4rem; height: 6.17rem; margin-left: -0.01rem; &.activeGif::before { top: 0.6rem; } &.anim::before { top: 0.6rem; } &.activeGif::after { top: 0.1rem; } } } .reserveProgress { position: relative; width: 10.8rem; height: 0.94rem; z-index: 1; .reserveProgressBg { width: 100%; height: 100%; } .reserveProgressLine { background-image: url( "./images/line_only.png" ); background-position: left top; background-size: 8.92rem 0.72rem; background-repeat: no-repeat; position: absolute; display: inline-block; left: 0.26rem; top: 0.1rem; width: 8.92rem; height: 0.72rem; z-index: 1; } .progress_30 { position: absolute; top: 0.054rem; left: 41%; height: 0.72rem; width: auto; z-index: 2; } .progress_50 { position: absolute; top: 0.054rem; left: 63%; height: 0.72rem; width: auto; z-index: 2; } .progress_100 { position: absolute; top: 5.4px; left: 85%; height: 0.72rem; width: auto; z-index: 2; } } } |
css3效果代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | @keyframes shake { 10% { transform: rotate(15deg); } 20% { transform: rotate(-10deg); } 30% { transform: rotate(5deg); } 40% { transform: rotate(-5deg); } 50%, 100% { transform: rotate(0deg); } } @keyframes jump { 10% { transform: translate(-50%, 2px); } 100% { transform: translate(-50%, 0); } } @keyframes anim { 0% { opacity: 1; } 40% { opacity: 0; } 70% { opacity: 1; } 100% { opacity: 0; } } |
由于是在原本静态的代码上删删改改,所以只是一个简陋的方案,思路就是在data中配置3个值作为控制:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | showAni() { const _this = this ; clearTimeout( this .timer); let count = 0; const length = this .list.filter((_, index) => index < this .active).length; function changeContent() { if (count < length) { _this.anim = count; count += 1; _this.timer = setTimeout(changeContent, 1500); } else { _this.anim = -1; clearTimeout( this .timer); if (count != -1 && count === length) { _this.activeGif = _this.active; } } } if (count < length) { changeContent(); } }, |
这个方法就是实现小人整体动画的核心。
在watch中监听当前是否处在里程碑这一屏:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | watch: { activePage(newVal) { if (newVal && newVal === 1) { this .$nextTick(() => { clearTimeout(); setTimeout(() => { this .showAni(newVal); }, 500); }); } else { this .$nextTick(() => { this .anim = -1; this .activeGif = -1; }); } }, }, |
这个activePage是父组件传来的,当对应到里程碑的index时,清除定时,并在500ms后执行动画,这个延时是为了保证不在用户刚进入这一屏时就已经开始执行第一次闪烁,视觉效果太仓促。同时,当当前没有展示里程碑这一屏时,设置anim和activeGif为初始值-1,保证当前没有元素处在动效中。
在执行showAni方法时,定义了一个当前count为0,也就是从第一个li的动效开始展示,我们的闪烁动效只需要展示到当前阶段之前就可以停了,所以count+1的操作只执行到当前阶段之前的index,待count+1后等于当前阶段的index,就给当前阶段的元素执行activeGif动画,也就是上下跳动+对话框,动效也就至此完成了。
做完这个之后我突然觉得,是不是做一个小人从第一个逐个跳到当前阶段的台阶还更简单点??可恶啊一个官网能不能少搞点这种花里胡哨的登西!做个gif拿来展示多好!(毕竟做完之后看了眼浏览器内存用量竟高达250mb,真的有点牛)
吐槽完毕,记录一下,方便以后copy嘻嘻
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
2022-10-12 React++antd+ProComponents可编辑表格EditableProTable组件实现表单中的可编辑列表组件