js圆环排列

参考: https://www.cnblogs.com/lufy/archive/2012/06/21/2558049.html

一、理论推理(想得通的方式)

  • 想要元素平均分布在圆环上 =》那么元素之间的弧长相等 =》相邻元素所在半径形成的圆心角大小一致 
  • 角度数360° = 弧度数 2π =》1° = π/180 =》360°/2π = 53.7° = 1
  • 在直角坐标系下,以x轴的非负轴作为起始边,逆时针旋转,圆心角为正

此时,单个元素中心点在圆弧上的坐标位置 (Ox+Math.cos(X)*R , Oy-Math.sin(X)*R),弧度数X = 角度数*π/180 。

二、canvas实践:

大前提:注意事项:

在canvas的2D绘图环境中,坐标系同窗口坐标系,左上角为原点,向右x正轴,向下y正轴。

所以坐标y改为Oy-Math.sin(X)*R,从视觉上达到与直角坐标系同样的效果。

1. 渲染一个元素

// 外圆 (X,Y) R
let X = 400,Y = 400,R = 150;
cxt.beginPath();
cxt.arc(X, Y, R, 0, Math.PI * 2);
cxt.stroke();
// 小圆
cxt.beginPath();
cxt.arc( X + Math.cos((30 * Math.PI) / 180 ) * R, Y - Math.sin((30 * Math.PI) / 180 ) * R, 50, 0, Math.PI * 2 );
cxt.closePath();
cxt.stroke();

2. 可借助数组下标,改变各个元素的圆心角,渲染一周8个元素

let n = 8;
for (let i = 0; i < n; i++) {
  cxt.beginPath();
  cxt.arc( X + Math.cos(((2 * Math.PI) / n) * i) * R, Y - Math.sin(((2 * Math.PI) / n) * i) * R, 50, 0,Math.PI * 2);
  cxt.stroke();
}

 其实,渲染方向与起始位都是很灵活的

3.  结合点阵规律,渲染多层环形

结合点阵规律 正a边形 第一层1,第二层a, 第三层2a, ... , 第n层 a(n-1)

但当下讨论的是除去中心点的点阵规律:因此不要第一层,则对于正a边形来说,第n层有a * n个点,n层共有a * n * (n + 1)) / 2 个点。

① 渲染一层

let a = 6; //正六边形
let level = 2; //第2层
let n = a * level; //该层需要分布元素个数
for (let i = 0; i < n; i++) {
  cxt.beginPath();
  cxt.arc(X + Math.sin(((2 * Math.PI) / n) * i) * R, Y - Math.cos(((2 * Math.PI) / n) * i) * R, 50, 0, Math.PI * 2);
  cxt.stroke();
}

② 渲染多层

let a = 6; //正六边形
let levelNum = 2; //层数
let r = 30;
for (let level = 1; level <= levelNum; level++) {
  let seatNum = a * level;
  for (let seat = 0; seat < seatNum; seat++) {
    cxt.beginPath();
    cxt.arc(
      X + Math.sin(((2 * Math.PI) / seatNum) * seat) * (R + 60 * (level - 1)),
      Y - Math.cos(((2 * Math.PI) / seatNum) * seat) * (R + 60 * (level - 1)),
      r,
      0,
      Math.PI * 2
    );
    cxt.stroke();
  }
}

可以看到,如果不好好调整 影响第一层轨迹的R , 层之间的距离(这里是60),就会很丑

4. 在实际情况下,往往给定总数,去渲染多层,所以只要先排座、再渲染就好

let X = 300;
let Y = 300;
let R = 45; //影响第一排轨迹,即红圈
let distance = 50; //两排元素中心的间距
let r = 25;
let rankList = [];

// 画红圈
cxt.beginPath();
cxt.strokeStyle = '#ff0000'
cxt.arc(X, Y, R, 0, Math.PI * 2);
cxt.stroke(); 

// 排座,排在几排几座,好确定各小球圆心位置
function sortCoords(sum, a) { // 将总数sum的小球排成正a边形点阵
  let level = 1;
  let seat = -1;
  let seatNum = a * level;
  for (let i = 1; i <= sum; i++) {
    if (i <= (a * level * (level + 1)) / 2) {
      seat++;
    } else {
      level++;
      seat = 0;
      seatNum = a * level;
    }
    const tempX = X + Math.sin(((2 * Math.PI) / seatNum) * seat) * (R + distance * (level - 1));
    const tempY = Y - Math.cos(((2 * Math.PI) / seatNum) * seat) * (R + distance * (level - 1));
    rankList[i - 1] = { level, seat, x: tempX, y: tempY, r };
  }
  return rankList;
}
rankList = sortCoords(30, 5);

// 渲染小球
rankList.forEach((item) => {
  cxt.beginPath();
  cxt.strokeStyle = '#333'
  cxt.arc(item.x, item.y, item.r, 0, Math.PI * 2);
  cxt.stroke();
});


总结:其实通篇都是废话,最重要的是确定各元素的中心位置,总结出通用的位置公式 ,这样就解决了90% 。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值