Skip to content

Instantly share code, notes, and snippets.

@sanjeevkse
Created October 5, 2023 04:25
Show Gist options
  • Save sanjeevkse/6965c8735c0d801c63e271cc4f23acde to your computer and use it in GitHub Desktop.
Save sanjeevkse/6965c8735c0d801c63e271cc4f23acde to your computer and use it in GitHub Desktop.
v
<!DOCTYPE html>
<html>
<head>
<style>
#artboard {
width: 800px; /* Initial width */
height: 600px; /* Initial height */
overflow: hidden;
border: 1px solid #000;
position: relative;
}
#draggable-container {
width: 2000px; /* Set a large width to make it effectively infinite */
height: 2000px; /* Set a large height to make it effectively infinite */
cursor: grab;
transform-origin: top left;
transition: transform 0.2s ease-in-out;
/* Add an initial transform scale */
transform: scale(1);
}
#treeSVG {
position: absolute;
}
</style>
</head>
<body>
<div id="artboard">
<div id="draggable-container">
<svg id="treeSVG" width="2000" height="2000"></svg>
</div>
</div>
<div id="nodeInfo"></div>
<button id="zoom-in">Zoom In</button>
<button id="zoom-out">Zoom Out</button>
<script>
const artboard = document.getElementById("artboard");
const draggableContainer = document.getElementById("draggable-container");
const svg = document.getElementById("treeSVG");
// Define your tree hierarchy data here
const treeData = {
name: "Root",
level: 0,
children: [
{
name: "Node 1",
level: 1,
children: [
{
name: "Node 1.1",
children: [
{ name: "Node 1.1.1" },
{ name: "Node 1.1.2" },
{ name: "Node 1.1.3" },
{ name: "Node 1.1.4" },
],
},
{ name: "Node 1.2" },
],
},
{ name: "Node 2" },
{
name: "Node 3",
children: [{ name: "Node 3.1" }, { name: "Node 3.2" }],
},
],
};
function drawTree(node, x, y, level) {
const spacingX = 100;
const spacingY = 100;
const textPadding = 10;
// Measure text size for dynamic rectangle width
const textElement = document.createElementNS(
"http://www.w3.org/2000/svg",
"text"
);
textElement.textContent = node.name;
svg.appendChild(textElement);
const textWidth = textElement.getBBox().width;
svg.removeChild(textElement);
// Calculate rectangle width based on text size
const rectWidth = textWidth + textPadding * 2;
const rectHeight = 40;
const borderRadius = 10;
// Draw the rectangle
const rect = document.createElementNS(
"http://www.w3.org/2000/svg",
"rect"
);
rect.setAttribute("x", x - rectWidth / 2);
rect.setAttribute("y", y - rectHeight / 2);
rect.setAttribute("width", rectWidth);
rect.setAttribute("height", rectHeight);
rect.setAttribute("rx", borderRadius);
rect.setAttribute("fill", "#0050FE");
svg.appendChild(rect);
// Add text to the rectangle
const text = document.createElementNS(
"http://www.w3.org/2000/svg",
"text"
);
text.setAttribute("x", x);
text.setAttribute("y", y);
text.setAttribute("text-anchor", "middle");
text.setAttribute("alignment-baseline", "middle");
text.setAttribute("fill", "white");
text.textContent = node.name;
svg.appendChild(text);
// Draw connections to children with Bezier curves
if (node.children) {
const childY = y + spacingY;
node.children.forEach((child, index) => {
const childX =
x -
(spacingX * (node.children.length - 1)) / 2 +
index * spacingX;
// Draw Bezier curve
const curve = document.createElementNS(
"http://www.w3.org/2000/svg",
"path"
);
const curveD = `M${x} ${y + rectHeight / 2} C${x} ${
y + rectHeight / 2 + spacingY / 2
}, ${childX} ${childY - spacingY / 2}, ${childX} ${
childY - rectHeight / 2
}`;
curve.setAttribute("d", curveD);
curve.setAttribute("fill", "transparent");
curve.setAttribute("stroke", "#BCC4CC");
curve.setAttribute("stroke-width", "2");
svg.appendChild(curve);
// Recursively draw child nodes
drawTree(child, childX, childY, level + 1);
});
}
// Add click event listener to the rectangle
rect.addEventListener("click", () => {
updateNodeInfo(node);
});
}
function updateNodeInfo(selectedNode) {
const nodeInfo = document.getElementById("nodeInfo");
nodeInfo.textContent = `Selected Node: ${JSON.stringify(selectedNode)}`;
}
// Start drawing from the root node
drawTree(treeData, svg.clientWidth / 2, 50, 0);
// Implement drag and pan functionality
let isDragging = false;
let startX, startY, scrollLeft, scrollTop;
draggableContainer.addEventListener("mousedown", (e) => {
isDragging = true;
startX = e.clientX;
startY = e.clientY;
scrollLeft = artboard.scrollLeft;
scrollTop = artboard.scrollTop;
draggableContainer.style.cursor = "grabbing";
});
window.addEventListener("mousemove", (e) => {
if (isDragging) {
const deltaX = e.clientX - startX;
const deltaY = e.clientY - startY;
artboard.scrollLeft = scrollLeft - deltaX;
artboard.scrollTop = scrollTop - deltaY;
}
});
window.addEventListener("mouseup", () => {
isDragging = false;
draggableContainer.style.cursor = "grab";
});
// Zoom-in and zoom-out buttons
document.getElementById("zoom-in").addEventListener("click", () => {
const currentScale = getComputedStyle(draggableContainer).transform;
const scaleValue = currentScale.match(/matrix\(([^,]+),/)[1];
const newScale = parseFloat(scaleValue) * 1.2; // Adjust the zoom factor as needed
draggableContainer.style.transform = `scale(${newScale})`;
});
document.getElementById("zoom-out").addEventListener("click", () => {
const currentScale = getComputedStyle(draggableContainer).transform;
const scaleValue = currentScale.match(/matrix\(([^,]+),/)[1];
const newScale = parseFloat(scaleValue) / 1.2; // Adjust the zoom factor as needed
draggableContainer.style.transform = `scale(${newScale})`;
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment