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.
Created
August 31, 2024 14:03
-
-
Save erikvullings/7181289960034bbd915bf1bbab23a2af to your computer and use it in GitHub Desktop.
Create interactive SVG grid
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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