layerchart 0.81.3 → 0.91.0

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.
Files changed (64) hide show
  1. package/dist/components/Arc.svelte +49 -14
  2. package/dist/components/Area.svelte +35 -11
  3. package/dist/components/Bar.svelte +15 -10
  4. package/dist/components/Bars.svelte +3 -3
  5. package/dist/components/Brush.svelte +38 -33
  6. package/dist/components/Calendar.svelte +2 -2
  7. package/dist/components/Chart.svelte +17 -4
  8. package/dist/components/Chart.svelte.d.ts +21 -10
  9. package/dist/components/ChartClipPath.svelte +0 -1
  10. package/dist/components/ChartClipPath.svelte.d.ts +0 -2
  11. package/dist/components/ChartContext.svelte +4 -11
  12. package/dist/components/ChartContext.svelte.d.ts +1 -1
  13. package/dist/components/Circle.svelte +35 -11
  14. package/dist/components/ClipPath.svelte +1 -1
  15. package/dist/components/ClipPath.svelte.d.ts +0 -5
  16. package/dist/components/ForceSimulation.svelte +8 -12
  17. package/dist/components/ForceSimulation.svelte.d.ts +6 -8
  18. package/dist/components/Frame.svelte +4 -7
  19. package/dist/components/Frame.svelte.d.ts +1 -5
  20. package/dist/components/GeoCircle.svelte +7 -1
  21. package/dist/components/GeoCircle.svelte.d.ts +6 -7
  22. package/dist/components/GeoPath.svelte +68 -39
  23. package/dist/components/GeoPath.svelte.d.ts +6 -12
  24. package/dist/components/GeoPoint.svelte +3 -24
  25. package/dist/components/GeoPoint.svelte.d.ts +4 -5
  26. package/dist/components/Graticule.svelte.d.ts +12 -6
  27. package/dist/components/Group.svelte +26 -6
  28. package/dist/components/Highlight.svelte +22 -12
  29. package/dist/components/Hull.svelte +20 -15
  30. package/dist/components/Hull.svelte.d.ts +7 -9
  31. package/dist/components/Legend.svelte +7 -7
  32. package/dist/components/Legend.svelte.d.ts +3 -3
  33. package/dist/components/Line.svelte +35 -10
  34. package/dist/components/Link.svelte +16 -9
  35. package/dist/components/Points.svelte +24 -57
  36. package/dist/components/Points.svelte.d.ts +0 -1
  37. package/dist/components/Rect.svelte +46 -15
  38. package/dist/components/Sankey.svelte +3 -4
  39. package/dist/components/Sankey.svelte.d.ts +1 -2
  40. package/dist/components/Spline.svelte +47 -11
  41. package/dist/components/Text.svelte +12 -6
  42. package/dist/components/TransformContext.svelte +8 -10
  43. package/dist/components/TransformContext.svelte.d.ts +9 -9
  44. package/dist/components/Voronoi.svelte +62 -40
  45. package/dist/components/Voronoi.svelte.d.ts +14 -13
  46. package/dist/components/charts/AreaChart.svelte +25 -17
  47. package/dist/components/charts/BarChart.svelte +24 -11
  48. package/dist/components/charts/LineChart.svelte +25 -17
  49. package/dist/components/charts/PieChart.svelte +22 -15
  50. package/dist/components/charts/PieChart.svelte.d.ts +29 -7
  51. package/dist/components/charts/ScatterChart.svelte +15 -9
  52. package/dist/components/index.d.ts +0 -1
  53. package/dist/components/index.js +0 -1
  54. package/dist/components/layout/Canvas.svelte +148 -7
  55. package/dist/components/layout/Canvas.svelte.d.ts +15 -2
  56. package/dist/components/tooltip/TooltipContext.svelte +25 -20
  57. package/dist/components/tooltip/TooltipContext.svelte.d.ts +6 -2
  58. package/dist/utils/canvas.js +39 -33
  59. package/dist/utils/color.d.ts +15 -0
  60. package/dist/utils/color.js +15 -0
  61. package/package.json +1 -1
  62. package/dist/components/Brush.svelte.d.ts +0 -65
  63. package/dist/components/HitCanvas.svelte +0 -118
  64. package/dist/components/HitCanvas.svelte.d.ts +0 -32
@@ -4,12 +4,23 @@
4
4
 
5
5
  export const tooltipContextKey = Symbol();
6
6
 
7
+ type TooltipMode =
8
+ | 'bisect-x'
9
+ | 'bisect-y'
10
+ | 'band'
11
+ | 'bisect-band'
12
+ | 'bounds'
13
+ | 'voronoi'
14
+ | 'quadtree'
15
+ | 'manual';
16
+
7
17
  export type TooltipContextValue = {
8
18
  x: number;
9
19
  y: number;
10
20
  data: any;
11
21
  show(e: PointerEvent, tooltipData?: any): void;
12
22
  hide(e?: PointerEvent): void;
23
+ mode: TooltipMode;
13
24
  };
14
25
 
15
26
  export type TooltipContext = Readable<TooltipContextValue>;
@@ -20,6 +31,7 @@
20
31
  data: null as any,
21
32
  show: () => {},
22
33
  hide: () => {},
34
+ mode: 'manual',
23
35
  });
24
36
  export function tooltipContext() {
25
37
  return getContext<TooltipContext>(tooltipContextKey) ?? defaultContext;
@@ -80,15 +92,7 @@
80
92
  /**
81
93
  * @type {'bisect-x' | 'bisect-y' | 'band' | 'bisect-band' | 'bounds' | 'voronoi' | 'quadtree' | 'manual'}
82
94
  */
83
- export let mode:
84
- | 'bisect-x'
85
- | 'bisect-y'
86
- | 'band'
87
- | 'bisect-band'
88
- | 'bounds'
89
- | 'voronoi'
90
- | 'quadtree'
91
- | 'manual' = 'manual';
95
+ export let mode: TooltipMode = 'manual';
92
96
  /**
93
97
  * @type {'closest' | 'left' | 'right'}
94
98
  */
@@ -107,7 +111,7 @@
107
111
  /** Enable debug view (show hit targets, etc) */
108
112
  export let debug = false;
109
113
 
110
- export let onClick: ({ data }: { data: any }) => any = () => {};
114
+ export let onclick: ({ data }: { data: any }) => any = () => {};
111
115
 
112
116
  /** Exposed to allow binding in Chart */
113
117
  export let tooltip = writable({
@@ -116,6 +120,7 @@
116
120
  data: null as any,
117
121
  show: showTooltip,
118
122
  hide: hideTooltip,
123
+ mode,
119
124
  });
120
125
  setTooltipContext(tooltip);
121
126
 
@@ -401,7 +406,7 @@
401
406
  on:pointerleave={triggerPointerEvents ? hideTooltip : undefined}
402
407
  on:click={(e) => {
403
408
  if (triggerPointerEvents) {
404
- onClick({ data: $tooltip?.data });
409
+ onclick({ data: $tooltip?.data });
405
410
  }
406
411
  }}
407
412
  >
@@ -418,22 +423,22 @@
418
423
  {#if mode === 'voronoi'}
419
424
  <Svg>
420
425
  <Voronoi
421
- on:pointerenter={(e) => {
422
- showTooltip(e.detail.event, e.detail.data);
426
+ onpointerenter={(e, { data }) => {
427
+ showTooltip(e, data);
423
428
  }}
424
- on:pointermove={(e) => {
425
- showTooltip(e.detail.event, e.detail.data);
429
+ onpointermove={(e, { data }) => {
430
+ showTooltip(e, data);
426
431
  }}
427
- on:pointerleave={hideTooltip}
428
- on:pointerdown={(e) => {
432
+ onpointerleave={hideTooltip}
433
+ onpointerdown={(e) => {
429
434
  // @ts-expect-error
430
435
  if (e.target?.hasPointerCapture(e.pointerId)) {
431
436
  // @ts-expect-error
432
437
  e.target.releasePointerCapture(e.pointerId);
433
438
  }
434
439
  }}
435
- on:click={(e) => {
436
- onClick({ data: e.detail.data });
440
+ onclick={(e, { data }) => {
441
+ onclick({ data });
437
442
  }}
438
443
  classes={{ path: cls(debug && 'fill-danger/10 stroke-danger') }}
439
444
  />
@@ -459,7 +464,7 @@
459
464
  }
460
465
  }}
461
466
  on:click={(e) => {
462
- onClick({ data: rect.data });
467
+ onclick({ data: rect.data });
463
468
  }}
464
469
  />
465
470
  {/each}
@@ -1,12 +1,14 @@
1
1
  import { SvelteComponentTyped } from "svelte";
2
2
  import type { Readable } from 'svelte/store';
3
3
  export declare const tooltipContextKey: unique symbol;
4
+ type TooltipMode = 'bisect-x' | 'bisect-y' | 'band' | 'bisect-band' | 'bounds' | 'voronoi' | 'quadtree' | 'manual';
4
5
  export type TooltipContextValue = {
5
6
  x: number;
6
7
  y: number;
7
8
  data: any;
8
9
  show(e: PointerEvent, tooltipData?: any): void;
9
10
  hide(e?: PointerEvent): void;
11
+ mode: TooltipMode;
10
12
  };
11
13
  export type TooltipContext = Readable<TooltipContextValue>;
12
14
  export declare function tooltipContext(): TooltipContext;
@@ -14,7 +16,7 @@ declare const __propDef: {
14
16
  props: {
15
17
  /**
16
18
  * @type {'bisect-x' | 'bisect-y' | 'band' | 'bisect-band' | 'bounds' | 'voronoi' | 'quadtree' | 'manual'}
17
- */ mode?: "bisect-x" | "bisect-y" | "band" | "bisect-band" | "bounds" | "voronoi" | "quadtree" | "manual";
19
+ */ mode?: TooltipMode;
18
20
  /**
19
21
  * @type {'closest' | 'left' | 'right'}
20
22
  */ findTooltipData?: "closest" | "left" | "right";
@@ -24,7 +26,7 @@ declare const __propDef: {
24
26
  * @type {number}
25
27
  */ radius?: number;
26
28
  /** Enable debug view (show hit targets, etc) */ debug?: boolean;
27
- onClick?: ({ data }: {
29
+ onclick?: ({ data }: {
28
30
  data: any;
29
31
  }) => any;
30
32
  /** Exposed to allow binding in Chart */ tooltip?: import("svelte/store").Writable<{
@@ -33,6 +35,7 @@ declare const __propDef: {
33
35
  data: any;
34
36
  show: (e: PointerEvent, tooltipData?: any) => void;
35
37
  hide: () => void;
38
+ mode: TooltipMode;
36
39
  }>;
37
40
  };
38
41
  events: {
@@ -46,6 +49,7 @@ declare const __propDef: {
46
49
  data: any;
47
50
  show: (e: PointerEvent, tooltipData?: any) => void;
48
51
  hide: () => void;
52
+ mode: TooltipMode;
49
53
  };
50
54
  };
51
55
  };
@@ -39,40 +39,40 @@ export function getComputedStyles(canvas, { styles, classes } = {}) {
39
39
  }
40
40
  }
41
41
  /** Render onto canvas context. Supports CSS variables and classes by tranferring to hidden `<svg>` element before retrieval) */
42
- function render(canvasCtx, render, styleOptions = {}) {
42
+ function render(ctx, render, styleOptions = {}) {
43
43
  // console.count('render');
44
44
  // TODO: Consider memoizing? How about reactiving to CSS variable changes (light/dark mode toggle)
45
- const computedStyles = getComputedStyles(canvasCtx.canvas, styleOptions);
45
+ const computedStyles = getComputedStyles(ctx.canvas, styleOptions);
46
46
  // Adhere to CSS paint order: https://developer.mozilla.org/en-US/docs/Web/CSS/paint-order
47
47
  const paintOrder = computedStyles?.paintOrder === 'stroke' ? ['stroke', 'fill'] : ['fill', 'stroke'];
48
48
  if (computedStyles?.opacity) {
49
- canvasCtx.globalAlpha = Number(computedStyles?.opacity);
49
+ ctx.globalAlpha = Number(computedStyles?.opacity);
50
50
  }
51
51
  // Text properties
52
- canvasCtx.font = `${computedStyles.fontSize} ${computedStyles.fontFamily}`; // build string instead of using `computedStyles.font` to fix/workaround `tabular-nums` returning `null`
52
+ ctx.font = `${computedStyles.fontSize} ${computedStyles.fontFamily}`; // build string instead of using `computedStyles.font` to fix/workaround `tabular-nums` returning `null`
53
53
  // TODO: Hack to handle `textAnchor` with canvas. Try to find a better approach
54
54
  if (computedStyles.textAnchor === 'middle') {
55
- canvasCtx.textAlign = 'center';
55
+ ctx.textAlign = 'center';
56
56
  }
57
57
  else if (computedStyles.textAnchor === 'end') {
58
- canvasCtx.textAlign = 'right';
58
+ ctx.textAlign = 'right';
59
59
  }
60
60
  else {
61
- canvasCtx.textAlign = computedStyles.textAlign; // TODO: Handle/map `justify` and `match-parent`?
61
+ ctx.textAlign = computedStyles.textAlign; // TODO: Handle/map `justify` and `match-parent`?
62
62
  }
63
63
  // TODO: Handle `textBaseline` / `verticalAnchor` (Text)
64
- // canvasCtx.textBaseline = 'top';
65
- // canvasCtx.textBaseline = 'middle';
66
- // canvasCtx.textBaseline = 'bottom';
67
- // canvasCtx.textBaseline = 'alphabetic';
68
- // canvasCtx.textBaseline = 'hanging';
69
- // canvasCtx.textBaseline = 'ideographic';
64
+ // ctx.textBaseline = 'top';
65
+ // ctx.textBaseline = 'middle';
66
+ // ctx.textBaseline = 'bottom';
67
+ // ctx.textBaseline = 'alphabetic';
68
+ // ctx.textBaseline = 'hanging';
69
+ // ctx.textBaseline = 'ideographic';
70
70
  // Dashed lines
71
71
  if (computedStyles.strokeDasharray.includes(',')) {
72
72
  const dashArray = computedStyles.strokeDasharray
73
73
  .split(',')
74
74
  .map((s) => Number(s.replace('px', '')));
75
- canvasCtx.setLineDash(dashArray);
75
+ ctx.setLineDash(dashArray);
76
76
  }
77
77
  paintOrder.forEach((attr) => {
78
78
  if (attr === 'fill') {
@@ -82,14 +82,14 @@ function render(canvasCtx, render, styleOptions = {}) {
82
82
  ? null
83
83
  : computedStyles?.fill;
84
84
  if (fill) {
85
- const currentGlobalAlpha = canvasCtx.globalAlpha;
85
+ const currentGlobalAlpha = ctx.globalAlpha;
86
86
  const fillOpacity = Number(computedStyles?.fillOpacity);
87
87
  const opacity = Number(computedStyles?.opacity);
88
- canvasCtx.globalAlpha = fillOpacity * opacity;
89
- canvasCtx.fillStyle = fill;
90
- render.fill(canvasCtx);
88
+ ctx.globalAlpha = fillOpacity * opacity;
89
+ ctx.fillStyle = fill;
90
+ render.fill(ctx);
91
91
  // Restore in case it was modified by `fillOpacity`
92
- canvasCtx.globalAlpha = currentGlobalAlpha;
92
+ ctx.globalAlpha = currentGlobalAlpha;
93
93
  }
94
94
  }
95
95
  else if (attr === 'stroke') {
@@ -99,42 +99,42 @@ function render(canvasCtx, render, styleOptions = {}) {
99
99
  ? null
100
100
  : computedStyles?.stroke;
101
101
  if (stroke) {
102
- canvasCtx.lineWidth =
102
+ ctx.lineWidth =
103
103
  typeof computedStyles?.strokeWidth === 'string'
104
104
  ? Number(computedStyles?.strokeWidth?.replace('px', ''))
105
105
  : (computedStyles?.strokeWidth ?? 1);
106
- canvasCtx.strokeStyle = stroke;
107
- render.stroke(canvasCtx);
106
+ ctx.strokeStyle = stroke;
107
+ render.stroke(ctx);
108
108
  }
109
109
  }
110
110
  });
111
111
  }
112
112
  /** Render SVG path data onto canvas context. Supports CSS variables and classes by tranferring to hidden `<svg>` element before retrieval) */
113
- export function renderPathData(canvasCtx, pathData, styleOptions = {}) {
113
+ export function renderPathData(ctx, pathData, styleOptions = {}) {
114
114
  const path = new Path2D(pathData ?? '');
115
- render(canvasCtx, {
115
+ render(ctx, {
116
116
  fill: (ctx) => ctx.fill(path),
117
117
  stroke: (ctx) => ctx.stroke(path),
118
118
  }, styleOptions);
119
119
  }
120
- export function renderText(canvasCtx, text, coords, styleOptions = {}) {
120
+ export function renderText(ctx, text, coords, styleOptions = {}) {
121
121
  if (text) {
122
- render(canvasCtx, {
122
+ render(ctx, {
123
123
  fill: (ctx) => ctx.fillText(text.toString(), coords.x, coords.y),
124
124
  stroke: (ctx) => ctx.strokeText(text.toString(), coords.x, coords.y),
125
125
  }, styleOptions);
126
126
  }
127
127
  }
128
- export function renderRect(canvasCtx, coords, styleOptions = {}) {
129
- render(canvasCtx, {
128
+ export function renderRect(ctx, coords, styleOptions = {}) {
129
+ render(ctx, {
130
130
  fill: (ctx) => ctx.fillRect(coords.x, coords.y, coords.width, coords.height),
131
131
  stroke: (ctx) => ctx.strokeRect(coords.x, coords.y, coords.width, coords.height),
132
132
  }, styleOptions);
133
133
  }
134
134
  /** Clear canvas accounting for Canvas `context.translate(...)` */
135
- export function clearCanvasContext(canvasCtx, options) {
135
+ export function clearCanvasContext(ctx, options) {
136
136
  // Clear with negative offset due to Canvas `context.translate(...)`
137
- canvasCtx.clearRect(-options.padding.left, -options.padding.top, options.containerWidth, options.containerHeight);
137
+ ctx.clearRect(-options.padding.left, -options.padding.top, options.containerWidth, options.containerHeight);
138
138
  }
139
139
  /**
140
140
  Scales a canvas for high DPI / retina displays.
@@ -150,15 +150,21 @@ export function scaleCanvas(ctx, width, height) {
150
150
  ctx.scale(devicePixelRatio, devicePixelRatio);
151
151
  return { width: ctx.canvas.width, height: ctx.canvas.height };
152
152
  }
153
- export function _createLinearGradient(canvasCtx, x0, y0, x1, y1, stops) {
154
- const gradient = canvasCtx.createLinearGradient(x0, y0, x1, y1);
153
+ export function _createLinearGradient(ctx, x0, y0, x1, y1, stops) {
154
+ const gradient = ctx.createLinearGradient(x0, y0, x1, y1);
155
155
  stops.forEach(({ offset, color }) => {
156
156
  gradient.addColorStop(offset, color);
157
157
  });
158
158
  return gradient;
159
159
  }
160
160
  /** Create linear gradient and memoize result to fix reactivity */
161
- export const createLinearGradient = memoize(_createLinearGradient, (canvasCtx, x0, y0, x1, y1, stops) => {
161
+ export const createLinearGradient = memoize(_createLinearGradient, (ctx, x0, y0, x1, y1, stops) => {
162
162
  const key = JSON.stringify({ x0, y0, x1, y1, stops });
163
163
  return key;
164
164
  });
165
+ export function getPixelColor(ctx, x, y) {
166
+ const dpr = window.devicePixelRatio ?? 1;
167
+ const imageData = ctx.getImageData(x * dpr, y * dpr, 1, 1);
168
+ const [r, g, b, a] = imageData.data;
169
+ return { r, g, b, a };
170
+ }
@@ -0,0 +1,15 @@
1
+ /** Generator to create a new color on each call */
2
+ export declare function rgbColorGenerator(step?: number): Generator<{
3
+ r: number;
4
+ g: number;
5
+ b: number;
6
+ }, {
7
+ r: number;
8
+ g: number;
9
+ b: number;
10
+ }, unknown>;
11
+ export declare function getColorStr(color: {
12
+ r: number;
13
+ g: number;
14
+ b: number;
15
+ }): string;
@@ -0,0 +1,15 @@
1
+ /** Generator to create a new color on each call */
2
+ export function* rgbColorGenerator(step = 500) {
3
+ let nextColor = 1;
4
+ while (nextColor < 16777216) {
5
+ const r = nextColor & 0xff;
6
+ const g = (nextColor & 0xff00) >> 8;
7
+ const b = (nextColor & 0xff0000) >> 16;
8
+ nextColor += step;
9
+ yield { r, g, b };
10
+ }
11
+ return { r: 0, g: 0, b: 0 };
12
+ }
13
+ export function getColorStr(color) {
14
+ return `rgb(${color.r},${color.g},${color.b})`;
15
+ }
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "author": "Sean Lynch <techniq35@gmail.com>",
5
5
  "license": "MIT",
6
6
  "repository": "techniq/layerchart",
7
- "version": "0.81.3",
7
+ "version": "0.91.0",
8
8
  "devDependencies": {
9
9
  "@changesets/cli": "^2.27.12",
10
10
  "@mdi/js": "^7.4.47",
@@ -1,65 +0,0 @@
1
- import { SvelteComponentTyped } from "svelte";
2
- import { type ComponentProps } from 'svelte';
3
- import type { SVGAttributes } from 'svelte/elements';
4
- import { type FormatType } from '@layerstack/utils';
5
- import Text from './Text.svelte';
6
- import type { DomainType } from '../utils/scales.js';
7
- declare const __propDef: {
8
- props: {
9
- [x: string]: any;
10
- axis?: "x" | "y" | "both" | undefined;
11
- handleSize?: number | undefined;
12
- resetOnEnd?: boolean | undefined;
13
- xDomain?: DomainType | undefined;
14
- yDomain?: DomainType | undefined;
15
- labels?: (ComponentProps<Text> | boolean) | undefined;
16
- mode?: "integrated" | "separated" | undefined;
17
- range?: SVGAttributes<SVGRectElement> | undefined;
18
- handle?: SVGAttributes<SVGRectElement> | undefined;
19
- format?: FormatType | undefined;
20
- classes?: {
21
- root?: string;
22
- frame?: string;
23
- range?: string;
24
- handle?: string;
25
- labels?: string;
26
- } | undefined;
27
- onChange?: ((e: {
28
- xDomain?: DomainType;
29
- yDomain?: DomainType;
30
- }) => void) | undefined;
31
- onBrushStart?: ((e: {
32
- xDomain?: DomainType;
33
- yDomain?: DomainType;
34
- }) => void) | undefined;
35
- onBrushEnd?: ((e: {
36
- xDomain?: DomainType;
37
- yDomain?: DomainType;
38
- }) => void) | undefined;
39
- onReset?: ((e: {
40
- xDomain?: DomainType;
41
- yDomain?: DomainType;
42
- }) => void) | undefined;
43
- };
44
- events: {
45
- [evt: string]: CustomEvent<any>;
46
- };
47
- slots: {
48
- range: {
49
- rangeWidth: any;
50
- rangeHeight: any;
51
- };
52
- handle: {
53
- edge: string;
54
- rangeWidth: any;
55
- rangeHeight: any;
56
- };
57
- labels: {};
58
- };
59
- };
60
- export type BrushProps = typeof __propDef.props;
61
- export type BrushEvents = typeof __propDef.events;
62
- export type BrushSlots = typeof __propDef.slots;
63
- export default class Brush extends SvelteComponentTyped<BrushProps, BrushEvents, BrushSlots> {
64
- }
65
- export {};
@@ -1,118 +0,0 @@
1
- <script lang="ts">
2
- import { createEventDispatcher } from 'svelte';
3
- import { cls } from '@layerstack/tailwind';
4
-
5
- import { chartContext } from './ChartContext.svelte';
6
- import Canvas from './layout/Canvas.svelte';
7
- import { transformContext } from './TransformContext.svelte';
8
-
9
- const { width, height } = chartContext();
10
-
11
- // @ts-expect-error: this will immediately be defined on mount via `bind:context`
12
- export let context: CanvasRenderingContext2D = undefined;
13
-
14
- /** Show canvas for debugging */
15
- export let debug = false;
16
-
17
- const dispatch = createEventDispatcher<{
18
- pointermove: {
19
- event: PointerEvent;
20
- data: any;
21
- };
22
- click: {
23
- event: MouseEvent;
24
- data: any;
25
- };
26
- }>();
27
-
28
- function* rgbColorGenerator(step = 500) {
29
- let nextColor = 1;
30
-
31
- while (nextColor < 16777216) {
32
- const rgb = [
33
- nextColor & 0xff, // red
34
- (nextColor & 0xff00) >> 8, // green
35
- (nextColor & 0xff0000) >> 16, // blue
36
- ];
37
-
38
- nextColor += step;
39
- yield `rgb(${rgb.join(',')})`;
40
- }
41
-
42
- return 'rgb(0,0,0)';
43
- }
44
-
45
- $: colorGenerator = rgbColorGenerator();
46
-
47
- // Reset color generator whenever updated (width/height) so always reusing same colors (and not exhausting)
48
- const { translate, scale, dragging } = transformContext();
49
- $: {
50
- $width;
51
- $height;
52
- $translate;
53
- $scale;
54
-
55
- colorGenerator = rgbColorGenerator();
56
- }
57
-
58
- const dataByColor = new Map<string, any>();
59
- function setColorData(color: string, data: any) {
60
- dataByColor.set(color, data);
61
- }
62
-
63
- let activePointer = false;
64
-
65
- function getPointerData(e: PointerEvent | MouseEvent) {
66
- const { offsetX, offsetY } = e;
67
-
68
- const dpr = window.devicePixelRatio ?? 1;
69
- const imageData = context.getImageData(offsetX * dpr, offsetY * dpr, 1, 1);
70
- const [r, g, b, a] = imageData.data;
71
- const colorKey = `rgb(${r},${g},${b})`;
72
- const data = dataByColor.get(colorKey);
73
-
74
- return data;
75
- }
76
-
77
- function dispatchPointerMove(e: PointerEvent) {
78
- const data = getPointerData(e);
79
-
80
- if (data) {
81
- activePointer = true;
82
- }
83
-
84
- // Still dispatch with `undefined data` to hide tooltip, etc
85
- dispatch('pointermove', { event: e, data });
86
- }
87
- </script>
88
-
89
- <Canvas
90
- bind:context
91
- willReadFrequently
92
- class={cls(
93
- 'HitCanvas absolute w-full h-full border border-danger',
94
- // '[image-rendering:pixelated]', // https://developer.mozilla.org/en-US/docs/Web/CSS/image-rendering
95
- !debug && 'opacity-0'
96
- )}
97
- on:pointerenter={dispatchPointerMove}
98
- on:pointermove={dispatchPointerMove}
99
- on:pointerleave={() => (activePointer = false)}
100
- on:pointerleave
101
- on:touchmove={(e) => {
102
- // Prevent touch to not interfer with pointer if over data
103
- if (activePointer) {
104
- e.preventDefault();
105
- }
106
- }}
107
- on:click={(e) => {
108
- const data = getPointerData(e);
109
- if (data) {
110
- dispatch('click', { event: e, data });
111
- }
112
- }}
113
- >
114
- <!-- Do not render while dragging to improve interaction performance -->
115
- {#if !$dragging}
116
- <slot nextColor={() => colorGenerator.next().value} {setColorData}></slot>
117
- {/if}
118
- </Canvas>
@@ -1,32 +0,0 @@
1
- import { SvelteComponentTyped } from "svelte";
2
- declare const __propDef: {
3
- props: {
4
- context?: CanvasRenderingContext2D;
5
- /** Show canvas for debugging */ debug?: boolean;
6
- };
7
- events: {
8
- pointerleave: PointerEvent;
9
- pointermove: CustomEvent<{
10
- event: PointerEvent;
11
- data: any;
12
- }>;
13
- click: CustomEvent<{
14
- event: MouseEvent;
15
- data: any;
16
- }>;
17
- } & {
18
- [evt: string]: CustomEvent<any>;
19
- };
20
- slots: {
21
- default: {
22
- nextColor: () => string;
23
- setColorData: (color: string, data: any) => void;
24
- };
25
- };
26
- };
27
- export type HitCanvasProps = typeof __propDef.props;
28
- export type HitCanvasEvents = typeof __propDef.events;
29
- export type HitCanvasSlots = typeof __propDef.slots;
30
- export default class HitCanvas extends SvelteComponentTyped<HitCanvasProps, HitCanvasEvents, HitCanvasSlots> {
31
- }
32
- export {};