Viewbox = ([W, H], bounds, onchange) => {
const [W2, H2] = [W / 2, H / 2];
let xc, yc, ratio;
const getBounds = () => {
const [wr, hr] = [W2 / ratio, H2 / ratio];
return [
[xc - wr, yc - hr],
[xc + wr, yc + hr]
];
};
const notify = () => onchange && onchange(getBounds());
const setBounds = ([[xmin, ymin], [xmax, ymax]]) => {
[xc, yc] = [(xmin + xmax) / 2, (ymin + ymax) / 2];
const [dx, dy] = [xmax - xmin, ymax - ymin];
ratio = dx && dy ? Math.min(W / dx, H / dy) : 1;
notify();
};
setBounds(
bounds || [
[0, 0],
[W, H]
]
);
return {
getBounds,
setBounds,
translate: (dx, dy) => ((xc += dx), (yc += dy), notify()),
zoom: ([xz, yz], f) => {
xc = f * (xc - xz) + xz;
yc = f * (yc - yz) + yz;
ratio /= f;
notify();
},
coords: ([x, y]) => [W2 + (x - xc) * ratio, H2 + (y - yc) * ratio],
coordsI: ([x, y]) => [(x - W2) / ratio + xc, (y - H2) / ratio + yc],
scale: (val) => val * ratio,
scaleI: (val) => val / ratio
};
}