Public
Edited
Oct 24, 2023
2 forks
23 stars
Also listed in…
d3-interpolate
Insert cell
Insert cell
Insert cell
start = [30, 30, 40] // start with a view centered at [30, 30] and a size of 40
Insert cell
end = [135, 85, 60] // end with a view centered at [135, 80] and a size of 60
Insert cell
Insert cell
Insert cell
svg`<svg viewBox="-2 -2 264 194" style="max-width: 600px">
<g id=view transform="translate(0, 0) scale(1)">
${scene}
</g>
</svg>`
Insert cell
Insert cell
w = 260
Insert cell
h = 190
Insert cell
Insert cell
k = Math.min(w, h) / start[2]
Insert cell
Insert cell
translate = [w / 2 - start[0] * k, h / 2 - start[1] * k]
Insert cell
Insert cell
transformStart = `translate(${translate}) scale(${k})`
Insert cell
svg`<svg viewBox="-2 -2 264 194" style="max-width: 600px">
<g id=view transform="${transformStart}">
${scene}
</g>
</svg>`
Insert cell
Insert cell
interpolator = d3.interpolateZoom(start, end)
Insert cell
function transform(t) {
const view = interpolator(t);

const k = Math.min(w, h) / view[2]; // scale
const translate = [w / 2 - view[0] * k, h / 2 - view[1] * k]; // translate

return `translate(${translate}) scale(${k})`;
}
Insert cell
Insert cell
interpolator(t)
Insert cell
transform(t)
Insert cell
svg`<svg viewBox="-2 -2 264 194" style="max-width: 600px">
<g id=view transform="${transform(t)}">
${scene}
</g>
</svg>`
Insert cell
Insert cell
duration = interpolator.duration * 1.5 // 33% slower than the recommendation
Insert cell
Insert cell
{
await visibility(replay);
const s = svg`<svg viewBox="-2 -2 264 194" style="max-width: 600px">
<g id=view transform="${transform(0)}">
${scene}
</g>
</svg>`;
yield s;

const view = d3.select(s).select("#view");

view
.transition()
.delay(1000)
.duration(duration)
.attrTween("transform", () => transform);
}
Insert cell
Insert cell
{
const s = svg`<svg viewBox="-2 -2 264 194" style="max-width: 600px">
<g id=view transform="${transform(0)}">
${scene}
</g>
</svg>`;

const transform_backward = t => transform(1 - t);
let forward = false;

d3.select(s)
.select("#view")
.call(transition);

return s;

function transition(view) {
forward = !forward;
view
.transition()
.delay(2000)
.duration(duration)
.attrTween("transform", () => (forward ? transform : transform_backward))
.on("end", () => transition(view));
}
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
scene = `
<g id=grid>
${d3
.ticks(0, 260, 30)
.map(x => `<line x1=${x} x2=${x} y1=0 y2=190 />`)
.join("\n")}
${d3
.ticks(0, 190, 20)
.map(y => `<line x1=0 x2=260 y1=${y} y2=${y} />`)
.join("\n")}
</g>

<g id=start>
<rect class=bbox1 x=${start[0] - start[2] / 2}
y=${start[1] - start[2] / 2}
width=${start[2]}
height=${start[2]}
></rect>
<circle cx=30 cy=30 r=13></circle>
</g>

<g id=end>
<rect class=bbox2 x=${end[0] - end[2] / 2}
y=${end[1] - end[2] / 2}
width=${end[2]} height=${end[2]}></rect>
<g transform="translate(135, 85)">
<path d="${d3
.symbol()
.type(d3.symbolStar)
.size(900)()}"></path>
</g>
</g>

<style>
svg #grid line { stroke: #aaa; stroke-width: 0.5 }
svg .bbox1 { stroke: red; stroke-width: 1; fill: none }
svg .bbox2 { stroke: blue; stroke-width: 1; fill: none }
svg * { vector-effect: non-scaling-stroke }
</style>
`
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more