【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个值作为控制:

anim:用来控制当前哪个元素需要闪烁出现并消失
active:当前预约等级
activeGif:当前需要展示小人跳动和对话框的预约等级(也就是动效结束后小人停留在哪个预约阶段的台阶上)
这三个元素的取值均由0开始,对应li遍历的index。初始值-1,不展示任何效果。
同时,还需要定义一个timer初始值伪null。
写一个方法:
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嘻嘻

 

posted @   芝麻小仙女  阅读(219)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
历史上的今天:
2022-10-12 React++antd+ProComponents可编辑表格EditableProTable组件实现表单中的可编辑列表组件
点击右上角即可分享
微信分享提示