mirror of
https://github.com/PeWu/topola-viewer.git
synced 2026-05-26 23:26:15 +00:00
Improved zoom support
This commit is contained in:
@@ -21,19 +21,17 @@ import {
|
|||||||
* @param size the size of the chart
|
* @param size the size of the chart
|
||||||
*/
|
*/
|
||||||
function zoomed(size: [number, number], enableZoom: boolean) {
|
function zoomed(size: [number, number], enableZoom: boolean) {
|
||||||
const svg = d3.select('#chart');
|
const parent = d3.select('#svgContainer').node() as Element;
|
||||||
const parent = (svg.node() as HTMLElement).parentElement!;
|
|
||||||
|
|
||||||
if (enableZoom) {
|
if (enableZoom) {
|
||||||
const scale = d3.event.transform.k;
|
const scale = d3.event.transform.k;
|
||||||
const offsetX = d3.max([0, (parent.clientWidth / scale - size[0]) / 2])!;
|
const offsetX = d3.max([0, (parent.clientWidth - size[0] * scale) / 2]);
|
||||||
const offsetY = d3.max([0, (parent.clientHeight / scale - size[1]) / 2])!;
|
const offsetY = d3.max([0, (parent.clientHeight - size[1] * scale) / 2]);
|
||||||
svg.attr(
|
d3.select('#chartSvg')
|
||||||
'transform',
|
.attr('width', size[0] * scale)
|
||||||
`translate(${-size[0] / 2}, ${-size[1] / 2})
|
.attr('height', size[1] * scale)
|
||||||
scale(${scale})
|
.attr('transform', `translate(${offsetX}, ${offsetY})`);
|
||||||
translate(${size[0] / 2 + offsetX}, ${size[1] / 2 + offsetY})`,
|
d3.select('#chart').attr('transform', `scale(${scale})`);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
parent.scrollLeft = -d3.event.transform.x;
|
parent.scrollLeft = -d3.event.transform.x;
|
||||||
@@ -42,8 +40,7 @@ function zoomed(size: [number, number], enableZoom: boolean) {
|
|||||||
|
|
||||||
/** Called when the scrollbars are used. */
|
/** Called when the scrollbars are used. */
|
||||||
function scrolled(enableZoom: boolean) {
|
function scrolled(enableZoom: boolean) {
|
||||||
const svg = d3.select('#chart');
|
const parent = d3.select('#svgContainer').node() as Element;
|
||||||
const parent = (svg.node() as HTMLElement).parentElement as Element;
|
|
||||||
const x = parent.scrollLeft + parent.clientWidth / 2;
|
const x = parent.scrollLeft + parent.clientWidth / 2;
|
||||||
const y = parent.scrollTop + parent.clientHeight / 2;
|
const y = parent.scrollTop + parent.clientHeight / 2;
|
||||||
const scale = enableZoom ? d3.zoomTransform(parent).k : 1;
|
const scale = enableZoom ? d3.zoomTransform(parent).k : 1;
|
||||||
@@ -200,8 +197,10 @@ export class Chart extends React.PureComponent<ChartProps, {}> {
|
|||||||
startIndi: this.props.selection.id,
|
startIndi: this.props.selection.id,
|
||||||
baseGeneration: this.props.selection.generation,
|
baseGeneration: this.props.selection.generation,
|
||||||
});
|
});
|
||||||
const svg = d3.select('#chart');
|
const svg = d3.select('#chartSvg');
|
||||||
const parent = (svg.node() as HTMLElement).parentElement as Element;
|
const parent = d3.select('#svgContainer').node() as Element;
|
||||||
|
|
||||||
|
const scale = this.props.enableZoom ? d3.zoomTransform(parent).k : 1;
|
||||||
|
|
||||||
d3.select(parent)
|
d3.select(parent)
|
||||||
.on('scroll', () => scrolled(this.props.enableZoom))
|
.on('scroll', () => scrolled(this.props.enableZoom))
|
||||||
@@ -230,10 +229,16 @@ export class Chart extends React.PureComponent<ChartProps, {}> {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const dx = parent.clientWidth / 2 - chartInfo.origin[0];
|
const dx = parent.clientWidth / 2 - chartInfo.origin[0] * scale;
|
||||||
const dy = parent.clientHeight / 2 - chartInfo.origin[1];
|
const dy = parent.clientHeight / 2 - chartInfo.origin[1] * scale;
|
||||||
const offsetX = d3.max([0, (parent.clientWidth - chartInfo.size[0]) / 2]);
|
const offsetX = d3.max([
|
||||||
const offsetY = d3.max([0, (parent.clientHeight - chartInfo.size[1]) / 2]);
|
0,
|
||||||
|
(parent.clientWidth - chartInfo.size[0] * scale) / 2,
|
||||||
|
]);
|
||||||
|
const offsetY = d3.max([
|
||||||
|
0,
|
||||||
|
(parent.clientHeight - chartInfo.size[1] * scale) / 2,
|
||||||
|
]);
|
||||||
const svgTransition = svg
|
const svgTransition = svg
|
||||||
.transition()
|
.transition()
|
||||||
.delay(200)
|
.delay(200)
|
||||||
@@ -241,8 +246,8 @@ export class Chart extends React.PureComponent<ChartProps, {}> {
|
|||||||
const transition = args.initialRender ? svg : svgTransition;
|
const transition = args.initialRender ? svg : svgTransition;
|
||||||
transition
|
transition
|
||||||
.attr('transform', `translate(${offsetX}, ${offsetY})`)
|
.attr('transform', `translate(${offsetX}, ${offsetY})`)
|
||||||
.attr('width', chartInfo.size[0])
|
.attr('width', chartInfo.size[0] * scale)
|
||||||
.attr('height', chartInfo.size[1]);
|
.attr('height', chartInfo.size[1] * scale);
|
||||||
if (args.initialRender) {
|
if (args.initialRender) {
|
||||||
parent.scrollLeft = -dx;
|
parent.scrollLeft = -dx;
|
||||||
parent.scrollTop = -dy;
|
parent.scrollTop = -dy;
|
||||||
@@ -280,20 +285,40 @@ export class Chart extends React.PureComponent<ChartProps, {}> {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div id="svgContainer">
|
<div id="svgContainer">
|
||||||
<svg id="chart" />
|
<svg id="chartSvg">
|
||||||
|
<g id="chart" />
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getSvgContents() {
|
/** Return a copy of the SVG chart but without scaling and positioning. */
|
||||||
const svg = document.getElementById('chart')!.cloneNode(true) as Element;
|
private getStrippedSvg() {
|
||||||
|
const svg = document.getElementById('chartSvg')!.cloneNode(true) as Element;
|
||||||
|
|
||||||
svg.removeAttribute('transform');
|
svg.removeAttribute('transform');
|
||||||
return new XMLSerializer().serializeToString(svg);
|
if (this.props.enableZoom) {
|
||||||
|
const parent = d3.select('#svgContainer').node() as Element;
|
||||||
|
const scale = d3.zoomTransform(parent).k;
|
||||||
|
svg.setAttribute(
|
||||||
|
'width',
|
||||||
|
String(Number(svg.getAttribute('width')) / scale),
|
||||||
|
);
|
||||||
|
svg.setAttribute(
|
||||||
|
'height',
|
||||||
|
String(Number(svg.getAttribute('height')) / scale),
|
||||||
|
);
|
||||||
|
svg.querySelector('#chart')!.removeAttribute('transform');
|
||||||
|
}
|
||||||
|
return svg;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getSvgContents() {
|
||||||
|
return new XMLSerializer().serializeToString(this.getStrippedSvg());
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getSvgContentsWithInlinedImages() {
|
private async getSvgContentsWithInlinedImages() {
|
||||||
const svg = document.getElementById('chart')!.cloneNode(true) as Element;
|
const svg = this.getStrippedSvg();
|
||||||
svg.removeAttribute('transform');
|
|
||||||
await inlineImages(svg);
|
await inlineImages(svg);
|
||||||
return new XMLSerializer().serializeToString(svg);
|
return new XMLSerializer().serializeToString(svg);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user