Skip to content

Commit b35d484

Browse files
committed
unique clip paths
- smaller files - better interaction performance
1 parent 68fd597 commit b35d484

18 files changed

+323
-879
lines changed

src/style.js

Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {geoPath, group, namespaces} from "d3";
1+
import {geoPath, group, namespaces, select} from "d3";
22
import {create} from "./context.js";
33
import {defined, nonempty} from "./defined.js";
44
import {formatDefault} from "./format.js";
@@ -306,43 +306,54 @@ export function maybeClip(clip) {
306306
return clip;
307307
}
308308

309-
// TODO avoid creating a new clip-path each time?
309+
function clipDefs({ownerSVGElement}) {
310+
const svg = select(ownerSVGElement);
311+
const defs = svg.select("defs.clip");
312+
return defs.size() ? defs : svg.insert("defs", ":first-child").attr("class", "clip");
313+
}
314+
310315
// Note: may mutate selection.node!
316+
const frame = Symbol("frame");
311317
function applyClip(selection, mark, dimensions, context) {
312318
let clipUrl;
313319
const {clip = context.clip} = mark;
314320
switch (clip) {
315321
case "frame": {
316-
const {width, height, marginLeft, marginRight, marginTop, marginBottom} = dimensions;
317-
const id = getClipId();
318-
clipUrl = `url(#${id})`;
319-
selection = create("svg:g", context)
320-
.call((g) =>
321-
g
322-
.append("svg:clipPath")
323-
.attr("id", id)
324-
.append("rect")
325-
.attr("x", marginLeft)
326-
.attr("y", marginTop)
327-
.attr("width", width - marginRight - marginLeft)
328-
.attr("height", height - marginTop - marginBottom)
329-
)
330-
.each(function () {
331-
this.appendChild(selection.node());
332-
selection.node = () => this; // Note: mutation!
333-
});
322+
const clips = context.clips ?? (context.clips = new WeakMap());
323+
if (!clips.has(frame)) {
324+
const {width, height, marginLeft, marginRight, marginTop, marginBottom} = dimensions;
325+
const id = getClipId();
326+
clips.set(frame, id);
327+
clipDefs(context)
328+
.append("clipPath")
329+
.attr("id", id)
330+
.append("rect")
331+
.attr("x", marginLeft)
332+
.attr("y", marginTop)
333+
.attr("width", width - marginRight - marginLeft)
334+
.attr("height", height - marginTop - marginBottom);
335+
}
336+
selection = create("svg:g", context).each(function () {
337+
this.appendChild(selection.node());
338+
selection.node = () => this; // Note: mutation!
339+
});
340+
clipUrl = `url(#${clips.get(frame)})`;
334341
break;
335342
}
336343
case "sphere": {
344+
const clips = context.clips ?? (context.clips = new WeakMap());
337345
const {projection} = context;
338346
if (!projection) throw new Error(`the "sphere" clip option requires a projection`);
339-
const id = getClipId();
340-
clipUrl = `url(#${id})`;
341-
selection
342-
.append("clipPath")
343-
.attr("id", id)
344-
.append("path")
345-
.attr("d", geoPath(projection)({type: "Sphere"}));
347+
if (!clips.has(projection)) {
348+
const id = getClipId();
349+
clips.set(projection, id);
350+
clipDefs(context)
351+
.append("clipPath")
352+
.attr("id", id)
353+
.append("path")
354+
.attr("d", geoPath(projection)({type: "Sphere"}));
355+
}
356+
clipUrl = `url(#${clips.get(projection)})`;
346357
break;
347358
}
348359
}

test/output/armadillo.svg

Lines changed: 6 additions & 7 deletions
Loading

test/output/bandClip.svg

Lines changed: 5 additions & 3 deletions
Loading

test/output/bandClip2.svg

Lines changed: 5 additions & 3 deletions
Loading

0 commit comments

Comments
 (0)