好久没写文章了,最近一直在想着写些实用性的逻辑文章。刚好在做公司的周年庆活动时,一个需求是 在一个地图区域内随机显示头像替换
代码已上传至 GitHub
国际惯例,先看下效果
这里的 需求点 有三个
-
随机显示头像和大小和动画
-
头像和头像间不能重叠
-
可以一直轮流显示动画
准备工作
- 准备一组随机的头像(上面的 GitHub 上有)
React
或者Vue
,这个随便,主要讲述思路,用什么都没所谓,这里作者使用的是React
。
实现思路
假设我获取了 100
个用户头像,将其分为 100 / 10 = 10
组二维数组,每次执行动画的时候取出其中一组执行,执行完毕后执行下一组,一直执行到最后一组的时候返回第一组执行,实现需求。
随机显示头像和大小和动画
鉴于我们获取到的头像列表往往只有关键的一个图片地址
属性,我们首先需要对头像的数组遍历,为其每个属性添加随机的宽
、高
、位置
、动画延迟时间
核心代码
/**
* 获取 min 到 max 的随机数
* @param {number} min default 1
* @param {number} max
*/
export const getRandomInt = (min: number = 1, max: number) => {
return Math.floor(Math.random() * (max - min + 1) + min);
};
/**
* 添加随机的宽、高、位置、动画延迟时间属性到头像列表内
* @param {Array<IAvatar>} arr
*/
const addAvatarAttributes = (arr: Array<IAvatar>) => {
return arr.map((item) => {
// 盒子的随机宽高
let whRandom = getRandomInt(30, 50);
// 盒子的位置
let positionRandom = getRandomInt(1, 78);
// 盒子的动画执行 delay 时间
let delayRandom = parseFloat((Math.random() * Math.floor(1)).toFixed(1));
// 返回一个新的头像对象
return {
...item,
width: whRandom,
height: whRandom,
left: `${positionRandom}%`,
top: `${positionRandom}%`,
delay: `${delayRandom}s`,
};
});
};
这样我们每个头像都有属于自己的宽
、高
、位置
、动画延迟时间
,第一步完成。
头像和头像间不能重叠
这里我的思路是划分一块长方形,将其分为 4 x 3
等分,这里用 css
的 flex
布局即可实现,这里不做描述。
然后只需将头像放入每个小方形内,这样就不会重叠了,如下图所示
将一维数组组成二维数组
核心代码
/**
* 将一维数组拆分成二维数组
* @param {Array<IAvatar>} 一维数组
* @param {number} 二维数组内,每个数组的长度,这里默认是 12 个
*/
const divideInToTDArray = (arr: Array<IAvatar>, ODLength: number = 12) => {
let TDArray: Array<Array<IAvatar>> = [];
// 如果一维数组总长度小于二维数组单个数组的长度
if (arr.length < ODLength) {
TDArray = [arr];
} else {
// 根据一维数组的长度放入一个新数组,向下取整
const length = Math.floor(arr.length / ODLength);
for (let i = 0; i < length; i++) {
TDArray.push(arr.slice(i * ODLength, (i + 1) * ODLength));
}
}
// 得到二维数组后,执行动画
animationPlay(TDArray, ODLength);
}
这样我们就得到了一个完整的二维数组
接下来,我们只要轮流的执行每一组的动画即可。
可以一直轮流显示动画
这里我们声明一个 playIndex
来指定目前 选中的一维数组 ,为了确保随机性,我们将 选中的一维数组 进行一次 重新排序 ,再次排序后的数组就是我们需要展示的 头像数组
核心代码
/**
* 定时循环数组动画
* @param {Array<Array<IAvatar>>} TDArray 二维数组
* @param {number} 一维数组的长度
*/
const animationPlay = (TDArray: Array<Array<IAvatar>>, ODLength: number) => {
// 选择要播放的数组的索引
let playIndex = 0;
// 定时循环数组动画
interval = window.setInterval(() => {
let ODArray = Array(ODLength).fill("");
setAvatarList(ODArray);
// 如果没执行到最后一组的时候
if (playIndex < TDArray.length) {
randomPushArr(TDArray[playIndex], ODLength);
playIndex++;
} else {
playIndex = 0;
randomPushArr(TDArray[playIndex], ODLength);
}
}, duration);
};
/**
* 随机插入到视图的一维数组内
* @param {Array<IAvatar>} arr 你选中的数组
* @param {number} 一维数组的长度
*/
const randomPushArr = (arr: Array<IAvatar>, ODLength: number) => {
let ODArray = Array(ODLength).fill("");
arr.forEach((item) => {
// 忽略掉会覆盖的可能性,这样反而会保证每次展示的数组长度不一,增加了随机性
const index = getRandomInt(1, ODLength - 1);
ODArray[index] = item;
});
// 设置最新的头像数组到视图内
setAvatarList(ODArray);
};
下图就是要设置的 头像数组 ,可以看出,每次输出内的 index
都是不一样的,保证了随机性
视图上是这样的
实行动画
上面已经实现了分布显示在不同区域的需求,最后只需添加动画即可。
这里通过 React
的重新 render
,当我每次设置新的 数组 到视图的时候,就会重新执行一遍样式的加载,作者利用这个性质,直接在 img
上添加一个 animation
的动画实现,部分 css
如下
.avatar-show {
opacity: 0;
animation: ShowHideAvatar 3s ease-out both;
}
@keyframes ShowHideAvatar {
0% {
transform: scale(0.4);
opacity: 0;
}
5% {
transform: scale(0.7);
opacity: 1;
}
10% {
transform: scale(1.16);
opacity: 1;
}
15%,
90% {
transform: scale(1);
opacity: 1;
}
95% {
transform: scale(1.06);
}
100% {
opacity: 0;
transform: scale(0);
}
}
完成
其他
部分等逻辑在文中没有展示,可以在 GitHub 上直接看所有的源码,欢迎 star
看得不过瘾?这里还有别的文章