前端实现蜂巢布局思路

效果图如下

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

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

观察不难得出结论,以中心的六边形为基点,第二圈很明显能排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这种辅助绘图的库来进行效果绘制了。

相关推荐
ekskef_sef10 分钟前
32岁前端干了8年,是继续做前端开发,还是转其它工作
前端
sunshine64135 分钟前
【CSS】实现tag选中对钩样式
前端·css·css3
真滴book理喻1 小时前
Vue(四)
前端·javascript·vue.js
蜜獾云1 小时前
npm淘宝镜像
前端·npm·node.js
dz88i81 小时前
修改npm镜像源
前端·npm·node.js
Jiaberrr1 小时前
解锁 GitBook 的奥秘:从入门到精通之旅
前端·gitbook
顾平安2 小时前
Promise/A+ 规范 - 中文版本
前端
聚名网2 小时前
域名和服务器是什么?域名和服务器是什么关系?
服务器·前端
桃园码工2 小时前
4-Gin HTML 模板渲染 --[Gin 框架入门精讲与实战案例]
前端·html·gin·模板渲染
不是鱼2 小时前
构建React基础及理解与Vue的区别
前端·vue.js·react.js