在动态创建的 canvas 或带有 javascript 的 SVG 上查找旋转对象的边界矩形(打字稿)

Finding bounding rect of rotated objects on dynamically created canvas or SVG with javascript (typescript)

我有一个问题,我已经尝试解决了一段时间。

我正在开发一个带有 typescript、react 和 google 地图的网络应用程序,对于标记,我想使用在给定更多参数的情况下在运行时创建的自定义图像。

图像有两个对象,一个圆形和一个三角形,它们按先前给定的角度指向方向,并根据其他参数,它们具有不同的 color.The 图像应该如下所示: Image 1 Image 2.

图像的范围从 1 到 300,具体取决于用户的选择。另一件事是三角形的顶部应该从地图坐标开始,当更多标记靠近时,应该看起来像这张图片 Image 3.

我已经成功创建图像并将它们按需要放置在地图上,但我的问题是图像太大并且在靠近时会重叠,因此所有标记在放大之前都不可点击,所以我遇到这样的情况:Image 4.

我需要剪掉透明区域,所以只剩下圆形和三角形用于地图。我创建了两个函数,一个带有 canvas 元素,一个带有 SVG 元素。它们都很相似,我先画圆然后画三角形然后旋转它们。

使用 SVG 解决方案,我能够使用 getBBox()getBoundingClientRect() 找到边界矩形,但问题是图像未加载到 DOM 中,我什么也得不到从这些方法直到将 SVG 添加到 DOM,这是不好的,因为我应该将每个图像添加到 DOM,然后处理它,添加到地图并将其从 [=48] 中删除=].

使用 canvas 方法,我能够在互联网上找到一个函数,该函数循环遍历并扫描 canvas 和 return 对象的每个像素,但是当有许多元素加载它有点慢。我还尝试通过使用以下公式查找旋转坐标来查找对象:

x1 = x * Math.cos(angle * Math.PI / 180) - y * Math.sin(angle * Math.PI / 180)
y1 = x * Math.sin(angle * Math.PI / 180) + y * Math.cos(angle * Math.PI / 180)

但没有成功。

那么解决这个问题的最佳方法是什么?

谢谢。

代码:

    // draw canvas
    const drawMarkerIconCanvas = (
        angle?: number | null,
        arrowFillColor?: string,
        circleFillColor?: string,
        strokeColor?: string,
        scale?: number,
        text?: string,
        textColor?: string) => {
    
        angle = angle || null;
        arrowFillColor = arrowFillColor || "#000000";
        circleFillColor = circleFillColor || "#FACF00";
        strokeColor = strokeColor || "#0050b3";
        scale = scale || 0.7;
        text =  text || " ";
        textColor = textColor || "#FFFFFF";
    
        const canvas = document.createElement("canvas");
        canvas.width = 180;
        canvas.height = 180;
        const ctx = canvas.getContext("2d");
        if(ctx) {
            ctx.scale(scale,scale!)
            ctx.strokeStyle=strokeColor!;
            if(angle) {
                ctx.translate(canvas.width / 2,canvas.height / 2)
                ctx.rotate(angle! * Math.PI / 180)
                ctx.translate(-30, 0)
            }
            // draw the circle
            ctx.lineWidth=2;
            ctx.fillStyle=circleFillColor!;
            ctx.beginPath();
            ctx.arc(30,60,25,0,2*Math.PI);
            ctx.fill();
            ctx.stroke();
            
            // draw the triangle
            ctx.fillStyle = arrowFillColor!;
            ctx.beginPath();
            ctx.moveTo(20, 30);
            ctx.lineTo(40, 30);
            ctx.lineTo(30, 0);
            ctx.lineTo(20, 30);
            ctx.closePath();
            ctx.fill()
            ctx.stroke()
    
            // draw the Text
            ctx.translate(30, 60)
            ctx.rotate(-angle! * Math.PI / 180)
            ctx.translate(-30, -60)
            ctx.font="14px Arial"
            ctx.fillStyle=textColor;
            ctx.fillText(text,27-ctx.measureText(text).width/2,63,)
        }
        return canvas.toDataURL();
    }

   // draw svg
    const drawMarkerIconSVG = (angle?: number | null,
                           arrowFillColor?: string,
                           circleFillColor?: string,
                           strokeColor?: string,
                           scale?: number,
                           text?: string,
                           textColor?: string) => {

    angle = angle || 0;
    arrowFillColor = arrowFillColor || "#000000";
    circleFillColor = circleFillColor || "#FACF00";
    strokeColor = strokeColor || "#0050b3";
    scale = scale || 0.7;
    text = text || " ";
    textColor = textColor || "#FFFFFF"

    const getBox = (element: any) => {
        return (element as SVGGraphicsElement).getBBox()
    }

    const svg: HTMLElement = document.createElement('svg')

    const group = document.createElement('g')

    const circle = document.createElement('circle')

    const poly = document.createElement('polygon')

    svg.setAttribute("xmlns","http://www.w3.org/2000/svg")

    circle.setAttribute('cx', '30')
    circle.setAttribute('cy', '60')
    circle.setAttribute('r', '25')
    circle.setAttribute('fill', circleFillColor)
    circle.setAttribute('stroke', strokeColor)
    circle.setAttribute('stroke-width', '2')

    poly.setAttribute('points', '20,30, 40,30,30,0')
    poly.setAttribute('fill', arrowFillColor);
    poly.setAttribute('stroke', strokeColor);
    poly.setAttribute('stroke-width', '2');

    group.appendChild(circle)
    group.appendChild(poly)

    group.setAttribute('transform', 'scale('+scale+') translate(90,90) rotate('+angle+') translate(-30,0)')


    const box = getBox(group)

    svg.appendChild(group)

    svg.setAttribute('viewBox', `${box.x-10} ${box.y-10} ${box.width+2} ${box.height+2}`)

    return svg
}

通过一些试验,我能够解决我的问题,而不是找到边界的预期解决方案,但在我的情况下对我有用。首先,我在 canvas 的中心绘制图像,然后缩放它们。之后,我根据旋转角度和 canvas 的平移将它们放置在地图上,应用上述公式在旋转后查找点,所以我创建了一个这样的函数:

const getCoordinates = (x: number, y: number, angle: number, imgWidth: number, imgHeight: number) => {
    let x1, y1;

    x1 = x * Math.cos(angle * Math.PI / 180) - y * Math.sin(angle * Math.PI / 180)
    y1 = x * Math.sin(angle * Math.PI / 180) + y * Math.cos(angle * Math.PI / 180)

    return {
        x: x1+imgWidth/2, y: y1+imgHeight/2
    }
}