javascript生成圆形分布的位置坐标

圆形布局-应用场景

在使用 echarts 关系图 graph 时,若要使用圆形或引力自动布局坐标系必须为 null;想要使关系图连线有动态效果,又需要使用动态的路线图 lines,lines 必需使用 cartesian2d 或 geo 的坐标系;因此为了实现动效,只能放弃自动圆形布局,手动计算出一个圆形布局的坐标。

圆形布局-代码示例

/**
 * 根据中心位置坐标、布局半径、布局数量及布局角度范围,按圆平均计算布局位置
 * @param {array} centerPosition 中心位置数组
 * @param {number} r 布局半径
 * @param {number} number 布局数量
 * @param {number} startAngle 布局起始角度,默认为0
 * @param {number} endAngle 布局结束角度,默认为360
 * @returns 生成的布局位置数组
 */
export function calcLayoutCircular(centerPosition, r, number, startAngle = 0, endAngle = 360) {
  /**
   * 将传入的任意角度转换为0-360度的角度
   * @param {number}} angle 传入角度
   * @returns 转换后的角度
   */
  function normalizeAngle(angle) {
    // 任意角度相对360度取余数
    const normalizedAngle = angle % 360;
    // 若为负角度则加上360度,转换为正角度
    return normalizedAngle < 0 ? normalizedAngle + 360 : normalizedAngle;
  }
  startAngle = normalizeAngle(startAngle);
  endAngle = normalizeAngle(endAngle);
  let intervalAngle;
  // 计算间隔角度
  // 若角度范围是完整圆周,起始角度与结束角度只占用一个布局数量,按布局数量平均分配角度即可;
  // 若角度范围不是完整圆周,则起始角度与结束角度会占用两个布局数量,需要以 number - 1 计算角度间隔,若number为1时不减1
  if (endAngle - startAngle === 0) {
    intervalAngle = 360 / number;
  } else {
    intervalAngle = normalizeAngle(endAngle - startAngle) / (number - 1 || 1);
  }
  // 根据布局数量与间隔角度计算所有布局位置
  const positionArr = [];
  for (let i = 0; i < number; i++) {
    let angle = normalizeAngle(startAngle + intervalAngle * i);
    const x = centerPosition[0] + r * Math.sin((angle * Math.PI) / 180);
    const y = centerPosition[1] + r * Math.cos((angle * Math.PI) / 180);
    positionArr.push({
      angle,
      position: [x, y],
    });
  }
  return positionArr;
}

标签位置-应用场景

使用圆形布局后,当元素标签不放在元素上居中显示,而要根据相对圆心的位置,显示在上、右、下、左等不同方位时,使用此方法计算。

标签位置-代码示例

/**
 * 根据传入的角度(当前元素相对圆心的角度)计算出标签位置,简单模式只计算左右,非简单模式计算上下左右
 * @param {number} angle 传入的角度
 * @param {boolean} simple 是否简单模式
 * @returns 位置字符串
 */
export function calcLabelPositionByAngle(angle, simple = true) {
  if (simple) {
    if (angle >= 0 && angle < 180) {
      return "right";
    } else {
      return "left";
    }
  } else {
    if (angle > 275 || angle <= 45) {
      return "top";
    } else if (angle > 45 && angle <= 135) {
      return "right";
    } else if (angle > 135 && angle <= 215) {
      return "bottom";
    } else if (angle > 215 && angle <= 275) {
      return "left";
    } else {
      return "top";
    }
  }
}