前端实现随机点位生成(附带完整代码)

99 阅读2分钟

最终实现效果如上DEMO(点点看)

需求简述

以随机点位的形式,渲染一组数据,要求互不重叠

需求分析

前端获取数据会是一个数组,或者一个数字代表长度,重点是如何计算出对应的点位和过饱和情况的处理

  • 首先定义渲染区域,我这里是以容器中心为圆心,以容器宽高小值的40%为半径的圆的区域为点位渲染区域;
    const { clientWidth, clientHeight } = contentDOM
    const size = Math.min(clientWidth, clientHeight)
    const radius = size * 0.4
    
  • 在此区域中以1-4象限为顺序,生成随机弧度
    // 四个象限 依次出现
    let quadrant = index % 4 
    // 随机弧度
    let angle = (Math.random() + quadrant) * Math.PI / 2
    
  • 生成随机半径,配合上一步的随机弧度即可计算出一个随机点位。
    // 随机半径
    let randomRadius = radius * Math.random()
    // 随机点位
    let position = { 
      x: Math.cos(angle) * randomRadius, 
      y: -Math.sin(angle) * randomRadius 
    }
    
  • 将刚刚生成的点位与已经缓存的点位进行差异化比较,判断是否存在重叠,存在则重新计算,否则缓存。
    let checked = !res.find(
      ({ x, y }) => 
        (Math.abs(position.x - x) < safeSize) 
        && 
        (Math.abs(position.y - y) < safeSize)
    )
    if (checked) return position
    return check(index)
    
  • 点位数据与业务数据整合,并渲染到页面上。
  • 补充动画效果
    .animation(@name, @rules) {
      @keyframes @name {
        @rules();
      }
    }
    .animation(scale-in, {
        from {scale: 0;}
        to {scale: 1;}
      }
    );
    .animation(point-move, {
        from {transform: translateY(0);}
        to {transform: translateY(-20%);}
      }
    );
    
    @maxCount: 15;
    
    .loopPoint(@i) when (@i >=0) {
        &.tip@{i} {
          // 出现动画 + 常驻动画
          animation:
              scale-in .3s ease-out @i * 0.2s forwards,
              point-move 2s + (@i + 1) * 0.1s infinite alternate;
    
          &.received {
            top: -100vh !important;
          }
        }
    
        .loopPoint(@i - 1);
    }
    .loopPoint(@maxCount - 1);
    

此方法生成的点位,大抵呈现为圆形,每个象限点位分布均匀,越接近圆心越密集,但不会重叠。

如果空间过小,不足支持最后生成15个点位会怎样

如果空间过小,循环计算100次也无法算满满足条件的15个,那么将已计算出来的渲染出来就行,在页面交互处理完成后,再次触发计算,与未绑定点位的数据再做绑定。

比如:接口返回100个点,前端定义生成50个点,实际前端运算只生成了30个点。这时候页面会渲染30个点可供用户点击。当用户点击完30个能量点后,用剩余未绑定点位的70条数据重新生成点位,循环直至数据消耗完。