echarts 地图引导线加复杂说明区样式怎么实现?说明卡片数量不定。如何使卡片做到环绕地图灵活布局?

2023-06-28 321 0

说明围绕地图一周个数不定  怎么让引导线准确的引到说明框

不才的破站 蹭饭图制作工具 ,有连线的实现:
image.png
不过具体实现是基于 ZRender 的,像你图中这种线,我称为“曲柄线”,其算法实现如下:

import { Circle, Polyline } from "zrender";
// 卡片包围盒的四条边的中点位置
export type EndPoints = [
  [number, number], // 上
  [number, number], // 右
  [number, number], // 下
  [number, number]  // 左
];
export type LineRender = (args: {
  // 地图中心点的坐标
  mapCenter: [number, number];
  tagPoints: EndPoints;
  style: Partial<{
    stroke: string;
    lineDash: number[];
    lineWidth: number;
  }>;
  pointStyle: Partial<{
    stroke: string;
    lineDash: number[];
    lineWidth: number;
  }>
}) => ZRender.Displayable[];
/**
 * @function getNearestPoint - 获取四个点中距地图最近的一个点
 * @param param0
 * @param tagPoints
 * @returns [number, [number, number]] - 最近点的序号 + 坐标
 */
export const getNearestPoint = (
  [x, y]: [number, number],
  tagPoints: EndPoints
) => {
  const [nearestX, nearestY] = [...tagPoints[0]];
  let shrtestIndex = 0;
  let shortestDistance = (x - nearestX) ** 2 + (y - nearestY) ** 2;
  for (let index = 1; index < 4; index++) {
    const [a, b] = tagPoints[index];
    const currentDistance = (x - a) ** 2 + (y - b) ** 2;
    if (currentDistance < shortestDistance) {
      shortestDistance = currentDistance;
      shrtestIndex = index;
    }
  }
  return [shrtestIndex, tagPoints[shrtestIndex]] as [number, [number, number]];
};
/**
 * 曲柄线,即拐角为 90° 的 "Z" 形线
 */
const crank: LineRender = ({ tagPoints, mapCenter, style, pointStyle }) => {
  // 先计算最近的点
  const [mapX, mapY] = mapCenter;
  const [index, [tagX, tagY]] = getNearestPoint(mapCenter, tagPoints);
  // 固定在 1/2 处拐弯,其实不该叫做 quater,而是 half
  const quaterOffsetX = (tagX - mapX) / 2;
  const quaterOffsetY = (tagY - mapY) / 2;
  // 确定曲柄是竖直方向还是水平方向
  const isVertical = +Boolean(index % 2); // 上下, y 不变
  const isHorizon = +!Boolean(index % 2); // 左右, x 不变
  return [
    // 曲柄线对象
    new ZRender.Polyline({
      shape: {
        points: [
          [...mapCenter],
          [mapX + quaterOffsetX * isVertical, mapY + quaterOffsetY * isHorizon],
          [tagX - quaterOffsetX * isVertical, tagY - quaterOffsetY * isHorizon],
          [tagX, tagY],
        ],
        smooth: 0,
      },
      silent: true,
      style,
      zlevel: 2,
    }),
    // 曲柄线端点,地图一侧
    new ZRender.Circle({
      style: pointStyle,
      shape: {
        cx: mapX,
        cy: mapY,
        r: style?.lineWidth || 1,
      },
      zlevel: 2,
    }),
    // 曲柄线端点,卡片一侧
    new ZRender.Circle({
      style: pointStyle,
      shape: {
        cx: tagX,
        cy: tagY,
        r: style?.lineWidth || 1,
      },
      zlevel: 2,
    }),
  ];
};

这里面的拐弯处坐标固定为起止点连线的中点 ,由用户自己调整以避免线条交叉。
我的卡片数量、大小也是不定的,但是排版和布线的包袱丢给了用户。如果想实现自动排版和布线的话,你需要写更复杂的算法以确定多个卡片的位置和各个拐点的坐标,计算好之后线条的形状算法依旧可以照搬我的实现。
至于在 ECharts 中如何画线,可以参考 echarts.graphic.extendShape。

不过我觉得 ECharts 默认的 LabelLine 其实并不比曲柄线差,考虑一下说服设计师直接用 LabelLine 得了。

回答

相关文章

nuxt2部署静态化和ssr的时候访问首页先报404再出现首页为什么?
`clip-path` 如何绘制圆角平行四边形呢?
多线程wait方法报错?
VUE 绑定的方法如何直接使用外部函数?
vue2固定定位该怎么做?
谁有redis实现信号量的代码,希望借鉴一下?