Here's my SVG:
<svg width="663" height="576" viewBox="0 0 663 576" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect id="misa" width="663" height="576" fill="#D9D9D9"/>
</svg>
And here is my React component:
import React, { useEffect, useRef } from "react";
import "./App.css";
import hillstateSvg from "./images/B.svg"; // SVG file path
const escapeCssSelector = (id) => {
return `#${CSS.escape(id)}`;
};
const App = () => {
const canvasRef = useRef(null);
useEffect(() => {
fetch(hillstateSvg)
.then((response) => response.text())
.then((data) => {
const parser = new DOMParser();
const svgDoc = parser.parseFromString(data, "image/svg+xml");
const svgElement = svgDoc.documentElement;
const canvas = canvasRef.current;
const ctx = canvas.getContext("2d");
// Set canvas size to match SVG size
const svgWidth = parseInt(svgElement.getAttribute("width"));
const svgHeight = parseInt(svgElement.getAttribute("height"));
canvas.width = svgWidth;
canvas.height = svgHeight;
console.log(`Canvas size set to width: ${svgWidth}, height: ${svgHeight}`);
const svgString = new XMLSerializer().serializeToString(svgElement);
const img = new Image();
const svgBlob = new Blob([svgString], { type: "image/svg+xml;charset=utf-8" });
const url = URL.createObjectURL(svgBlob);
img.onload = () => {
ctx.drawImage(img, 0, 0);
URL.revokeObjectURL(url);
console.log("Image loaded and drawn on canvas");
// Add blue dots
const ids = ["misa", "misa2"];
ids.forEach((id) => {
const targetElement = svgElement.querySelector(escapeCssSelector(id));
if (targetElement) {
const bbox = targetElement.getBBox();
if (bbox.width === 0 || bbox.height === 0) {
// Try getBoundingClientRect if getBBox returns invalid dimensions
const rect = targetElement.getBoundingClientRect();
if (rect.width === 0 || rect.height === 0) {
console.log(`Element with ID: ${id} has invalid dimensions.`);
return;
} else {
console.log(`Element ID: ${id}, rect:`, rect);
const cx = rect.left + rect.width / 2;
const cy = rect.top + rect.height / 2;
ctx.beginPath();
ctx.arc(cx, cy, 10, 0, 2 * Math.PI);
ctx.fillStyle = "blue";
ctx.fill();
console.log("Blue dot drawn using getBoundingClientRect");
}
} else {
const cx = bbox.x + bbox.width / 2;
const cy = bbox.y + bbox.height / 2;
console.log(`Element ID: ${id}, bbox:`, bbox);
ctx.beginPath();
ctx.arc(cx, cy, 10, 0, 2 * Math.PI);
ctx.fillStyle = "blue";
ctx.fill();
console.log("Blue dot drawn using getBBox");
}
} else {
// Log if the element with the ID is not found
console.log(`Element with ID: ${id} not found.`);
}
});
};
img.onerror = () => {
console.error("Failed to load the image.");
};
img.src = url;
})
.catch((error) => {
console.error("Error fetching or parsing the SVG file:", error);
});
}, []);
return (
<div className="App">
<header className="App-header">
<h1>SVG Element Click Coordinates</h1>
<canvas ref={canvasRef} className="canvas-container"></canvas>
</header>
</div>
);
};
export default App;
The console outputs the following messages indicating that the elements misa
and are found, but their dimensions are invalid:
I'm using getBBox()
to calculate the position of the elements. I've also tried using getBoundingClientRect()
, but it also returns dimensions of 0.
How can I correctly get the dimensions and positions of these SVG elements to draw the blue dots on the canvas?
Any help would be greatly appreciated!
debugging
getBBox
in a headless/non-rendered environment by calculating x/y extremes from the geometry of elements. Dimensions set via CSS,<text>
elements and transformations are hard to emulate - I'm not aware of any library that solves these issues. But maybe someone is working on it.