diff --git a/core/frontend/graph.js b/core/frontend/graph.js index 9a2752a2..e94e0e7f 100644 --- a/core/frontend/graph.js +++ b/core/frontend/graph.js @@ -536,6 +536,99 @@ function translate() { } } +const zoomMax = 10, + zoomMin = 1; + +let zoomInterval = 0.2; + +/** + * Sum of nodes size + * @type {number} + */ +const nodeFactor = graph.reduceNodes((acc, node, { size }) => acc + size, 0); + +window.addEventListener('resize', () => { + let density = nodeFactor / (window.innerWidth * window.innerHeight); + density *= 1000; + + zoomInterval = Math.log2(density); +}); + +const zoom = d3 + .zoom() + .scaleExtent([zoomMin, zoomMax]) + .on('zoom', (e) => { + const { x, y, k } = e.transform; + View.position.x = x || 0; + View.position.y = y || 0; + View.position.zoom = k || 1; + translate(); + }); + +svg.call(zoom); + +function zoomMore() { + zoom.scaleTo(svg, View.position.zoom + zoomInterval); +} + +function zoomLess() { + zoom.scaleTo(svg, View.position.zoom - zoomInterval); +} + +function zoomReset() { + View.position.zoom = 1; + View.position.x = 0; + View.position.y = 0; + svg.call( + zoom.transform, + d3.zoomIdentity.translate(View.position.y, View.position.x).scale(View.position.zoom), + ); + translate(); +} + +window.zoomMore = zoomMore; +window.zoomLess = zoomLess; +window.zoomReset = zoomReset; + +hotkeys('e,alt+r', (e) => { + e.preventDefault(); + zoomReset(); +}); + +/** + * Zoom to a node from its coordinates + * @param {string} nodeId + */ + +function zoomToNode(nodeId) { + const nodes = elts.nodes.data(); + + const node = nodes.find(({ key }) => key === nodeId); + console.log(node); + + if (!node) return; + const { x, y } = node; + + const meanX = d3.mean(nodes, (d) => d.x); + const meanY = d3.mean(nodes, (d) => d.y); + + const zoomScale = + View.position.zoom === 1 ? View.position.zoom + zoomInterval * 2 : View.position.zoom; + + svg.call( + zoom.transform, + d3.zoomIdentity.translate(-(x * zoomScale - meanX), -(y * zoomScale - meanY)).scale(zoomScale), + ); + translate(); +} + +hotkeys('c', (e) => { + e.preventDefault(); + const recordId = getRecordIdFromHash(); + + zoomToNode(recordId); +}); + export { svg, svgSub, @@ -548,4 +641,5 @@ export { unlightNodes, translate, graph, + zoomToNode, }; diff --git a/core/frontend/index.js b/core/frontend/index.js index ff1a56fb..6dc58d34 100644 --- a/core/frontend/index.js +++ b/core/frontend/index.js @@ -1,7 +1,6 @@ import './records.js'; import './search.js'; import './graph.js'; -import './zoom.js'; import './bibliography.js'; import './timeline.js'; import './filter.js'; diff --git a/core/frontend/records.js b/core/frontend/records.js index b60baf82..41ef2e36 100644 --- a/core/frontend/records.js +++ b/core/frontend/records.js @@ -1,5 +1,4 @@ -import { graph, highlightNodes, unlightNodes } from './graph.js'; -import { zoomToNode } from './zoom.js'; +import { graph, zoomToNode, highlightNodes, unlightNodes } from './graph.js'; import hotkeys from 'hotkeys-js'; window.addEventListener('DOMContentLoaded', () => { diff --git a/core/frontend/zoom.js b/core/frontend/zoom.js deleted file mode 100644 index 20006455..00000000 --- a/core/frontend/zoom.js +++ /dev/null @@ -1,97 +0,0 @@ -import * as d3 from 'd3'; - -import View from './view.js'; -import { svg, translate, graph } from './graph.js'; -import hotkeys from 'hotkeys-js'; -import { getRecordIdFromHash } from './records.js'; - -const zoomMax = 10, - zoomMin = 1; - -let zoomInterval = 0.2; - -/** - * Sum of nodes size - * @type {number} - */ -const nodeFactor = graph.reduceNodes((acc, node, { size }) => acc + size, 0); - -window.addEventListener('resize', () => { - let density = nodeFactor / (window.innerWidth * window.innerHeight); - density *= 1000; - - zoomInterval = Math.log2(density); -}); - -const zoom = d3 - .zoom() - .scaleExtent([zoomMin, zoomMax]) - .on('zoom', (e) => { - const { x, y, k } = e.transform; - View.position.x = x || 0; - View.position.y = y || 0; - View.position.zoom = k || 1; - translate(); - }); - -svg.call(zoom); - -function zoomMore() { - zoom.scaleTo(svg, View.position.zoom + zoomInterval); -} - -function zoomLess() { - zoom.scaleTo(svg, View.position.zoom - zoomInterval); -} - -function zoomReset() { - View.position.zoom = 1; - View.position.x = 0; - View.position.y = 0; - svg.call( - zoom.transform, - d3.zoomIdentity.translate(View.position.y, View.position.x).scale(View.position.zoom), - ); - translate(); -} - -hotkeys('e,alt+r', (e) => { - e.preventDefault(); - zoomReset(); -}); - -/** - * Zoom to a node from its coordinates - * @param {string} nodeId - */ - -function zoomToNode(nodeId) { - const node = nodes.find(({ id }) => id === nodeId); - if (!node) return; - const { x, y } = node; - - const meanX = d3.mean(nodes, (d) => d.x); - const meanY = d3.mean(nodes, (d) => d.y); - - const zoomScale = - View.position.zoom === 1 ? View.position.zoom + zoomInterval * 2 : View.position.zoom; - - svg.call( - zoom.transform, - d3.zoomIdentity.translate(-(x * zoomScale - meanX), -(y * zoomScale - meanY)).scale(zoomScale), - ); - translate(); -} - -hotkeys('c', (e) => { - e.preventDefault(); - const recordId = getRecordIdFromHash(); - - zoomToNode(recordId); -}); - -window.zoomMore = zoomMore; -window.zoomLess = zoomLess; -window.zoomReset = zoomReset; - -export { zoomToNode };