前端实现蜂巢布局思路

效果图如下

image.png

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

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

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

image.png

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

 
// 第二圈时的对应圆上的坐标值
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度的意思。

const sin60 = Math.sin(Math.PI / 3)
const cos60 = Math.cos(Math.PI / 3)

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

// 生成六边形中心坐标
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需要实现其,方式为

 
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这种辅助绘图的库来进行效果绘制了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值