Skip to content

Commit

Permalink
fix(Canvas/GeoPath): Fix tooltip ghosting (recreate geoPath() when `g…
Browse files Browse the repository at this point in the history
…eojson` data changes). Fix tooltip path performance by rendering to separate <Canvas>
  • Loading branch information
techniq committed Jan 7, 2025
1 parent 4738e04 commit 0f531c6
Show file tree
Hide file tree
Showing 7 changed files with 37 additions and 10 deletions.
11 changes: 8 additions & 3 deletions packages/layerchart/src/lib/components/GeoPath.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
} from 'd3-geo';
import { cls } from '@layerstack/tailwind';
import { computedStyles } from '@layerstack/svelte-actions';
import { merge } from 'lodash-es';
import { geoContext } from './GeoContext.svelte';
import type { TooltipContextValue } from './tooltip/TooltipContext.svelte';
Expand Down Expand Up @@ -67,6 +68,11 @@
$: _projection = geoTransform ? d3geoTransform(geoTransform($geo)) : $geo;
$: geoPath = geoCurvePath(_projection, curve);
$: {
// Recreate `geoPath()` if `geojson` data changes (fixes ghosting issue when rendering to canvas)
geojson;
geoPath = geoCurvePath(_projection, curve);
}
const canvasContext = getCanvasContext();
const renderContext = canvasContext ? 'canvas' : 'svg';
Expand All @@ -79,8 +85,7 @@
if (geojson) {
// console.log('rendering', _styles.fill);
const pathData = geoPath(geojson);
// renderPathData(ctx, pathData, { ..._styles, fill, stroke, strokeWidth });
renderPathData(ctx, pathData, { ..._styles });
renderPathData(ctx, pathData, merge({}, _styles, { fill, stroke, strokeWidth }));
}
}
}
Expand All @@ -91,7 +96,7 @@
$: if (renderContext === 'canvas') {
// Redraw when geojson, projection, or class change
geojson && _projection && className;
geojson && _projection && className && fill && stroke && strokeWidth;
canvasContext.invalidate();
}
Expand Down
17 changes: 13 additions & 4 deletions packages/layerchart/src/lib/components/layout/Canvas.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,13 @@
function update() {
if (!context) return;
// TODO: only `scaleCanvas()` when containerWidth/Height change (not all invalidations)
// scaleCanvas in `update()` to fix `requestAnimationFrame()` timing causing flash of blank canvas
scaleCanvas(context, $containerWidth, $containerHeight);
context.clearRect(0, 0, $containerWidth, $containerHeight);
context.translate($padding.left ?? 0, $padding.top ?? 0);
if (mode === 'canvas') {
const center = { x: $width / 2, y: $height / 2 };
const newTranslate = {
Expand All @@ -92,7 +94,6 @@
context.scale($scale, $scale);
}
// console.log({ drawFunctions });
drawFunctions.forEach((fn) => {
context.save();
fn(context);
Expand All @@ -102,7 +103,7 @@
pendingInvalidation = false;
}
$: setCanvasContext({
const canvasContext: CanvasContext = {
register(fn) {
drawFunctions.push(fn);
this.invalidate();
Expand All @@ -116,7 +117,15 @@
pendingInvalidation = true;
frameId = requestAnimationFrame(update);
},
});
};
$: {
// Redraw when resized
$containerWidth, $containerHeight;
canvasContext.invalidate();
}
setCanvasContext(canvasContext);
</script>

<canvas
Expand Down
5 changes: 4 additions & 1 deletion packages/layerchart/src/lib/utils/canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ export function renderPathData(

const stroke = styles.stroke === 'none' ? null : styles.stroke;
if (stroke) {
canvasCtx.lineWidth = Number(styles.strokeWidth?.replace('px', ''));
canvasCtx.lineWidth =
typeof styles.strokeWidth === 'string'
? Number(styles.strokeWidth?.replace('px', ''))
: (styles.strokeWidth ?? 0);
canvasCtx.strokeStyle = stroke;
canvasCtx.stroke(path);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@
<Canvas>
<GeoPath geojson={states} class="stroke-surface-content" />
<GeoPath geojson={counties} class="stroke-surface-content/20" />
</Canvas>

<!-- Provides better performance by rendering tooltip path on separate <Canvas> -->
<Canvas>
{#if tooltip.data}
<GeoPath geojson={tooltip.data} class="stroke-surface-content fill-surface-content/20" />
{/if}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,10 @@
<Graticule class="stroke-surface-content/20" />
<GeoPath geojson={countries} class="stroke-surface-content/50 fill-white" />
<GeoPath geojson={selectedFeature} class="stroke-primary-900 fill-primary" />
</Canvas>

<!-- Provides better performance by rendering tooltip path on separate <Canvas> -->
<Canvas>
{#if tooltip.data}
<GeoPath geojson={tooltip.data} class="fill-surface-content/20" />
{/if}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@
<Canvas>
<GeoPath geojson={states} class="stroke-surface-content" />
<GeoPath geojson={counties} class="stroke-surface-content/20" />
</Canvas>

<!-- Provides better performance by rendering tooltip path on separate <Canvas> -->
<Canvas>
{#if tooltip.data}
<GeoPath geojson={tooltip.data} class="stroke-surface-content fill-surface-content/20" />
{/if}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -353,9 +353,7 @@
class="stroke-surface-content fill-surface-100 hover:fill-surface-content/10"
strokeWidth={1 / transform.scale}
/>
</Canvas>

<Canvas>
<!-- TODO: Fade in with delay like SVG -->
<!-- <g in:fade={{ duration: 300, delay: 600 }} out:fade={{ duration: 300 }}> -->
<GeoPath
Expand All @@ -371,6 +369,7 @@
<!-- </g> -->
</Canvas>

<!-- Provides better performance by rendering tooltip path on separate <Canvas> -->
{#if tooltip.data}
<Canvas>
<GeoPath
Expand Down

0 comments on commit 0f531c6

Please sign in to comment.