@luma.gl/core 9.3.3 → 9.3.5
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/adapter/canvas-observer.d.ts +25 -0
- package/dist/adapter/canvas-observer.d.ts.map +1 -1
- package/dist/adapter/canvas-observer.js +18 -1
- package/dist/adapter/canvas-observer.js.map +1 -1
- package/dist/adapter/canvas-surface.d.ts +17 -0
- package/dist/adapter/canvas-surface.d.ts.map +1 -1
- package/dist/adapter/canvas-surface.js +35 -7
- package/dist/adapter/canvas-surface.js.map +1 -1
- package/dist/adapter/luma.js +1 -1
- package/dist/dist.dev.js +51 -6
- package/dist/dist.min.js +3 -3
- package/dist/index.cjs +51 -8
- package/dist/index.cjs.map +2 -2
- package/package.json +2 -2
- package/src/adapter/canvas-observer.ts +27 -1
- package/src/adapter/canvas-surface.ts +61 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@luma.gl/core",
|
|
3
|
-
"version": "9.3.
|
|
3
|
+
"version": "9.3.5",
|
|
4
4
|
"description": "The luma.gl core Device API",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -46,5 +46,5 @@
|
|
|
46
46
|
"@probe.gl/stats": "^4.1.1",
|
|
47
47
|
"@types/offscreencanvas": "^2019.7.3"
|
|
48
48
|
},
|
|
49
|
-
"gitHead": "
|
|
49
|
+
"gitHead": "7c66d63f213836a3fc83f2ad0103d1d3dadf7633"
|
|
50
50
|
}
|
|
@@ -2,22 +2,33 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
+
/** Options and callbacks used by {@link CanvasObserver}. */
|
|
5
6
|
type CanvasObserverProps = {
|
|
7
|
+
/** HTML canvas element whose DOM lifecycle should be observed. */
|
|
6
8
|
canvas?: HTMLCanvasElement;
|
|
9
|
+
/** Whether to poll for canvas position changes. */
|
|
7
10
|
trackPosition: boolean;
|
|
11
|
+
/** ResizeObserver box type passed to `observe()`. */
|
|
12
|
+
resizeObserverBox: ResizeObserverBoxOptions;
|
|
13
|
+
/** Called with ResizeObserver entries for the observed canvas. */
|
|
8
14
|
onResize: (entries: ResizeObserverEntry[]) => void;
|
|
15
|
+
/** Called with IntersectionObserver entries for the observed canvas. */
|
|
9
16
|
onIntersection: (entries: IntersectionObserverEntry[]) => void;
|
|
17
|
+
/** Called when the window device pixel ratio may have changed. */
|
|
10
18
|
onDevicePixelRatioChange: () => void;
|
|
19
|
+
/** Called while canvas position tracking is enabled. */
|
|
11
20
|
onPositionChange: () => void;
|
|
12
21
|
};
|
|
13
22
|
|
|
14
23
|
/**
|
|
15
24
|
* Internal DOM observer orchestration for HTML canvas surfaces.
|
|
16
25
|
*
|
|
26
|
+
* @remarks
|
|
17
27
|
* CanvasSurface owns the tracked state and device callback dispatch. This helper only manages
|
|
18
28
|
* browser observers, timers, and polling loops, then reports events through callbacks.
|
|
19
29
|
*/
|
|
20
30
|
export class CanvasObserver {
|
|
31
|
+
/** Observer options and event callbacks. */
|
|
21
32
|
readonly props: CanvasObserverProps;
|
|
22
33
|
|
|
23
34
|
private _resizeObserver: ResizeObserver | undefined;
|
|
@@ -28,14 +39,21 @@ export class CanvasObserver {
|
|
|
28
39
|
private _trackPositionInterval: ReturnType<typeof setInterval> | null = null;
|
|
29
40
|
private _started = false;
|
|
30
41
|
|
|
42
|
+
/** Whether the DOM observers and polling loops have been started. */
|
|
31
43
|
get started(): boolean {
|
|
32
44
|
return this._started;
|
|
33
45
|
}
|
|
34
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Creates an observer coordinator for one HTML canvas.
|
|
49
|
+
*
|
|
50
|
+
* @param props - Observer options and event callbacks.
|
|
51
|
+
*/
|
|
35
52
|
constructor(props: CanvasObserverProps) {
|
|
36
53
|
this.props = props;
|
|
37
54
|
}
|
|
38
55
|
|
|
56
|
+
/** Starts DOM observation and optional position polling. */
|
|
39
57
|
start(): void {
|
|
40
58
|
if (this._started || !this.props.canvas) {
|
|
41
59
|
return;
|
|
@@ -48,8 +66,9 @@ export class CanvasObserver {
|
|
|
48
66
|
this._resizeObserver ||= new ResizeObserver(entries => this.props.onResize(entries));
|
|
49
67
|
|
|
50
68
|
this._intersectionObserver.observe(this.props.canvas);
|
|
69
|
+
const box = this.props.resizeObserverBox;
|
|
51
70
|
try {
|
|
52
|
-
this._resizeObserver.observe(this.props.canvas, {box
|
|
71
|
+
this._resizeObserver.observe(this.props.canvas, {box});
|
|
53
72
|
} catch {
|
|
54
73
|
this._resizeObserver.observe(this.props.canvas, {box: 'content-box'});
|
|
55
74
|
}
|
|
@@ -61,6 +80,7 @@ export class CanvasObserver {
|
|
|
61
80
|
}
|
|
62
81
|
}
|
|
63
82
|
|
|
83
|
+
/** Stops DOM observation, media-query listeners, and position polling. */
|
|
64
84
|
stop(): void {
|
|
65
85
|
if (!this._started) {
|
|
66
86
|
return;
|
|
@@ -90,6 +110,7 @@ export class CanvasObserver {
|
|
|
90
110
|
this._intersectionObserver?.disconnect();
|
|
91
111
|
}
|
|
92
112
|
|
|
113
|
+
/** Reports the current device pixel ratio and arms the media query for its next change. */
|
|
93
114
|
private _refreshDevicePixelRatio(): void {
|
|
94
115
|
if (!this._started) {
|
|
95
116
|
return;
|
|
@@ -111,6 +132,11 @@ export class CanvasObserver {
|
|
|
111
132
|
);
|
|
112
133
|
}
|
|
113
134
|
|
|
135
|
+
/**
|
|
136
|
+
* Starts periodic position callbacks while the observer remains active.
|
|
137
|
+
*
|
|
138
|
+
* @param intervalMs - Poll interval in milliseconds.
|
|
139
|
+
*/
|
|
114
140
|
private _trackPosition(intervalMs: number = 100): void {
|
|
115
141
|
if (this._trackPositionInterval) {
|
|
116
142
|
return;
|
|
@@ -29,6 +29,15 @@ export type CanvasContextProps = {
|
|
|
29
29
|
visible?: boolean;
|
|
30
30
|
/** Whether to size the drawing buffer to the pixel size during auto resize. If a number is provided it is used as a static pixel ratio */
|
|
31
31
|
useDevicePixels?: boolean | number;
|
|
32
|
+
/**
|
|
33
|
+
* How to derive the tracked device pixel size for HTML canvases when auto-resizing.
|
|
34
|
+
*
|
|
35
|
+
* - `'exact'` uses `ResizeObserver.devicePixelContentBoxSize` when available to match the
|
|
36
|
+
* browser's exact physical pixel coverage.
|
|
37
|
+
* - `'css-dpr'` uses `Math.floor(cssSize * devicePixelRatio)` to match overlays and external
|
|
38
|
+
* canvases that size their drawing buffer via implicit truncation (e.g. `canvas.width = css * dpr`).
|
|
39
|
+
*/
|
|
40
|
+
pixelSizeSource?: 'exact' | 'css-dpr';
|
|
32
41
|
/** Whether to track window resizes. */
|
|
33
42
|
autoResize?: boolean;
|
|
34
43
|
/** @see https://developer.mozilla.org/en-US/docs/Web/API/GPUCanvasContext/configure#alphamode */
|
|
@@ -44,6 +53,11 @@ export type MutableCanvasContextProps = {
|
|
|
44
53
|
useDevicePixels?: boolean | number;
|
|
45
54
|
};
|
|
46
55
|
|
|
56
|
+
type DevicePixelSize = {
|
|
57
|
+
devicePixelWidth: number;
|
|
58
|
+
devicePixelHeight: number;
|
|
59
|
+
};
|
|
60
|
+
|
|
47
61
|
/**
|
|
48
62
|
* Shared tracked-canvas lifecycle used by both renderable and presentation contexts.
|
|
49
63
|
* - Creates a new canvas or looks up a canvas from the DOM
|
|
@@ -66,6 +80,7 @@ export abstract class CanvasSurface {
|
|
|
66
80
|
width: 800,
|
|
67
81
|
height: 600,
|
|
68
82
|
useDevicePixels: true,
|
|
83
|
+
pixelSizeSource: 'exact',
|
|
69
84
|
autoResize: true,
|
|
70
85
|
container: null,
|
|
71
86
|
visible: true,
|
|
@@ -166,6 +181,8 @@ export abstract class CanvasSurface {
|
|
|
166
181
|
this._canvasObserver = new CanvasObserver({
|
|
167
182
|
canvas: this.htmlCanvas,
|
|
168
183
|
trackPosition: this.props.trackPosition,
|
|
184
|
+
resizeObserverBox:
|
|
185
|
+
this.props.pixelSizeSource === 'css-dpr' ? 'content-box' : 'device-pixel-content-box',
|
|
169
186
|
onResize: entries => this._handleResize(entries),
|
|
170
187
|
onIntersection: entries => this._handleIntersection(entries),
|
|
171
188
|
onDevicePixelRatioChange: () => this._observeDevicePixelRatio(),
|
|
@@ -347,17 +364,7 @@ export abstract class CanvasSurface {
|
|
|
347
364
|
|
|
348
365
|
const oldPixelSize = this.getDevicePixelSize();
|
|
349
366
|
|
|
350
|
-
|
|
351
|
-
entry.devicePixelContentBoxSize?.[0]?.inlineSize ||
|
|
352
|
-
contentBoxSize.inlineSize * devicePixelRatio;
|
|
353
|
-
|
|
354
|
-
const devicePixelHeight =
|
|
355
|
-
entry.devicePixelContentBoxSize?.[0]?.blockSize ||
|
|
356
|
-
contentBoxSize.blockSize * devicePixelRatio;
|
|
357
|
-
|
|
358
|
-
const [maxDevicePixelWidth, maxDevicePixelHeight] = this.getMaxDrawingBufferSize();
|
|
359
|
-
this.devicePixelWidth = Math.max(1, Math.min(devicePixelWidth, maxDevicePixelWidth));
|
|
360
|
-
this.devicePixelHeight = Math.max(1, Math.min(devicePixelHeight, maxDevicePixelHeight));
|
|
367
|
+
this._setDevicePixelSize(this._getDevicePixelSizeFromResizeEntry(entry));
|
|
361
368
|
|
|
362
369
|
this._updateDrawingBufferSize();
|
|
363
370
|
|
|
@@ -385,6 +392,40 @@ export abstract class CanvasSurface {
|
|
|
385
392
|
this.updatePosition();
|
|
386
393
|
}
|
|
387
394
|
|
|
395
|
+
protected _getDevicePixelSizeFromResizeEntry(entry: ResizeObserverEntry): DevicePixelSize {
|
|
396
|
+
const contentBoxSize = assertDefined(entry.contentBoxSize?.[0]);
|
|
397
|
+
|
|
398
|
+
if (this.props.pixelSizeSource === 'css-dpr') {
|
|
399
|
+
return this._getDevicePixelSizeFromCSSSize(
|
|
400
|
+
contentBoxSize.inlineSize,
|
|
401
|
+
contentBoxSize.blockSize
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return {
|
|
406
|
+
devicePixelWidth:
|
|
407
|
+
entry.devicePixelContentBoxSize?.[0]?.inlineSize ||
|
|
408
|
+
contentBoxSize.inlineSize * devicePixelRatio,
|
|
409
|
+
devicePixelHeight:
|
|
410
|
+
entry.devicePixelContentBoxSize?.[0]?.blockSize ||
|
|
411
|
+
contentBoxSize.blockSize * devicePixelRatio
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
protected _getDevicePixelSizeFromCSSSize(cssWidth: number, cssHeight: number): DevicePixelSize {
|
|
416
|
+
const devicePixelRatio = this.getDevicePixelRatio();
|
|
417
|
+
return {
|
|
418
|
+
devicePixelWidth: Math.floor(cssWidth * devicePixelRatio),
|
|
419
|
+
devicePixelHeight: Math.floor(cssHeight * devicePixelRatio)
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
protected _setDevicePixelSize({devicePixelWidth, devicePixelHeight}: DevicePixelSize): void {
|
|
424
|
+
const [maxDevicePixelWidth, maxDevicePixelHeight] = this.getMaxDrawingBufferSize();
|
|
425
|
+
this.devicePixelWidth = Math.max(1, Math.min(devicePixelWidth, maxDevicePixelWidth));
|
|
426
|
+
this.devicePixelHeight = Math.max(1, Math.min(devicePixelHeight, maxDevicePixelHeight));
|
|
427
|
+
}
|
|
428
|
+
|
|
388
429
|
_resizeDrawingBufferIfNeeded() {
|
|
389
430
|
if (this._needsDrawingBufferResize) {
|
|
390
431
|
this._needsDrawingBufferResize = false;
|
|
@@ -406,6 +447,15 @@ export abstract class CanvasSurface {
|
|
|
406
447
|
const oldRatio = this.devicePixelRatio;
|
|
407
448
|
this.devicePixelRatio = window.devicePixelRatio;
|
|
408
449
|
|
|
450
|
+
if (this.props.pixelSizeSource === 'css-dpr') {
|
|
451
|
+
// In css-dpr mode the ResizeObserver watches content-box, which won't fire on a pure DPR
|
|
452
|
+
// change (CSS size unchanged). Recalculate the drawing buffer from the new DPR here.
|
|
453
|
+
const oldPixelSize = this.getDevicePixelSize();
|
|
454
|
+
this._setDevicePixelSize(this._getDevicePixelSizeFromCSSSize(this.cssWidth, this.cssHeight));
|
|
455
|
+
this._updateDrawingBufferSize();
|
|
456
|
+
this.device.props.onResize(this as CanvasContext | PresentationContext, {oldPixelSize});
|
|
457
|
+
}
|
|
458
|
+
|
|
409
459
|
this.updatePosition();
|
|
410
460
|
|
|
411
461
|
this.device.props.onDevicePixelRatioChange?.(this as CanvasContext | PresentationContext, {
|