在动态创建的 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
}
}
我有一个问题,我已经尝试解决了一段时间。
我正在开发一个带有 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
}
}