最终实现效果如上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条数据重新生成点位,循环直至数据消耗完。