layerchart 0.60.3 → 0.70.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Arc.svelte +83 -27
- package/dist/components/Area.svelte +51 -12
- package/dist/components/Bar.svelte +5 -1
- package/dist/components/Circle.svelte +54 -12
- package/dist/components/ComputedStyles.svelte +16 -0
- package/dist/components/ComputedStyles.svelte.d.ts +20 -0
- package/dist/components/GeoPath.svelte +36 -44
- package/dist/components/GeoPath.svelte.d.ts +2 -2
- package/dist/components/GeoPoint.svelte +27 -13
- package/dist/components/GeoTile.svelte +22 -7
- package/dist/components/Graticule.svelte.d.ts +4 -4
- package/dist/components/Group.svelte +46 -18
- package/dist/components/HitCanvas.svelte +7 -21
- package/dist/components/Legend.svelte.d.ts +1 -1
- package/dist/components/Line.svelte +70 -31
- package/dist/components/LinearGradient.svelte +89 -26
- package/dist/components/LinearGradient.svelte.d.ts +2 -2
- package/dist/components/Points.svelte +23 -41
- package/dist/components/Points.svelte.d.ts +2 -1
- package/dist/components/RadialGradient.svelte +86 -28
- package/dist/components/RadialGradient.svelte.d.ts +2 -2
- package/dist/components/Rect.svelte +62 -18
- package/dist/components/Rule.svelte +1 -1
- package/dist/components/Spline.svelte +95 -57
- package/dist/components/Text.svelte +80 -22
- package/dist/components/TransformControls.svelte.d.ts +1 -1
- package/dist/components/charts/AreaChart.svelte +14 -10
- package/dist/components/charts/BarChart.svelte +13 -9
- package/dist/components/charts/LineChart.svelte +13 -9
- package/dist/components/charts/PieChart.svelte +6 -2
- package/dist/components/charts/PieChart.svelte.d.ts +2 -1
- package/dist/components/charts/ScatterChart.svelte +14 -10
- package/dist/components/layout/Canvas.svelte +93 -11
- package/dist/components/layout/Canvas.svelte.d.ts +13 -0
- package/dist/components/layout/Svg.svelte +1 -1
- package/dist/components/tooltip/Tooltip.svelte.d.ts +1 -1
- package/dist/utils/canvas.d.ts +46 -0
- package/dist/utils/canvas.js +146 -0
- package/dist/utils/common.d.ts +10 -1
- package/dist/utils/common.js +13 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/math.d.ts +2 -0
- package/dist/utils/math.js +10 -0
- package/dist/utils/path.d.ts +7 -0
- package/dist/utils/path.js +17 -3
- package/package.json +2 -2
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
// https://svelte.dev/repl/09711e43a1264ba18945d7db7cab9335?version=3.38.2
|
|
18
18
|
// https://codepen.io/simeydotme/pen/rrOEmO/
|
|
19
19
|
|
|
20
|
-
import { tick } from 'svelte';
|
|
20
|
+
import { onDestroy, tick } from 'svelte';
|
|
21
21
|
import type { SVGAttributes } from 'svelte/elements';
|
|
22
22
|
import type { spring as springStore, tweened as tweenedStore } from 'svelte/motion';
|
|
23
23
|
import { arc as d3arc } from 'd3-shape';
|
|
@@ -28,6 +28,8 @@
|
|
|
28
28
|
import { motionStore } from '../stores/motionStore.js';
|
|
29
29
|
import { degreesToRadians } from '../utils/math.js';
|
|
30
30
|
import type { TooltipContextValue } from './tooltip/TooltipContext.svelte';
|
|
31
|
+
import { getCanvasContext } from './layout/Canvas.svelte';
|
|
32
|
+
import { renderPathData } from '../utils/canvas.js';
|
|
31
33
|
|
|
32
34
|
export let spring: boolean | Parameters<typeof springStore>[1] = undefined;
|
|
33
35
|
export let tweened: boolean | Parameters<typeof tweenedStore>[1] = undefined;
|
|
@@ -80,6 +82,11 @@
|
|
|
80
82
|
export let padAngle = 0;
|
|
81
83
|
// export let padRadius = 0;
|
|
82
84
|
|
|
85
|
+
export let fill: string | undefined = undefined;
|
|
86
|
+
export let fillOpacity: number | undefined = undefined;
|
|
87
|
+
export let stroke: string | undefined = undefined;
|
|
88
|
+
export let strokeWidth: number | undefined = undefined;
|
|
89
|
+
|
|
83
90
|
export let track: boolean | SVGAttributes<SVGPathElement> = false;
|
|
84
91
|
|
|
85
92
|
const { yRange } = chartContext();
|
|
@@ -183,36 +190,85 @@
|
|
|
183
190
|
* Data to set when showing tooltip
|
|
184
191
|
*/
|
|
185
192
|
export let data: any = undefined;
|
|
193
|
+
|
|
194
|
+
const canvasContext = getCanvasContext();
|
|
195
|
+
const renderContext = canvasContext ? 'canvas' : 'svg';
|
|
196
|
+
|
|
197
|
+
function render(ctx: CanvasRenderingContext2D) {
|
|
198
|
+
ctx.translate(xOffset, yOffset);
|
|
199
|
+
|
|
200
|
+
// Track
|
|
201
|
+
const trackProps = { ...(typeof track === 'object' ? track : null) };
|
|
202
|
+
renderPathData(ctx, trackArc(), {
|
|
203
|
+
styles: {
|
|
204
|
+
fill: trackProps['fill'] ?? undefined,
|
|
205
|
+
fillOpacity: trackProps['fill-opacity'] ?? undefined,
|
|
206
|
+
stroke: trackProps['stroke'] ?? undefined,
|
|
207
|
+
strokeWidth: trackProps['stroke-width'] ?? undefined,
|
|
208
|
+
opacity: trackProps['opacity'] ?? undefined,
|
|
209
|
+
},
|
|
210
|
+
classes: trackProps.class ?? undefined,
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// Arc
|
|
214
|
+
renderPathData(ctx, arc(), {
|
|
215
|
+
styles: { fill, fillOpacity, stroke, strokeWidth },
|
|
216
|
+
classes: $$props.class,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
let canvasUnregister: ReturnType<typeof canvasContext.register>;
|
|
221
|
+
$: if (renderContext === 'canvas') {
|
|
222
|
+
canvasUnregister = canvasContext.register({ name: 'Arc', render });
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
$: if (renderContext === 'canvas') {
|
|
226
|
+
// Redraw when props changes (TODO: styles, class, etc)
|
|
227
|
+
arc && trackArc;
|
|
228
|
+
canvasContext.invalidate();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
onDestroy(() => {
|
|
232
|
+
if (renderContext === 'canvas') {
|
|
233
|
+
canvasUnregister();
|
|
234
|
+
}
|
|
235
|
+
});
|
|
186
236
|
</script>
|
|
187
237
|
|
|
188
|
-
{#if
|
|
238
|
+
{#if renderContext === 'svg'}
|
|
239
|
+
{#if track}
|
|
240
|
+
<path
|
|
241
|
+
d={trackArc()}
|
|
242
|
+
class="track"
|
|
243
|
+
bind:this={trackArcEl}
|
|
244
|
+
{...typeof track === 'object' ? track : null}
|
|
245
|
+
/>
|
|
246
|
+
{/if}
|
|
247
|
+
|
|
248
|
+
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
|
189
249
|
<path
|
|
190
|
-
d={
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
{
|
|
250
|
+
d={arc()}
|
|
251
|
+
transform="translate({xOffset}, {yOffset})"
|
|
252
|
+
{fill}
|
|
253
|
+
fill-opacity={fillOpacity}
|
|
254
|
+
{stroke}
|
|
255
|
+
stroke-width={strokeWidth}
|
|
256
|
+
{...$$restProps}
|
|
257
|
+
on:pointerenter={(e) => tooltip?.show(e, data)}
|
|
258
|
+
on:pointermove={(e) => tooltip?.show(e, data)}
|
|
259
|
+
on:pointerleave={(e) => tooltip?.hide()}
|
|
260
|
+
on:touchmove={(e) => {
|
|
261
|
+
if (tooltip) {
|
|
262
|
+
// Prevent touch to not interfer with pointer when using tooltip
|
|
263
|
+
e.preventDefault();
|
|
264
|
+
}
|
|
265
|
+
}}
|
|
266
|
+
on:click
|
|
267
|
+
on:pointerenter
|
|
268
|
+
on:pointermove
|
|
269
|
+
on:pointerleave
|
|
270
|
+
on:touchmove
|
|
194
271
|
/>
|
|
195
272
|
{/if}
|
|
196
273
|
|
|
197
|
-
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
|
198
|
-
<path
|
|
199
|
-
d={arc()}
|
|
200
|
-
transform="translate({xOffset}, {yOffset})"
|
|
201
|
-
{...$$restProps}
|
|
202
|
-
on:pointerenter={(e) => tooltip?.show(e, data)}
|
|
203
|
-
on:pointermove={(e) => tooltip?.show(e, data)}
|
|
204
|
-
on:pointerleave={(e) => tooltip?.hide()}
|
|
205
|
-
on:touchmove={(e) => {
|
|
206
|
-
if (tooltip) {
|
|
207
|
-
// Prevent touch to not interfer with pointer when using tooltip
|
|
208
|
-
e.preventDefault();
|
|
209
|
-
}
|
|
210
|
-
}}
|
|
211
|
-
on:click
|
|
212
|
-
on:pointerenter
|
|
213
|
-
on:pointermove
|
|
214
|
-
on:pointerleave
|
|
215
|
-
on:touchmove
|
|
216
|
-
/>
|
|
217
|
-
|
|
218
274
|
<slot value={$tweened_value} centroid={trackArcCentroid} {boundingBox} />
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { type ComponentProps } from 'svelte';
|
|
2
|
+
import { onDestroy, type ComponentProps } from 'svelte';
|
|
3
3
|
import type { tweened as tweenedStore } from 'svelte/motion';
|
|
4
4
|
import { type Area, area as d3Area, areaRadial } from 'd3-shape';
|
|
5
5
|
import type { CurveFactory } from 'd3-shape';
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
import Spline from './Spline.svelte';
|
|
15
15
|
import { accessor, type Accessor } from '../utils/common.js';
|
|
16
16
|
import { isScaleBand } from '../utils/scales.js';
|
|
17
|
+
import { renderPathData } from '../utils/canvas.js';
|
|
18
|
+
import { getCanvasContext } from './layout/Canvas.svelte';
|
|
17
19
|
|
|
18
20
|
const {
|
|
19
21
|
data: contextData,
|
|
@@ -23,8 +25,8 @@
|
|
|
23
25
|
y,
|
|
24
26
|
yDomain,
|
|
25
27
|
yRange,
|
|
26
|
-
config,
|
|
27
28
|
radial,
|
|
29
|
+
config,
|
|
28
30
|
} = chartContext();
|
|
29
31
|
|
|
30
32
|
/** Override data instead of using context */
|
|
@@ -52,6 +54,11 @@
|
|
|
52
54
|
/** Enable showing line */
|
|
53
55
|
export let line: boolean | Partial<ComponentProps<Spline>> = false;
|
|
54
56
|
|
|
57
|
+
export let fill: string | undefined = undefined;
|
|
58
|
+
export let fillOpacity: number | undefined = undefined;
|
|
59
|
+
export let stroke: string | undefined = undefined;
|
|
60
|
+
export let strokeWidth: number | undefined = undefined;
|
|
61
|
+
|
|
55
62
|
const xAccessor = x ? accessor(x) : $contextX;
|
|
56
63
|
const y0Accessor = y0 ? accessor(y0) : (d: any) => min($yDomain);
|
|
57
64
|
const y1Accessor = y1 ? accessor(y1) : $y;
|
|
@@ -125,8 +132,34 @@
|
|
|
125
132
|
const d = pathData ?? path(data ?? $contextData);
|
|
126
133
|
tweened_d.set(d ?? '');
|
|
127
134
|
}
|
|
135
|
+
|
|
136
|
+
const canvasContext = getCanvasContext();
|
|
137
|
+
const renderContext = canvasContext ? 'canvas' : 'svg';
|
|
138
|
+
|
|
139
|
+
function render(ctx: CanvasRenderingContext2D) {
|
|
140
|
+
renderPathData(ctx, $tweened_d, {
|
|
141
|
+
styles: { fill, fillOpacity, stroke, strokeWidth },
|
|
142
|
+
classes: $$props.class,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
let canvasUnregister: ReturnType<typeof canvasContext.register>;
|
|
147
|
+
$: if (renderContext === 'canvas') {
|
|
148
|
+
canvasUnregister = canvasContext.register({ name: 'Area', render });
|
|
149
|
+
|
|
150
|
+
tweened_d.subscribe(() => {
|
|
151
|
+
canvasContext.invalidate();
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
onDestroy(() => {
|
|
156
|
+
if (renderContext === 'canvas') {
|
|
157
|
+
canvasUnregister();
|
|
158
|
+
}
|
|
159
|
+
});
|
|
128
160
|
</script>
|
|
129
161
|
|
|
162
|
+
<!-- TODO: Find way to not clear <Canvas> when rendering Spline (remove Area rendering). Idea: https://github.com/techniq/layerchart/issues/158#issuecomment-2543416108 -->
|
|
130
163
|
{#if line}
|
|
131
164
|
<Spline
|
|
132
165
|
{data}
|
|
@@ -139,13 +172,19 @@
|
|
|
139
172
|
/>
|
|
140
173
|
{/if}
|
|
141
174
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
175
|
+
{#if renderContext === 'svg'}
|
|
176
|
+
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
|
177
|
+
<path
|
|
178
|
+
d={$tweened_d}
|
|
179
|
+
clip-path={clipPath}
|
|
180
|
+
{fill}
|
|
181
|
+
fill-opacity={fillOpacity}
|
|
182
|
+
{stroke}
|
|
183
|
+
stroke-width={strokeWidth}
|
|
184
|
+
{...$$restProps}
|
|
185
|
+
class={cls('path-area', $$props.class)}
|
|
186
|
+
on:click
|
|
187
|
+
on:pointermove
|
|
188
|
+
on:pointerleave
|
|
189
|
+
/>
|
|
190
|
+
{/if}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { isScaleBand } from '../utils/scales.js';
|
|
10
10
|
import { accessor, type Accessor } from '../utils/common.js';
|
|
11
11
|
import { greatestAbs } from '@layerstack/utils';
|
|
12
|
+
import { getCanvasContext } from './layout/Canvas.svelte';
|
|
12
13
|
|
|
13
14
|
const { x: xContext, y: yContext, xScale } = chartContext();
|
|
14
15
|
|
|
@@ -106,9 +107,12 @@
|
|
|
106
107
|
z`
|
|
107
108
|
.split('\n')
|
|
108
109
|
.join('');
|
|
110
|
+
|
|
111
|
+
const canvasContext = getCanvasContext();
|
|
112
|
+
const renderContext = canvasContext ? 'canvas' : 'svg';
|
|
109
113
|
</script>
|
|
110
114
|
|
|
111
|
-
{#if _rounded === 'all' || radius === 0}
|
|
115
|
+
{#if (_rounded === 'all' || radius === 0) && renderContext === 'svg'}
|
|
112
116
|
<Rect
|
|
113
117
|
{fill}
|
|
114
118
|
{spring}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { tick } from 'svelte';
|
|
2
|
+
import { onDestroy, tick } from 'svelte';
|
|
3
3
|
import type { spring as springStore, tweened as tweenedStore } from 'svelte/motion';
|
|
4
4
|
import { cls } from '@layerstack/tailwind';
|
|
5
5
|
|
|
6
6
|
import { motionStore } from '../stores/motionStore.js';
|
|
7
|
+
import { getCanvasContext } from './layout/Canvas.svelte';
|
|
8
|
+
import { circlePath } from '../utils/path.js';
|
|
9
|
+
import { renderPathData } from '../utils/canvas.js';
|
|
7
10
|
|
|
8
11
|
export let cx: number = 0;
|
|
9
12
|
export let initialCx = cx;
|
|
@@ -17,6 +20,11 @@
|
|
|
17
20
|
export let spring: boolean | Parameters<typeof springStore>[1] = undefined;
|
|
18
21
|
export let tweened: boolean | Parameters<typeof tweenedStore>[1] = undefined;
|
|
19
22
|
|
|
23
|
+
export let fill: string | undefined = undefined;
|
|
24
|
+
export let fillOpacity: number | undefined = undefined;
|
|
25
|
+
export let stroke: string | undefined = undefined;
|
|
26
|
+
export let strokeWidth: number | undefined = undefined;
|
|
27
|
+
|
|
20
28
|
let tweened_cx = motionStore(initialCx, { spring, tweened });
|
|
21
29
|
let tweened_cy = motionStore(initialCy, { spring, tweened });
|
|
22
30
|
let tweened_r = motionStore(initialR, { spring, tweened });
|
|
@@ -26,16 +34,50 @@
|
|
|
26
34
|
tweened_cy.set(cy);
|
|
27
35
|
tweened_r.set(r);
|
|
28
36
|
});
|
|
37
|
+
|
|
38
|
+
const canvasContext = getCanvasContext();
|
|
39
|
+
const renderContext = canvasContext ? 'canvas' : 'svg';
|
|
40
|
+
|
|
41
|
+
function render(ctx: CanvasRenderingContext2D) {
|
|
42
|
+
const pathData = circlePath({ cx: $tweened_cx, cy: $tweened_cy, r: $tweened_r });
|
|
43
|
+
renderPathData(ctx, pathData, {
|
|
44
|
+
styles: { fill, fillOpacity, stroke, strokeWidth },
|
|
45
|
+
classes: $$props.class,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let canvasUnregister: ReturnType<typeof canvasContext.register>;
|
|
50
|
+
$: if (renderContext === 'canvas') {
|
|
51
|
+
canvasUnregister = canvasContext.register({ name: 'Circle', render });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
$: if (renderContext === 'canvas') {
|
|
55
|
+
// Redraw when props changes (TODO: styles, class, etc)
|
|
56
|
+
$tweened_cx && $tweened_cy && $tweened_r;
|
|
57
|
+
canvasContext.invalidate();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
onDestroy(() => {
|
|
61
|
+
if (renderContext === 'canvas') {
|
|
62
|
+
canvasUnregister();
|
|
63
|
+
}
|
|
64
|
+
});
|
|
29
65
|
</script>
|
|
30
66
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
67
|
+
{#if renderContext === 'svg'}
|
|
68
|
+
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
|
69
|
+
<circle
|
|
70
|
+
cx={$tweened_cx}
|
|
71
|
+
cy={$tweened_cy}
|
|
72
|
+
r={$tweened_r}
|
|
73
|
+
{fill}
|
|
74
|
+
fill-opacity={fillOpacity}
|
|
75
|
+
{stroke}
|
|
76
|
+
stroke-width={strokeWidth}
|
|
77
|
+
class={cls($$props.fill == null && 'fill-surface-content')}
|
|
78
|
+
{...$$restProps}
|
|
79
|
+
on:click
|
|
80
|
+
on:pointermove
|
|
81
|
+
on:pointerleave
|
|
82
|
+
/>
|
|
83
|
+
{/if}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { computedStyles } from '@layerstack/svelte-actions';
|
|
3
|
+
import { cls } from '@layerstack/tailwind';
|
|
4
|
+
|
|
5
|
+
let className: string | undefined = undefined;
|
|
6
|
+
export { className as class };
|
|
7
|
+
|
|
8
|
+
let styles: CSSStyleDeclaration;
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<div
|
|
12
|
+
class={cls('ComputedStyles hidden', className)}
|
|
13
|
+
use:computedStyles={(_styles) => (styles = _styles)}
|
|
14
|
+
></div>
|
|
15
|
+
|
|
16
|
+
<slot {styles} />
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { SvelteComponentTyped } from "svelte";
|
|
2
|
+
declare const __propDef: {
|
|
3
|
+
props: {
|
|
4
|
+
class?: string | undefined;
|
|
5
|
+
};
|
|
6
|
+
events: {
|
|
7
|
+
[evt: string]: CustomEvent<any>;
|
|
8
|
+
};
|
|
9
|
+
slots: {
|
|
10
|
+
default: {
|
|
11
|
+
styles: CSSStyleDeclaration;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
export type ComputedStylesProps = typeof __propDef.props;
|
|
16
|
+
export type ComputedStylesEvents = typeof __propDef.events;
|
|
17
|
+
export type ComputedStylesSlots = typeof __propDef.slots;
|
|
18
|
+
export default class ComputedStyles extends SvelteComponentTyped<ComputedStylesProps, ComputedStylesEvents, ComputedStylesSlots> {
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { createEventDispatcher,
|
|
3
|
-
import type { Readable } from 'svelte/store';
|
|
2
|
+
import { createEventDispatcher, onDestroy } from 'svelte';
|
|
4
3
|
import {
|
|
5
4
|
geoTransform as d3geoTransform,
|
|
6
5
|
type GeoIdentityTransform,
|
|
@@ -10,11 +9,12 @@
|
|
|
10
9
|
} from 'd3-geo';
|
|
11
10
|
import { cls } from '@layerstack/tailwind';
|
|
12
11
|
|
|
13
|
-
import { chartContext } from './ChartContext.svelte';
|
|
14
12
|
import { geoContext } from './GeoContext.svelte';
|
|
15
13
|
import type { TooltipContextValue } from './tooltip/TooltipContext.svelte';
|
|
16
14
|
import { curveLinearClosed, type CurveFactory, type CurveFactoryLineOnly } from 'd3-shape';
|
|
17
15
|
import { geoCurvePath } from '../utils/geo.js';
|
|
16
|
+
import { renderPathData } from '../utils/canvas.js';
|
|
17
|
+
import { getCanvasContext } from './layout/Canvas.svelte';
|
|
18
18
|
|
|
19
19
|
export let geojson: GeoPermissibleObjects | null | undefined = undefined;
|
|
20
20
|
|
|
@@ -22,13 +22,13 @@
|
|
|
22
22
|
export let render:
|
|
23
23
|
| ((
|
|
24
24
|
ctx: CanvasRenderingContext2D,
|
|
25
|
-
options: {
|
|
25
|
+
options: { newGeoPath: () => ReturnType<typeof geoCurvePath> }
|
|
26
26
|
) => any)
|
|
27
27
|
| undefined = undefined;
|
|
28
28
|
|
|
29
29
|
export let fill: string | undefined = undefined;
|
|
30
30
|
export let stroke: string | undefined = undefined;
|
|
31
|
-
export let strokeWidth: number |
|
|
31
|
+
export let strokeWidth: number | undefined = undefined;
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* Tooltip context to setup mouse events to show tooltip for related data
|
|
@@ -53,8 +53,6 @@
|
|
|
53
53
|
click: { geoPath: ReturnType<typeof geoCurvePath>; event: MouseEvent };
|
|
54
54
|
}>();
|
|
55
55
|
|
|
56
|
-
const { containerWidth, containerHeight, padding } = chartContext();
|
|
57
|
-
const canvas = getContext<{ ctx: Readable<CanvasRenderingContext2D> }>('canvas');
|
|
58
56
|
const geo = geoContext();
|
|
59
57
|
|
|
60
58
|
/**
|
|
@@ -68,51 +66,45 @@
|
|
|
68
66
|
$: _projection = geoTransform ? d3geoTransform(geoTransform($geo)) : $geo;
|
|
69
67
|
|
|
70
68
|
$: geoPath = geoCurvePath(_projection, curve);
|
|
69
|
+
$: {
|
|
70
|
+
// Recreate `geoPath()` if `geojson` data changes (fixes ghosting issue when rendering to canvas)
|
|
71
|
+
geojson;
|
|
72
|
+
geoPath = geoCurvePath(_projection, curve);
|
|
73
|
+
}
|
|
71
74
|
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
$: renderContext = canvas ? 'canvas' : 'svg';
|
|
75
|
-
|
|
76
|
-
$: ctx = canvas?.ctx;
|
|
77
|
-
$: if (renderContext === 'canvas' && $ctx) {
|
|
78
|
-
let computedStyles: Partial<CSSStyleDeclaration> = {};
|
|
79
|
-
|
|
80
|
-
// Transfer classes defined on <GeoPath> to <canvas> to enable window.getComputedStyle() retrieval (Tailwind classes, etc)
|
|
81
|
-
if (className) {
|
|
82
|
-
$ctx.canvas.classList.add(...className.split(' '));
|
|
83
|
-
computedStyles = window.getComputedStyle($ctx.canvas);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// console.count('render');
|
|
87
|
-
|
|
88
|
-
// Clear with negative offset due to Canvas `context.translate(...)`
|
|
89
|
-
$ctx.clearRect(-$padding.left, -$padding.top, $containerWidth, $containerHeight);
|
|
75
|
+
const canvasContext = getCanvasContext();
|
|
76
|
+
const renderContext = canvasContext ? 'canvas' : 'svg';
|
|
90
77
|
|
|
78
|
+
function _render(ctx: CanvasRenderingContext2D) {
|
|
91
79
|
if (render) {
|
|
92
|
-
|
|
93
|
-
render($ctx, { geoPath });
|
|
80
|
+
render(ctx, { newGeoPath: () => geoCurvePath(_projection, curve) });
|
|
94
81
|
} else {
|
|
95
|
-
$ctx.beginPath();
|
|
96
|
-
// Set the context here since setting it in `$: geoPath` is a circular reference
|
|
97
|
-
geoPath = geoCurvePath(_projection, curve, $ctx);
|
|
98
82
|
if (geojson) {
|
|
99
|
-
geoPath(geojson);
|
|
83
|
+
const pathData = geoPath(geojson);
|
|
84
|
+
renderPathData(ctx, pathData, {
|
|
85
|
+
styles: { fill, stroke, strokeWidth },
|
|
86
|
+
classes: className,
|
|
87
|
+
});
|
|
100
88
|
}
|
|
101
|
-
|
|
102
|
-
$ctx.fillStyle =
|
|
103
|
-
fill ??
|
|
104
|
-
(computedStyles.fill !== DEFAULT_FILL ? computedStyles.fill : undefined) ??
|
|
105
|
-
'transparent';
|
|
106
|
-
$ctx.fill();
|
|
107
|
-
|
|
108
|
-
$ctx.lineWidth = Number(strokeWidth ?? 0);
|
|
109
|
-
$ctx.strokeStyle =
|
|
110
|
-
(stroke ?? computedStyles.stroke === 'none')
|
|
111
|
-
? 'transparent'
|
|
112
|
-
: (computedStyles.stroke ?? '');
|
|
113
|
-
$ctx.stroke();
|
|
114
89
|
}
|
|
115
90
|
}
|
|
91
|
+
|
|
92
|
+
let canvasUnregister: ReturnType<typeof canvasContext.register>;
|
|
93
|
+
$: if (renderContext === 'canvas') {
|
|
94
|
+
canvasUnregister = canvasContext.register({ name: 'GeoPath', render: _render });
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
$: if (renderContext === 'canvas') {
|
|
98
|
+
// Redraw when geojson, projection, or class change
|
|
99
|
+
geojson && _projection && className && fill && stroke && strokeWidth;
|
|
100
|
+
canvasContext.invalidate();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
onDestroy(() => {
|
|
104
|
+
if (renderContext === 'canvas') {
|
|
105
|
+
canvasUnregister();
|
|
106
|
+
}
|
|
107
|
+
});
|
|
116
108
|
</script>
|
|
117
109
|
|
|
118
110
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
|
@@ -8,11 +8,11 @@ declare const __propDef: {
|
|
|
8
8
|
[x: string]: any;
|
|
9
9
|
geojson?: GeoPermissibleObjects | null | undefined;
|
|
10
10
|
render?: ((ctx: CanvasRenderingContext2D, options: {
|
|
11
|
-
|
|
11
|
+
newGeoPath: () => ReturnType<typeof geoCurvePath>;
|
|
12
12
|
}) => any) | undefined | undefined;
|
|
13
13
|
fill?: string | undefined | undefined;
|
|
14
14
|
stroke?: string | undefined | undefined;
|
|
15
|
-
strokeWidth?: number |
|
|
15
|
+
strokeWidth?: number | undefined | undefined;
|
|
16
16
|
tooltip?: TooltipContextValue | undefined;
|
|
17
17
|
curve?: (CurveFactory | CurveFactoryLineOnly) | undefined;
|
|
18
18
|
class?: string | undefined | undefined;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
import type { Readable } from 'svelte/store';
|
|
2
|
+
import { onDestroy } from 'svelte';
|
|
4
3
|
|
|
5
4
|
import { geoContext } from './GeoContext.svelte';
|
|
6
5
|
import Circle from './Circle.svelte';
|
|
7
6
|
import Group from './Group.svelte';
|
|
7
|
+
import { getCanvasContext } from './layout/Canvas.svelte';
|
|
8
8
|
|
|
9
9
|
/** Latitude */
|
|
10
10
|
export let lat: number;
|
|
@@ -17,24 +17,27 @@
|
|
|
17
17
|
coords: { x: number; y: number }
|
|
18
18
|
) => any = () => {};
|
|
19
19
|
|
|
20
|
-
const canvas = getContext<{ ctx: Readable<CanvasRenderingContext2D> }>('canvas');
|
|
21
20
|
const geo = geoContext();
|
|
22
21
|
|
|
23
22
|
$: [x, y] = $geo([long, lat]) ?? [0, 0];
|
|
24
23
|
|
|
25
|
-
|
|
24
|
+
const canvasContext = getCanvasContext();
|
|
25
|
+
const renderContext = canvasContext ? 'canvas' : 'svg';
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
// Transfer classes defined on <GeoPoint> to <canvas> to enable window.getComputedStyle() retrieval (Tailwind classes, etc)
|
|
32
|
-
if ($$props.class) {
|
|
33
|
-
$ctx.canvas.classList.add(...$$props.class.split(' '));
|
|
34
|
-
}
|
|
27
|
+
function _render(ctx: CanvasRenderingContext2D) {
|
|
28
|
+
render(ctx, { x, y });
|
|
29
|
+
}
|
|
35
30
|
|
|
36
|
-
|
|
31
|
+
let canvasUnregister: ReturnType<typeof canvasContext.register>;
|
|
32
|
+
$: if (renderContext === 'canvas') {
|
|
33
|
+
canvasUnregister = canvasContext.register({ name: 'GeoPoint', render: _render });
|
|
37
34
|
}
|
|
35
|
+
|
|
36
|
+
onDestroy(() => {
|
|
37
|
+
if (renderContext === 'canvas') {
|
|
38
|
+
canvasUnregister();
|
|
39
|
+
}
|
|
40
|
+
});
|
|
38
41
|
</script>
|
|
39
42
|
|
|
40
43
|
{#if renderContext === 'svg'}
|
|
@@ -46,3 +49,14 @@
|
|
|
46
49
|
<Circle cx={x} cy={y} {...$$restProps} />
|
|
47
50
|
{/if}
|
|
48
51
|
{/if}
|
|
52
|
+
|
|
53
|
+
{#if renderContext === 'canvas'}
|
|
54
|
+
{#if $$slots.default}
|
|
55
|
+
<!-- TODO: Handle Canvas translation. Conslidate with svg use case above (if `render` is not defined) -->
|
|
56
|
+
<!-- <Group {x} {y} {...$$restProps}> -->
|
|
57
|
+
<slot />
|
|
58
|
+
<!-- </Group> -->
|
|
59
|
+
{:else}
|
|
60
|
+
<Circle cx={x} cy={y} {...$$restProps} />
|
|
61
|
+
{/if}
|
|
62
|
+
{/if}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
import type { Readable } from 'svelte/store';
|
|
2
|
+
import { onDestroy } from 'svelte';
|
|
4
3
|
// @ts-expect-error
|
|
5
4
|
import { tile as d3Tile } from 'd3-tile';
|
|
6
5
|
|
|
@@ -8,6 +7,7 @@
|
|
|
8
7
|
import { geoContext } from './GeoContext.svelte';
|
|
9
8
|
import Group from './Group.svelte';
|
|
10
9
|
import TileImage from './TileImage.svelte';
|
|
10
|
+
import { getCanvasContext } from './layout/Canvas.svelte';
|
|
11
11
|
|
|
12
12
|
export let url: (x: number, y: number, z: number) => string;
|
|
13
13
|
export let zoomDelta = 0;
|
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
export let debug = false;
|
|
17
17
|
|
|
18
18
|
const { containerWidth, containerHeight, padding } = chartContext();
|
|
19
|
-
const canvas = getContext<{ ctx: Readable<CanvasRenderingContext2D> }>('canvas');
|
|
20
19
|
const geo = geoContext();
|
|
21
20
|
|
|
22
21
|
$: center = $geo([0, 0]) ?? [0, 0];
|
|
@@ -34,18 +33,34 @@
|
|
|
34
33
|
scale,
|
|
35
34
|
} = tiles);
|
|
36
35
|
|
|
37
|
-
|
|
36
|
+
const canvasContext = getCanvasContext();
|
|
37
|
+
const renderContext = canvasContext ? 'canvas' : 'svg';
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
$: if (renderContext === 'canvas' && $ctx && url) {
|
|
39
|
+
function render(ctx: CanvasRenderingContext2D) {
|
|
41
40
|
tiles.forEach(([x, y, z]: number[]) => {
|
|
42
41
|
const image = new Image();
|
|
43
42
|
image.onload = () => {
|
|
44
|
-
|
|
43
|
+
ctx.drawImage(image, (x + tx) * scale, (y + ty) * scale, scale, scale);
|
|
45
44
|
};
|
|
46
45
|
image.src = url(x, y, z);
|
|
47
46
|
});
|
|
48
47
|
}
|
|
48
|
+
|
|
49
|
+
let canvasUnregister: ReturnType<typeof canvasContext.register>;
|
|
50
|
+
$: if (renderContext === 'canvas') {
|
|
51
|
+
canvasUnregister = canvasContext.register({ name: 'GeoTile', render });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
$: if (renderContext === 'canvas') {
|
|
55
|
+
tile;
|
|
56
|
+
canvasContext.invalidate();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
onDestroy(() => {
|
|
60
|
+
if (renderContext === 'canvas') {
|
|
61
|
+
canvasUnregister();
|
|
62
|
+
}
|
|
63
|
+
});
|
|
49
64
|
</script>
|
|
50
65
|
|
|
51
66
|
{#if renderContext === 'svg' && url}
|