前端实现蜂巢布局思路

效果图如下

上图的组成可以分为两部分,一个为底图六边形的形成,一个为内容六边形的形成。

要生成对应的六边形,首先要获得需要绘制的六边形的中心坐标。

观察不难得出结论,以中心的六边形为基点,第二圈很明显能排6个,第三圈能排12个,以此进行类推。

这里可以以中心点为坐标原点0,0,以1,0, 1,1,-1,1,-1,0,-1,-1,1,-1这种形式来表示第二圈在轴线上的六个六边形的中心点关系(这是一种形式,并非真实的坐标系坐标)。

js 复制代码
// 第二圈时的对应圆上的坐标值
const pixiArr = [
  [1, 0],
  [1, 1],
  [-1, 1],
  [-1, 0],
  [-1, -1],
  [1, -1]
]

// 根据圈数生成对应轴线上的坐标
function generatePixiArrByWeight(weight) {
  if (weight === 2) {
    return pixiArr
  }
  const multiple = weight - 1
  const tempPixiArr = pixiArr.map((x) => {
    return [x[0] * multiple, x[1] * multiple]
  })
  return tempPixiArr
}

进一步观察,可知,第三圈时两条发散的轴中间夹了一个六边形,第四圈时两条发散的轴中间夹了两个六边形,依次类推。

六条发散轴上的六边形中心点坐标是最容易计算的,不过要计算三圈及其开外的,就得有那么一点点的数学基础,知道sin60度cos60度的意思。

js 复制代码
const sin60 = Math.sin(Math.PI / 3)
const cos60 = Math.cos(Math.PI / 3)

有了上面的铺垫后就可以开始了,定义一个函数,传入的参数为六边形总个数和六边形的边长

js 复制代码
// 生成六边形中心坐标
function getHexagonCoordinateArrayByTotal(total = 0, radius = 0){
    // 1、获取圈数weight
    if (total === 0) return []
  let tierList = [] // 用于存放每圈的个数
  let tierIndex = 0
  while (total > 0) {
    if (tierIndex === 0) {
      tierList.push(1)
      total = total - 1
    } else {
      let n = 6 * tierIndex
      total = total - n
      if (total < 0) {
        tierList.push(total + n)
      } else {
        tierList.push(n)
      }
    }
    tierIndex++
  }
  const weight = tierList.length
    // 2、根据圈数去获取coordinateArray坐标列表
    // getHexagonCoordinateArrayByWeight:根据圈数和边长返回对应的坐标点
    const weight = tierList.length
  let coordinateArray = []
  for (let i = 0; i < weight; i++) {
    if (i + 1 === weight) {
      coordinateArray = [
        ...coordinateArray,
        ...getHexagonCoordinateArrayByWeight(i + 1, radius).slice(
          0,
          tierList[weight - 1]
        )
      ]
    } else {
      coordinateArray = [
        ...coordinateArray,
        ...getHexagonCoordinateArrayByWeight(i + 1, radius)
      ]
    }
  }

  return coordinateArray
}

有个getHexagonCoordinateArrayByWeight需要实现其,方式为

js 复制代码
function _abs(val = 0) {
  return Math.abs(val)
}

function getHexagonCoordinateArrayByWeight(weight = 1, radius = 0) {
  if (weight === 0) return []
  if (weight === 1) return [[0, 0]]
  const addNum = weight - 2
  const addArr = generatePixiArrByWeight(weight)
  const hypotenuse = radius * sin60 * 2 // 两倍的边心距长度
  let offsetArr = []
  let offsetX
  let offsetY
  for (let i = 0; i < addArr.length; i++) {
    const t = addArr[i]
    if (t[1] !== 0) {
      offsetX = t[0] * hypotenuse * cos60
      offsetY = t[1] * hypotenuse * sin60
    } else {
      offsetX = t[0] * hypotenuse
      offsetY = 0
    }
    offsetArr.push([offsetX, offsetY])
  }
  const tempOffsetArr = JSON.parse(JSON.stringify(offsetArr))
  let resArr = new Array(6 * (weight - 1))
  let lineArr = []
  for (let i = 0; i < 6; i++) {
    let lindex = i * (weight - 1)
    resArr[lindex] = tempOffsetArr[i]
    lineArr.push(lindex)
  }
  // 利用已知的六个发散轴上的中心坐标点推出剩余的中心坐标点
  if (addNum > 0) {
    for (let i = 0; i < 6; i++) {
      let s = tempOffsetArr[i]
      let e = i + 1 === 6 ? tempOffsetArr[0] : tempOffsetArr[i + 1]
      let si = lineArr[i]
      let sp = addNum + 1
      let fx
      let fy
      if (i === 0) {
        fx = (s[0] - e[0]) / sp
        fy = (e[1] - s[1]) / sp
      }
      if (i === 1) {
        fx = (_abs(s[0]) + _abs(e[0])) / sp
        fy = 0

      }
      if (i === 2) {
        fx = (_abs(e[0]) - _abs(s[0])) / sp
        fy = (_abs(s[1]) - _abs(e[1])) / sp
      }
      if (i === 3) {
        fx = (_abs(s[0]) - _abs(e[0])) / sp
        fy = (_abs(e[1]) - _abs(s[1])) / sp
      }
      if (i === 4) {
        fx = (_abs(s[0]) + _abs(e[0])) / sp
        fy = 0
      }
      if (i === 5) {
        fx = _abs(s[0]) / sp
        fy = (_abs(e[1]) - _abs(s[1])) / sp
      }
      let mr = []
      for (let j = 0; j < addNum; j++) {
        if (i === 0 || i === 1) {
          mr.push([s[0] - fx * (j + 1), s[1] + fy * (j + 1)])
        }
        if (i === 2) {
          mr.push([s[0] - fx * (j + 1), s[1] - fy * (j + 1)])
        }
        if (i === 3) {
          mr.push([s[0] + fx * (j + 1), s[1] - fy * (j + 1)])
        }
        if (i === 4) {
          mr.push([s[0] + fx * (j + 1), s[1] - fy * (j + 1)])
        }
        if (i === 5) {
          mr.push([s[0] + fx * (j + 1), s[1] - fy * (j + 1)])
        }
      }
      mr.forEach((x, index) => {
        resArr[si + index + 1] = x
      })
    }
  }
  return resArr
}

至此,生成六边形中心坐标点的方法完成。 有了中心坐标生成方式之后,就可以使用Konva这种辅助绘图的库来进行效果绘制了。

相关推荐
谭光志3 小时前
Vibe Coding 时代,程序员该何去何从
前端·后端·ai编程
仿生狮子5 小时前
别再说“全栈”了,AI 时代团队只认这 5 种人
前端·人工智能·后端
kyriewen5 小时前
AI 写的 React 组件,合并前必查的 6 个坏味道——每个都有代码对比
前端·javascript·react.js
Highcharts.js5 小时前
软件开发公司为什么选择 Highcharts?
前端·前端框架·echarts·数据可视化·技术选型·highcharts·图表工具
阿祖zu6 小时前
企业 AI 转型之痛,个人提效十倍不止,组织效率仍止步不前?
前端·aigc·agent
摇滚侠7 小时前
SpringBoot3+Vue3 全套视频教程 45-51
前端·javascript·vue.js
酸梅果茶7 小时前
【6】lightning_lm项目-LIO 前端 -点云预处理模块
前端·slam
Hilaku8 小时前
前端大裁员背后的恐慌:我们还剩什么底牌?
前端·javascript·程序员
咖啡无伴侣8 小时前
unplugin-auto-import 使用详解+面试高频考点
前端·架构
搬砖记录员8 小时前
录屏文件打不开?H.265兼容性踩坑与4种实战方案
前端