Skip to content

Instantly share code, notes, and snippets.

@erikvullings
Created August 31, 2024 14:03
Show Gist options
  • Save erikvullings/7181289960034bbd915bf1bbab23a2af to your computer and use it in GitHub Desktop.
Save erikvullings/7181289960034bbd915bf1bbab23a2af to your computer and use it in GitHub Desktop.
Create interactive SVG grid

Introduction

Click anywhere in the SVG grid and start a new line. Click again, and the line will be drawn. Try it out in the playground.

function createInteractiveGridSVG(w: number, h: number, additionalElements?: SVGElement[]): SVGSVGElement {
const svgNS = "http://www.w3.org/2000/svg";
const cellSize = 10;
const width = w * cellSize;
const height = h * cellSize;
// Create SVG element
const svg = document.createElementNS(svgNS, "svg");
svg.setAttribute("width", width.toString());
svg.setAttribute("height", height.toString());
svg.style.backgroundColor = "white";
// Create grid
for (let x = 0; x <= width; x += cellSize) {
const line = document.createElementNS(svgNS, "line");
line.setAttribute("x1", x.toString());
line.setAttribute("y1", "0");
line.setAttribute("x2", x.toString());
line.setAttribute("y2", height.toString());
line.setAttribute("stroke", "lightblue");
line.setAttribute("stroke-width", "1");
svg.appendChild(line);
}
for (let y = 0; y <= height; y += cellSize) {
const line = document.createElementNS(svgNS, "line");
line.setAttribute("x1", "0");
line.setAttribute("y1", y.toString());
line.setAttribute("x2", width.toString());
line.setAttribute("y2", y.toString());
line.setAttribute("stroke", "lightblue");
line.setAttribute("stroke-width", "1");
svg.appendChild(line);
}
// Add additional elements if provided
if (additionalElements) {
additionalElements.forEach(element => svg.appendChild(element));
}
// Add event listeners for drawing the red line
let startPoint: { x: number, y: number } | null = null;
let redLine: SVGLineElement | null = null;
let dashedLine: SVGLineElement | null = null;
function snapToGrid(x: number, y: number): { x: number, y: number } {
return {
x: Math.round(x / cellSize) * cellSize,
y: Math.round(y / cellSize) * cellSize
};
}
function isWithinSVG(x: number, y: number): boolean {
return x >= 0 && x <= width && y >= 0 && y <= height;
}
function removeExistingLines() {
if (redLine) {
svg.removeChild(redLine);
redLine = null;
}
if (dashedLine) {
svg.removeChild(dashedLine);
dashedLine = null;
}
}
svg.addEventListener("click", (event: MouseEvent) => {
const rect = svg.getBoundingClientRect();
const { x, y } = snapToGrid(event.clientX - rect.left, event.clientY - rect.top);
if (isWithinSVG(x, y)) {
if (!startPoint) {
removeExistingLines();
startPoint = { x, y };
// Create dashed line
dashedLine = document.createElementNS(svgNS, "line");
dashedLine.setAttribute("x1", x.toString());
dashedLine.setAttribute("y1", y.toString());
dashedLine.setAttribute("x2", x.toString());
dashedLine.setAttribute("y2", y.toString());
dashedLine.setAttribute("stroke", "red");
dashedLine.setAttribute("stroke-width", "2");
dashedLine.setAttribute("stroke-dasharray", "5,5");
svg.appendChild(dashedLine);
} else {
removeExistingLines();
redLine = document.createElementNS(svgNS, "line");
redLine.setAttribute("x1", startPoint.x.toString());
redLine.setAttribute("y1", startPoint.y.toString());
redLine.setAttribute("x2", x.toString());
redLine.setAttribute("y2", y.toString());
redLine.setAttribute("stroke", "red");
redLine.setAttribute("stroke-width", "2");
svg.appendChild(redLine);
startPoint = null;
}
}
});
svg.addEventListener("mousemove", (event: MouseEvent) => {
if (startPoint && dashedLine) {
const rect = svg.getBoundingClientRect();
const { x, y } = snapToGrid(event.clientX - rect.left, event.clientY - rect.top);
if (isWithinSVG(x, y)) {
dashedLine.setAttribute("x2", x.toString());
dashedLine.setAttribute("y2", y.toString());
} else {
// Reset if mouse is outside SVG
removeExistingLines();
startPoint = null;
}
}
});
return svg;
}
// Usage example
const additionalElements: SVGElement[] = [
(() => {
const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
rect.setAttribute("x", "30");
rect.setAttribute("y", "30");
rect.setAttribute("width", "40");
rect.setAttribute("height", "40");
rect.setAttribute("fill", "rgba(0, 255, 0, 0.3)");
return rect;
})(),
(() => {
const polyline = document.createElementNS("http://www.w3.org/2000/svg", "polyline");
polyline.setAttribute("points", "10,80 30,90 50,80 70,90 90,80");
polyline.setAttribute("fill", "none");
polyline.setAttribute("stroke", "purple");
polyline.setAttribute("stroke-width", "2");
return polyline;
})()
];
const gridSVG = createInteractiveGridSVG(20, 15, additionalElements);
document.body.appendChild(gridSVG);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment