節點圍繞圓排列

節點圍繞圓排列

最近在做一個大屏,需要動態計算節點圍繞圓依次有序排列。大致效果這樣的形狀

分析

每個三角都是一個獨立div, 基於定位進行排序。現如今需要知道如何計算每個三角相對於圓的位置。需要設計到一些額外的基礎數學知識

三角知識

math

  • 正弦
 sinθ = b / a
  • 餘弦
 cosθ = c / a
  • 正切
 tanθ = b / c
  • 弧度

    弧長等於半徑的弧,其所對的圓心角為 1 弧度。(即兩條射線從圓心向圓周射出,形成一個夾角和夾角正對的一段弧。當這段弧長正好等於圓的半徑時,兩條射線的夾角的弧度為 1)。link

    • rad 弧度
    • Math.PI π
    • r 半徑
    • n 角度數
 rad = n * Math.Pi * r / 180

注意 在使用 Math.sin等方法時,角度需要轉為弧度才能計算使用

前端瀏覽器中的角度

web

前端的角度與數學上的角度開始位置不太一樣,上圖是前端下的角度

coding

通過上述的基礎知識,便可知曉最初的需求,三角 的位置計算。

html

<div class="container">
  <div class="circle">
    <div class="circle__item"></div>
    <div class="circle__item"></div>
    <div class="circle__item"></div>
    <div class="circle__item"></div>
    <div class="circle__item"></div>
    <div class="circle__item"></div>
    <div class="circle__item"></div>
    <div class="circle__item"></div>
    <div class="circle__item"></div>
  </div>
</div>

css

.container {
  position: relative;
  width: 600px;
  height: 600px;
  margin: 50px auto;
  border: 1px solid red;
}
.container::before {
  content: '';
  position: absolute;
  left: 50%;
  height: 100%;
  border-right: 1px solid red;
}
.container::after {
  content: '';
  position: absolute;
  top: 50%;
  width: 100%;
  border-top: 1px solid red;
}
.circle {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  box-shadow: 0 9px 30px 3px rgba(0, 0, 0, 0.46);
}
.circle__item {
  position: absolute;
  width: 40px;
  height: 40px;
  background: linear-gradient(70deg, #0ebeff, #ffdd40, #ae63e4, #47cf73);
}

頁面效果圖

coding-1

計算位置

每個節點的lefttop 位置

// 偽程式碼
// n 角度
// r 半徑
const left = r * Math.cos(n)
const top = r * Math.sin(n)

實際程式碼

在寫前,需要知道幾個固定參數

— 角度轉弧度

  const transformToRadians = (degrees) => degrees * (Math.PI / 180)
  • r 半徑

      const circleRadius = containerWidth / 2
    
  • 圓心的橫縱位置

      const circlePointX = circleRadius
      const circlePointY = circleRadius
    

coding

/**
 * 目標:
 * 1. 圍繞圓旋轉
 * 2. 在圓的內部緊貼邊緣
 * 3. 可以指定開始結束位置
 * 4. 依次均勻分開擺放
 */
const container = document.querySelector('.container')
const containerWidth = container.clientWidth
const circleList = Array.from(document.querySelectorAll('.circle__item'))
// 角度轉弧度
const transformToRadians = (degrees) => degrees * (Math.PI / 180)
// 圓半徑
const circleRadius = containerWidth / 2
// 圓心
const circlePointX = circleRadius
const circlePointY = circleRadius
// 劃分多少等分
const count = circleList.length

/**
 * @description: 根據已知角度算出每個元素的具體位置
 * @param {number} angleStart 開始的角度
 * @param {number} circle 劃分多大的圓
 */
let cache = {}
function changeItemAngle(
  options = {
    angleStart: 200,
    circle: 330,
  }
) {
  cache = {
    ...cache,
    ...options,
  }
  // 劃分的角度
  const angle = Math.floor(cache.circle / count)

  circleList.forEach((item, index) => {
    // 轉為 0 - 360 度
    const itemAngle = (angle * (index + 1) + cache.angleStart) % 360
    // 弧度
    const itemRadians = transformToRadians(itemAngle)
    const { clientWidth, clientHeight } = item

    let top = circleRadius * Math.sin(itemRadians) + circlePointY
    let left = circleRadius * Math.cos(itemRadians) + circlePointX

    // 為了讓子節點緊貼圓內壁
    if (itemAngle < 90 && itemAngle >= 0) {
      top -= clientHeight
      left -= clientWidth
    } else if (itemAngle >= 90 && itemAngle < 180) {
      top -= clientHeight
    } else if (itemAngle >= 270 && itemAngle < 360) {
      left -= clientWidth
    }

    item.style.top = top + 'px'
    item.style.left = left + 'px'
    item.innerText = itemAngle
  })
}

coding-2

後記

基本功能完成,也能正常使用,美中不足,需要控制子節點個數及大小,這塊需要看項目具體需求了。

雖然一直在 coding,但在開發中,對一個的綜合能力越來越考驗,因而一直前行一直學習,才能對得起這日日coding啊