共计 3541 个字符,预计需要花费 9 分钟才能阅读完成。
1. 前言
以前在看微信视频号直播的时候,常常点击右下角的点赞按钮。看着它的数字缓缓从一位数变成五位数,还是挺有气氛感的。特地是长按的时候,有个手机触动的反馈,很带感。
尽管之前很好奇这些飘动的点赞动效是怎么实现的,但没有特地去钻研。直到前阵子投入腾讯课堂 H5 直播间的需要,须要本人去实现一个这样的成果时,才开始摸索。
先看看最初的成果:
相比视频号的点赞动效,轨迹简单了很多。能够看到课堂直播间的这一段点赞动效,大略分为这么三个阶段:
1. 从无到有,在回升过程中放大成失常大小
2. 回升过程中左右摇曳,且摇曳的幅度随机
3. 左右摇曳回升的过程中,渐隐并放大
在入手之前,我先想到了应用 CSS animation 去实现这种静止轨迹。在实现之后,又用 Canvas 重构了一版,优化了性能。
接下来咱们别离来看看这两种实现形式。
2. CSS 实现点赞动效
2.1 轨迹剖析
因为点赞动画是在一个二维立体上的,咱们能够将它的静止轨迹拆分为 x 轴 和 y 轴 上的两段。
在 y 轴 上非常简单,咱们的点赞图标会做一段垂直回升的匀速运动,从容器底部回升到容器顶部。
而 x 轴 上是左右摇曳的,用数学的角度说,是一段简谐运动。
但用 css 实现的时候,其实不必这么精密。为了简化计算,咱们能够用几个关键帧来串联这段静止轨迹,例如:
@keyframes bubble_swing {
0% {两头}
25% {最左}
75% {最右}
100% {两头}
}
2.2 轨迹设计
依据下面的剖析,咱们能够设计一段雷同的回升轨迹,以及几段不同的左右摇曳轨迹。
回升轨迹很简略,同时咱们还能够加上透明度(opacity)、大小(transform)的变动,如下:
@keyframes bubble_y {
0% {transform: scale(1);
margin-bottom: 0;
opacity: 0;
}
5% {transform: scale(1.5);
opacity: 1;
}
80% {transform: scale(1);
opacity: 1;
}
100% {margin-bottom: var(--cntHeight);
transform: scale(0.8);
opacity: 0;
}
}
其中,–cntHeight 指的是容器的高度。也就是说,咱们通过让 margin-bottom 一直增大,来控制点赞图标从容器底部回升到容器顶部。
而对于横向静止的轨迹,为了减少静止轨迹的多样性,咱们能够设计多段左右摇曳的轨迹,比如说一段“两头 -> 最左 -> 两头 -> 最右”的轨迹:
@keyframes bubble_swing_1 {
0% {
// 两头
margin-left: 0;
}
25% {
// 最左
margin-left: -12px;
}
75% {
// 最右
margin-left: 12px;
}
100% {margin-left: 0;}
}
这里同样应用 margin 来管制图标的左右挪动。相似的,咱们还能够设计几段别的轨迹:
// 任意轨迹
@keyframes bubble_swing_2 {
0% {
// 两头
margin-left: 0;
}
33% {
// 最左
margin-left: -12px;
}
100% {
// 随机地位
margin-left: 6px;
}
}
// 简谐反向
@keyframes bubble_swing_3 {
0% {
// 两头
margin-left: 0;
}
25% {
// 最右
margin-left: 12px;
}
75% {
// 最左
margin-left: -12px;
}
100% {margin-left: 0;}
}
接下来咱们把 x 轴 和 y 轴 的轨迹(@keyframes)联合起来,并设置一个随机的动画工夫,比如说:
@for$i from 1 through 3 {
@for$j from 1 through 2 {.bl_#{$i}_#{$j} {animation: bubble_y calc(1.5s + $j * 0.5s) linear 1 forwards,
bubble_swing_#{$i} calc(1.5s + $j * 0.5s) linear 1 forwards;
}
}
}
这里生成了 3 * 2 = 6 种不同的轨迹。针对这类反复的选择器,用 SCSS 中的循环语法,能够少写很多代码。
2.3 随机抉择图片(雪碧图)
咱们每次点赞会呈现不同的图标,于是这里设计了一系列选择器给不同的图标,让它们出现不同的图片。首先咱们要筹备一张雪碧图,放弃所有图标的大小统一,而后同样应用 SCSS 的循环语法:
@for$i from 0 through 7 {.b#{$i} {background: url('../../images/like_sprites.png') calc(#{$i} * -24px) 0;
}
}
像下面生成了 8 个选择器,咱们在程序执行时就能够随机给图标赋予一个选择器。
2.4 生成一个点赞图标
CSS 的局部差不多了,咱们当初来看 JS 是怎么执行的。咱们须要有一个容器 div,让它来装载要生成的点赞图标。以及一个按钮来绑定点击事件:
const cacheRef = useRef<LikeCache>({
bubbleCnt: null,
likeIcon: null,
bubbleIndex: 0,
timer: null,
});
useEffect(() => {cacheRef.current.bubbleCnt = document.getElementById('like-bubble-cnt');
cacheRef.current.likeIcon = document.getElementById('like-icon');
}, []);
在点击事件中,生成一个新的 div 元素,并为它设置 className。接着将它 append 到容器下,最初在一段时间后销毁这个点赞图标元素。如下:
/**
* 增加 bubble
*/
const addBubble = () => {const { bubbleCnt} = cacheRef.current;
cacheRef.current.bubbleIndex %= maxBubble;
const d = document.createElement('div');
// 图片类 b0 - b7
// 随机动画类 bl_1_1 - bl_3_2
const swing = Math.floor(Math.random() * 3) + 1;
const speed = Math.floor(Math.random() * 2) + 1;
d.className = `like-bubble b${cacheRef.current.bubbleIndex} bl_${swing}_${speed}`;
bubbleCnt?.appendChild(d);
cacheRef.current.bubbleIndex++;
// 动画完结后销毁元素
setTimeout(() => {bubbleCnt?.removeChild(d);
}, 2600);
};
到这里,咱们就实现得差不多了。不过,咱们还能够给点击的图标加点动画,让它有一个被按压后弹起的成果:
/**
* 点击“喜爱”*/
const onClick = () => {const { timer, likeIcon} = cacheRef.current;
if (!likeIcon) {return;}
if (timer) {clearTimeout(timer);
cacheRef.current.timer = null;
}
likeIcon.classList.remove('bounce-click');
// 删除并从新增加类,须要提早增加
setTimeout(() => {likeIcon.classList.add('bounce-click');
}, 0);
cacheRef.current.timer = window.setTimeout(() => {likeIcon.classList.remove('bounce-click');
clearTimeout(timer!);
cacheRef.current.timer = null;
}, 300);
addBubble();};
2.5 最终成果
最初来看看成果吧!
明天就到这, 下期给大家分享 canvas 实现点赞动效的形式
❤️欢送大家关注我, 文章小白上路, 你们是我持续整顿分享的能源❤️
❤️公众号: 前端别搞我❤️
❤️关注 + 点赞珍藏 + 评论 + 分享❤️,手留余香,谢谢🙏大家。