如何在一个区域内实现随机出现头像并替换

2,034 阅读4分钟

好久没写文章了,最近一直在想着写些实用性的逻辑文章。刚好在做公司的周年庆活动时,一个需求是 在一个地图区域内随机显示头像替换

代码已上传至 GitHub

国际惯例,先看下效果

DkN2dg.gif

这里的 需求点 有三个

  • 随机显示头像和大小和动画

  • 头像和头像间不能重叠

  • 可以一直轮流显示动画

准备工作

  • 准备一组随机的头像(上面的 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等分,这里用 cssflex 布局即可实现,这里不做描述。

DkXZ6J.png

然后只需将头像放入每个小方形内,这样就不会重叠了,如下图所示

DApo36.png

将一维数组组成二维数组

核心代码

/**
 * 将一维数组拆分成二维数组
 * @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);
}

这样我们就得到了一个完整的二维数组

DAVpn0.png

接下来,我们只要轮流的执行每一组的动画即可。

可以一直轮流显示动画

这里我们声明一个 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 都是不一样的,保证了随机性

DAnUzQ.png

视图上是这样的

DAuh9S.png

实行动画

上面已经实现了分布显示在不同区域的需求,最后只需添加动画即可。

这里通过 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);
  }
}

完成

DAKuDA.gif

其他

部分等逻辑在文中没有展示,可以在 GitHub 上直接看所有的源码,欢迎 star

看得不过瘾?这里还有别的文章