Files
WebtreesFullDiagram/resources/js/modules/lib/chart/zoom.js
Alexander Bocken 273e398431 Initial commit: webtrees full diagram chart module
Interactive SVG family tree visualization using ELK (Sugiyama) for
layout and D3 for rendering. Shows ancestors, descendants, and siblings
in a single diagram with orthogonal bus-line connectors.

Features:
- Bidirectional tree traversal (ancestors + descendants + siblings)
- Generation-aligned layout with post-processing Y-snap
- Person cards with photos, names, dates, and hover bio cards
- "More ancestors" indicator for persons with hidden parents
- Pan/zoom navigation
- Docker dev environment
2026-03-14 18:52:17 +01:00

73 lines
1.9 KiB
JavaScript

/**
* Pan and zoom behavior via d3-zoom.
*/
import { zoom, zoomIdentity, select } from "../d3.js";
import { getCanvas } from "./svg.js";
/**
* Initialize zoom behavior on the SVG element.
*
* @param {d3.Selection} svg
* @returns {d3.ZoomBehavior}
*/
export function initZoom(svg) {
const canvas = getCanvas(svg);
const zoomBehavior = zoom()
.scaleExtent([0.1, 4])
.on("zoom", (event) => {
canvas.attr("transform", event.transform);
});
svg.call(zoomBehavior);
// Disable double-click zoom (we use click for navigation)
svg.on("dblclick.zoom", null);
return zoomBehavior;
}
/**
* Create zoom control buttons.
*
* @param {string} containerSelector
* @param {d3.Selection} svg
* @param {d3.ZoomBehavior} zoomBehavior
*/
export function createZoomControls(containerSelector, svg, zoomBehavior) {
const container = select(containerSelector);
const controls = container
.append("div")
.attr("class", "zoom-controls");
controls
.append("button")
.attr("type", "button")
.attr("title", "Zoom in")
.text("+")
.on("click", () => svg.transition().duration(300).call(zoomBehavior.scaleBy, 1.3));
controls
.append("button")
.attr("type", "button")
.attr("title", "Zoom out")
.text("\u2212")
.on("click", () => svg.transition().duration(300).call(zoomBehavior.scaleBy, 0.7));
controls
.append("button")
.attr("type", "button")
.attr("title", "Reset view")
.text("\u21BA")
.on("click", () => {
const { width, height } = svg.node().getBoundingClientRect();
svg.transition()
.duration(500)
.call(
zoomBehavior.transform,
zoomIdentity.translate(width / 2, height / 2)
);
});
}