Skip to content

Commit bf69ec4

Browse files
committed
unique clip paths
- smaller files - better interaction performance
1 parent ab59f39 commit bf69ec4

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";
@@ -313,42 +313,53 @@ export function maybeClip(clip) {
313313
return maybeKeyword(clip, "clip", ["frame", "sphere"]);
314314
}
315315

316-
// TODO avoid creating a new clip-path each time?
316+
function clipDefs({ownerSVGElement}) {
317+
const svg = select(ownerSVGElement);
318+
const defs = svg.select("defs.clip");
319+
return defs.size() ? defs : svg.insert("defs", ":first-child").attr("class", "clip");
320+
}
321+
317322
// Note: may mutate selection.node!
323+
const frame = Symbol("frame");
318324
function applyClip(selection, mark, dimensions, context) {
319325
let clipUrl;
320326
switch (mark.clip) {
321327
case "frame": {
322-
const {width, height, marginLeft, marginRight, marginTop, marginBottom} = dimensions;
323-
const id = getClipId();
324-
clipUrl = `url(#${id})`;
325-
selection = create("svg:g", context)
326-
.call((g) =>
327-
g
328-
.append("svg: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-
.each(function () {
337-
this.appendChild(selection.node());
338-
selection.node = () => this; // Note: mutation!
339-
});
328+
const clips = context.clips ?? (context.clips = new WeakMap());
329+
if (!clips.has(frame)) {
330+
const {width, height, marginLeft, marginRight, marginTop, marginBottom} = dimensions;
331+
const id = getClipId();
332+
clips.set(frame, id);
333+
clipDefs(context)
334+
.append("clipPath")
335+
.attr("id", id)
336+
.append("rect")
337+
.attr("x", marginLeft)
338+
.attr("y", marginTop)
339+
.attr("width", width - marginRight - marginLeft)
340+
.attr("height", height - marginTop - marginBottom);
341+
}
342+
selection = create("svg:g", context).each(function () {
343+
this.appendChild(selection.node());
344+
selection.node = () => this; // Note: mutation!
345+
});
346+
clipUrl = `url(#${clips.get(frame)})`;
340347
break;
341348
}
342349
case "sphere": {
350+
const clips = context.clips ?? (context.clips = new WeakMap());
343351
const {projection} = context;
344352
if (!projection) throw new Error(`the "sphere" clip option requires a projection`);
345-
const id = getClipId();
346-
clipUrl = `url(#${id})`;
347-
selection
348-
.append("clipPath")
349-
.attr("id", id)
350-
.append("path")
351-
.attr("d", geoPath(projection)({type: "Sphere"}));
353+
if (!clips.has(projection)) {
354+
const id = getClipId();
355+
clips.set(projection, id);
356+
clipDefs(context)
357+
.append("clipPath")
358+
.attr("id", id)
359+
.append("path")
360+
.attr("d", geoPath(projection)({type: "Sphere"}));
361+
}
362+
clipUrl = `url(#${clips.get(projection)})`;
352363
break;
353364
}
354365
}

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)