Skip to content

Commit a6a7351

Browse files
committed
Use (and re-use) the same clip-path when two renders are clipped with the same shape
1 parent cce2ff5 commit a6a7351

File tree

1 file changed

+37
-26
lines changed

1 file changed

+37
-26
lines changed

src/style.js

Lines changed: 37 additions & 26 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,42 +306,53 @@ export function maybeClip(clip) {
306306
return clip;
307307
}
308308

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+
309315
// Note: may mutate selection.node!
310316
function applyClip(selection, mark, dimensions, context) {
311317
let clipUrl;
312318
const {clip = context.clip} = mark;
313319
switch (clip) {
314320
case "frame": {
315-
const {width, height, marginLeft, marginRight, marginTop, marginBottom} = dimensions;
316-
const id = getClipId();
317-
clipUrl = `url(#${id})`;
318-
selection = create("svg:g", context)
319-
.call((g) =>
320-
g
321-
.append("svg:clipPath")
322-
.attr("id", id)
323-
.append("rect")
324-
.attr("x", marginLeft)
325-
.attr("y", marginTop)
326-
.attr("width", width - marginRight - marginLeft)
327-
.attr("height", height - marginTop - marginBottom)
328-
)
329-
.each(function () {
330-
this.appendChild(selection.node());
331-
selection.node = () => this; // Note: mutation!
332-
});
321+
const clips = context.clips ?? (context.clips = new Map());
322+
if (!clips.has("frame")) {
323+
const {width, height, marginLeft, marginRight, marginTop, marginBottom} = dimensions;
324+
const id = getClipId();
325+
clips.set("frame", id);
326+
clipDefs(context)
327+
.append("clipPath")
328+
.attr("id", id)
329+
.append("rect")
330+
.attr("x", marginLeft)
331+
.attr("y", marginTop)
332+
.attr("width", width - marginRight - marginLeft)
333+
.attr("height", height - marginTop - marginBottom);
334+
}
335+
selection = create("svg:g", context).each(function () {
336+
this.appendChild(selection.node());
337+
selection.node = () => this; // Note: mutation!
338+
});
339+
clipUrl = `url(#${clips.get("frame")})`;
333340
break;
334341
}
335342
case "sphere": {
343+
const clips = context.clips ?? (context.clips = new Map());
336344
const {projection} = context;
337345
if (!projection) throw new Error(`the "sphere" clip option requires a projection`);
338-
const id = getClipId();
339-
clipUrl = `url(#${id})`;
340-
selection
341-
.append("clipPath")
342-
.attr("id", id)
343-
.append("path")
344-
.attr("d", geoPath(projection)({type: "Sphere"}));
346+
if (!clips.has("projection")) {
347+
const id = getClipId();
348+
clips.set("projection", id);
349+
clipDefs(context)
350+
.append("clipPath")
351+
.attr("id", id)
352+
.append("path")
353+
.attr("d", geoPath(projection)({type: "Sphere"}));
354+
}
355+
clipUrl = `url(#${clips.get("projection")})`;
345356
break;
346357
}
347358
}

0 commit comments

Comments
 (0)