@atlaskit/react-ufo 3.1.4 → 3.3.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.
- package/CHANGELOG.md +16 -0
- package/README.md +11 -0
- package/dist/cjs/create-experimental-interaction-metrics-payload/index.js +52 -25
- package/dist/cjs/create-payload/index.js +290 -231
- package/dist/cjs/interaction-metrics/index.js +61 -23
- package/dist/cjs/interaction-metrics/post-interaction-log.js +63 -34
- package/dist/cjs/interaction-metrics-init/index.js +26 -7
- package/dist/cjs/segment/schedule-on-paint.js +35 -0
- package/dist/cjs/segment/segment.js +10 -1
- package/dist/cjs/vc/index.js +105 -1
- package/dist/cjs/vc/types.js +5 -0
- package/dist/cjs/vc/vc-observer/index.js +198 -208
- package/dist/cjs/vc/vc-observer-new/entries-timeline/index.js +56 -0
- package/dist/cjs/vc/vc-observer-new/get-element-name.js +68 -0
- package/dist/cjs/vc/vc-observer-new/index.js +132 -0
- package/dist/cjs/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +75 -0
- package/dist/cjs/vc/vc-observer-new/metric-calculator/fy25_03/index.js +60 -0
- package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/canvas-pixel.js +274 -0
- package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.js +151 -0
- package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/heatmap.js +367 -0
- package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/index.js +397 -0
- package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/types.js +5 -0
- package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/index.js +61 -0
- package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/calc-union-area.js +151 -0
- package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/index.js +107 -0
- package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/types.js +5 -0
- package/dist/cjs/vc/vc-observer-new/metric-calculator/types.js +5 -0
- package/dist/cjs/vc/vc-observer-new/metric-calculator/utils/get-viewport-height.js +16 -0
- package/dist/cjs/vc/vc-observer-new/metric-calculator/utils/get-viewport-width.js +16 -0
- package/dist/cjs/vc/vc-observer-new/metric-calculator/utils/is-viewport-entry-data.js +16 -0
- package/dist/cjs/vc/vc-observer-new/metric-calculator/utils/task-yield.js +45 -0
- package/dist/cjs/vc/vc-observer-new/types.js +5 -0
- package/dist/cjs/vc/vc-observer-new/viewport-observer/index.js +195 -0
- package/dist/cjs/vc/vc-observer-new/viewport-observer/intersection-observer/index.js +71 -0
- package/dist/cjs/vc/vc-observer-new/viewport-observer/mutation-observer/index.js +65 -0
- package/dist/cjs/vc/vc-observer-new/viewport-observer/performance-observer/index.js +58 -0
- package/dist/cjs/vc/vc-observer-new/viewport-observer/types.js +1 -0
- package/dist/cjs/vc/vc-observer-new/window-event-observer/index.js +54 -0
- package/dist/es2019/create-experimental-interaction-metrics-payload/index.js +2 -2
- package/dist/es2019/create-payload/index.js +8 -7
- package/dist/es2019/interaction-metrics/index.js +3 -3
- package/dist/es2019/interaction-metrics/post-interaction-log.js +5 -5
- package/dist/es2019/interaction-metrics-init/index.js +26 -7
- package/dist/es2019/segment/schedule-on-paint.js +29 -0
- package/dist/es2019/segment/segment.js +9 -1
- package/dist/es2019/vc/index.js +56 -1
- package/dist/es2019/vc/types.js +1 -0
- package/dist/es2019/vc/vc-observer/index.js +1 -4
- package/dist/es2019/vc/vc-observer-new/entries-timeline/index.js +34 -0
- package/dist/es2019/vc/vc-observer-new/get-element-name.js +62 -0
- package/dist/es2019/vc/vc-observer-new/index.js +98 -0
- package/dist/es2019/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +38 -0
- package/dist/es2019/vc/vc-observer-new/metric-calculator/fy25_03/index.js +39 -0
- package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/canvas-pixel.js +194 -0
- package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.js +69 -0
- package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/heatmap.js +248 -0
- package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/index.js +261 -0
- package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/types.js +1 -0
- package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/index.js +19 -0
- package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/calc-union-area.js +98 -0
- package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/index.js +58 -0
- package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/types.js +1 -0
- package/dist/es2019/vc/vc-observer-new/metric-calculator/types.js +1 -0
- package/dist/es2019/vc/vc-observer-new/metric-calculator/utils/get-viewport-height.js +9 -0
- package/dist/es2019/vc/vc-observer-new/metric-calculator/utils/get-viewport-width.js +9 -0
- package/dist/es2019/vc/vc-observer-new/metric-calculator/utils/is-viewport-entry-data.js +10 -0
- package/dist/es2019/vc/vc-observer-new/metric-calculator/utils/task-yield.js +17 -0
- package/dist/es2019/vc/vc-observer-new/types.js +1 -0
- package/dist/es2019/vc/vc-observer-new/viewport-observer/index.js +168 -0
- package/dist/es2019/vc/vc-observer-new/viewport-observer/intersection-observer/index.js +65 -0
- package/dist/es2019/vc/vc-observer-new/viewport-observer/mutation-observer/index.js +48 -0
- package/dist/es2019/vc/vc-observer-new/viewport-observer/performance-observer/index.js +41 -0
- package/dist/es2019/vc/vc-observer-new/viewport-observer/types.js +0 -0
- package/dist/es2019/vc/vc-observer-new/window-event-observer/index.js +36 -0
- package/dist/esm/create-experimental-interaction-metrics-payload/index.js +52 -25
- package/dist/esm/create-payload/index.js +290 -231
- package/dist/esm/interaction-metrics/index.js +61 -23
- package/dist/esm/interaction-metrics/post-interaction-log.js +63 -34
- package/dist/esm/interaction-metrics-init/index.js +26 -7
- package/dist/esm/segment/schedule-on-paint.js +29 -0
- package/dist/esm/segment/segment.js +10 -1
- package/dist/esm/vc/index.js +104 -1
- package/dist/esm/vc/types.js +1 -0
- package/dist/esm/vc/vc-observer/index.js +198 -208
- package/dist/esm/vc/vc-observer-new/entries-timeline/index.js +50 -0
- package/dist/esm/vc/vc-observer-new/get-element-name.js +62 -0
- package/dist/esm/vc/vc-observer-new/index.js +126 -0
- package/dist/esm/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +69 -0
- package/dist/esm/vc/vc-observer-new/metric-calculator/fy25_03/index.js +54 -0
- package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/canvas-pixel.js +268 -0
- package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.js +143 -0
- package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/heatmap.js +361 -0
- package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/index.js +390 -0
- package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/types.js +1 -0
- package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/index.js +54 -0
- package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/calc-union-area.js +144 -0
- package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/index.js +100 -0
- package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/types.js +1 -0
- package/dist/esm/vc/vc-observer-new/metric-calculator/types.js +1 -0
- package/dist/esm/vc/vc-observer-new/metric-calculator/utils/get-viewport-height.js +10 -0
- package/dist/esm/vc/vc-observer-new/metric-calculator/utils/get-viewport-width.js +10 -0
- package/dist/esm/vc/vc-observer-new/metric-calculator/utils/is-viewport-entry-data.js +10 -0
- package/dist/esm/vc/vc-observer-new/metric-calculator/utils/task-yield.js +38 -0
- package/dist/esm/vc/vc-observer-new/types.js +1 -0
- package/dist/esm/vc/vc-observer-new/viewport-observer/index.js +189 -0
- package/dist/esm/vc/vc-observer-new/viewport-observer/intersection-observer/index.js +65 -0
- package/dist/esm/vc/vc-observer-new/viewport-observer/mutation-observer/index.js +59 -0
- package/dist/esm/vc/vc-observer-new/viewport-observer/performance-observer/index.js +51 -0
- package/dist/esm/vc/vc-observer-new/viewport-observer/types.js +0 -0
- package/dist/esm/vc/vc-observer-new/window-event-observer/index.js +48 -0
- package/dist/types/common/index.d.ts +1 -0
- package/dist/types/create-experimental-interaction-metrics-payload/index.d.ts +3 -2
- package/dist/types/create-payload/index.d.ts +4 -4
- package/dist/types/interaction-metrics/post-interaction-log.d.ts +3 -2
- package/dist/types/segment/schedule-on-paint.d.ts +2 -0
- package/dist/types/vc/index.d.ts +3 -3
- package/dist/types/vc/types.d.ts +34 -0
- package/dist/types/vc/vc-observer/index.d.ts +5 -21
- package/dist/types/vc/vc-observer-new/entries-timeline/index.d.ts +13 -0
- package/dist/types/vc/vc-observer-new/get-element-name.d.ts +8 -0
- package/dist/types/vc/vc-observer-new/index.d.ts +18 -0
- package/dist/types/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.d.ts +9 -0
- package/dist/types/vc/vc-observer-new/metric-calculator/fy25_03/index.d.ts +7 -0
- package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/canvas-pixel.d.ts +91 -0
- package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.d.ts +4 -0
- package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/heatmap.d.ts +39 -0
- package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/index.d.ts +8 -0
- package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/types.d.ts +43 -0
- package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/index.d.ts +3 -0
- package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/calc-union-area.d.ts +11 -0
- package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/index.d.ts +23 -0
- package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/types.d.ts +9 -0
- package/dist/types/vc/vc-observer-new/metric-calculator/types.d.ts +20 -0
- package/dist/types/vc/vc-observer-new/metric-calculator/utils/get-viewport-height.d.ts +1 -0
- package/dist/types/vc/vc-observer-new/metric-calculator/utils/get-viewport-width.d.ts +1 -0
- package/dist/types/vc/vc-observer-new/metric-calculator/utils/is-viewport-entry-data.d.ts +2 -0
- package/dist/types/vc/vc-observer-new/metric-calculator/utils/task-yield.d.ts +1 -0
- package/dist/types/vc/vc-observer-new/types.d.ts +21 -0
- package/dist/types/vc/vc-observer-new/viewport-observer/index.d.ts +22 -0
- package/dist/types/vc/vc-observer-new/viewport-observer/intersection-observer/index.d.ts +30 -0
- package/dist/types/vc/vc-observer-new/viewport-observer/mutation-observer/index.d.ts +14 -0
- package/dist/types/vc/vc-observer-new/viewport-observer/performance-observer/index.d.ts +14 -0
- package/dist/types/vc/vc-observer-new/viewport-observer/types.d.ts +10 -0
- package/dist/types/vc/vc-observer-new/window-event-observer/index.d.ts +17 -0
- package/dist/types-ts4.5/common/index.d.ts +1 -0
- package/dist/types-ts4.5/create-experimental-interaction-metrics-payload/index.d.ts +3 -2
- package/dist/types-ts4.5/create-payload/index.d.ts +4 -4
- package/dist/types-ts4.5/interaction-metrics/post-interaction-log.d.ts +3 -2
- package/dist/types-ts4.5/segment/schedule-on-paint.d.ts +2 -0
- package/dist/types-ts4.5/vc/index.d.ts +3 -3
- package/dist/types-ts4.5/vc/types.d.ts +34 -0
- package/dist/types-ts4.5/vc/vc-observer/index.d.ts +5 -21
- package/dist/types-ts4.5/vc/vc-observer-new/entries-timeline/index.d.ts +13 -0
- package/dist/types-ts4.5/vc/vc-observer-new/get-element-name.d.ts +8 -0
- package/dist/types-ts4.5/vc/vc-observer-new/index.d.ts +18 -0
- package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.d.ts +9 -0
- package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/fy25_03/index.d.ts +7 -0
- package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/canvas-pixel.d.ts +91 -0
- package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.d.ts +4 -0
- package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/heatmap.d.ts +39 -0
- package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/index.d.ts +8 -0
- package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/types.d.ts +43 -0
- package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/index.d.ts +3 -0
- package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/calc-union-area.d.ts +11 -0
- package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/index.d.ts +23 -0
- package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/types.d.ts +9 -0
- package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/types.d.ts +20 -0
- package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/utils/get-viewport-height.d.ts +1 -0
- package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/utils/get-viewport-width.d.ts +1 -0
- package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/utils/is-viewport-entry-data.d.ts +2 -0
- package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/utils/task-yield.d.ts +1 -0
- package/dist/types-ts4.5/vc/vc-observer-new/types.d.ts +21 -0
- package/dist/types-ts4.5/vc/vc-observer-new/viewport-observer/index.d.ts +22 -0
- package/dist/types-ts4.5/vc/vc-observer-new/viewport-observer/intersection-observer/index.d.ts +30 -0
- package/dist/types-ts4.5/vc/vc-observer-new/viewport-observer/mutation-observer/index.d.ts +14 -0
- package/dist/types-ts4.5/vc/vc-observer-new/viewport-observer/performance-observer/index.d.ts +14 -0
- package/dist/types-ts4.5/vc/vc-observer-new/viewport-observer/types.d.ts +10 -0
- package/dist/types-ts4.5/vc/vc-observer-new/window-event-observer/index.d.ts +17 -0
- package/package.json +13 -1
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import taskYield from '../../utils/task-yield';
|
|
2
|
+
|
|
3
|
+
// 24-bit color value
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Class responsible for managing a scaled canvas and tracking pixel drawing operations.
|
|
7
|
+
* It uses an OffscreenCanvas for better performance and maintains a mapping between
|
|
8
|
+
* colors and timestamps for pixel counting purposes.
|
|
9
|
+
*/
|
|
10
|
+
export class ViewportCanvas {
|
|
11
|
+
/** The underlying OffscreenCanvas instance */
|
|
12
|
+
|
|
13
|
+
/** The 2D rendering context of the canvas */
|
|
14
|
+
|
|
15
|
+
/** Scale factor applied to the canvas (affects final pixel counts) */
|
|
16
|
+
|
|
17
|
+
/** Maps unique colors to their corresponding timestamps */
|
|
18
|
+
|
|
19
|
+
/** Counter used to generate unique colors */
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Creates a new ViewportCanvas instance.
|
|
23
|
+
* @param viewport - The dimensions of the viewport
|
|
24
|
+
* @param scaleFactor - Scale factor to apply to the canvas (default: 0.5)
|
|
25
|
+
* @throws {Error} If canvas context cannot be obtained
|
|
26
|
+
*/
|
|
27
|
+
constructor(viewport, scaleFactor = 1) {
|
|
28
|
+
this.scaleFactor = scaleFactor;
|
|
29
|
+
this.colorCounter = 1;
|
|
30
|
+
this.colorTimeMap = new Map();
|
|
31
|
+
|
|
32
|
+
// Calculate scaled dimensions
|
|
33
|
+
this.scaledWidth = Math.ceil(viewport.width * scaleFactor);
|
|
34
|
+
this.scaledHeight = Math.ceil(viewport.height * scaleFactor);
|
|
35
|
+
this.scaleX = this.scaledWidth / viewport.width;
|
|
36
|
+
this.scaleY = this.scaledHeight / viewport.height;
|
|
37
|
+
|
|
38
|
+
// Initialize OffscreenCanvas with scaled dimensions
|
|
39
|
+
this.canvas = document.createElement('canvas');
|
|
40
|
+
this.canvas.width = this.scaledWidth;
|
|
41
|
+
this.canvas.height = this.scaledHeight;
|
|
42
|
+
const ctx = this.canvas.getContext('2d', {
|
|
43
|
+
alpha: false,
|
|
44
|
+
// Disable alpha channel for better performance
|
|
45
|
+
willReadFrequently: true,
|
|
46
|
+
// Optimize for frequent pixel reading
|
|
47
|
+
colorSpace: 'srgb' // Use standard RGB color space
|
|
48
|
+
});
|
|
49
|
+
if (!ctx) {
|
|
50
|
+
throw new Error('Could not get canvas context');
|
|
51
|
+
}
|
|
52
|
+
this.ctx = ctx;
|
|
53
|
+
this.ctx.globalCompositeOperation = 'source-over';
|
|
54
|
+
this.clear();
|
|
55
|
+
}
|
|
56
|
+
getScaledDimensions() {
|
|
57
|
+
return {
|
|
58
|
+
width: this.scaledWidth,
|
|
59
|
+
height: this.scaledHeight
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Clears the entire canvas by removing all drawn content.
|
|
65
|
+
* @private
|
|
66
|
+
*/
|
|
67
|
+
clear() {
|
|
68
|
+
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Generates a unique RGB color from an incrementing counter.
|
|
73
|
+
* Uses a 24-bit color space (16,777,216 possible colors).
|
|
74
|
+
*
|
|
75
|
+
* @private
|
|
76
|
+
* @returns A unique RGB color string
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* // Example bit operations for color 0x00FF8040:
|
|
80
|
+
* // Red: (0x00FF8040 >> 16) & 0xFF = 0xFF = 255
|
|
81
|
+
* // Green: (0x00FF8040 >> 8) & 0xFF = 0x80 = 128
|
|
82
|
+
* // Blue: 0x00FF8040 & 0xFF = 0x40 = 64
|
|
83
|
+
*/
|
|
84
|
+
generateColor() {
|
|
85
|
+
// Wrap around at 16,777,215 (0x00FFFFFF) to stay within 24-bit color space
|
|
86
|
+
const rgbColor = this.colorCounter++ % 0x00ffffff;
|
|
87
|
+
return getRGBComponents(rgbColor);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Draws a rectangle on the canvas with a unique color and associates it with a timestamp.
|
|
92
|
+
* Each drawn rectangle gets a unique color which is mapped to the provided timestamp.
|
|
93
|
+
*
|
|
94
|
+
* @param rect - The rectangle dimensions to draw
|
|
95
|
+
* @param timestamp - The timestamp to associate with this drawing operation
|
|
96
|
+
*/
|
|
97
|
+
drawRect(rect, timestamp) {
|
|
98
|
+
const color = this.generateColor();
|
|
99
|
+
this.colorTimeMap.set(color, timestamp);
|
|
100
|
+
this.ctx.fillStyle = color;
|
|
101
|
+
if (this.scaleFactor === 1) {
|
|
102
|
+
return this.ctx.fillRect(rect.x, rect.y, rect.width, rect.height);
|
|
103
|
+
}
|
|
104
|
+
const scaledX = rect.x * this.scaleX;
|
|
105
|
+
const scaledY = rect.y * this.scaleY;
|
|
106
|
+
const scaledWidth = rect.width * this.scaleX;
|
|
107
|
+
const scaledHeight = rect.height * this.scaleY;
|
|
108
|
+
this.ctx.fillRect(Math.floor(scaledX), Math.ceil(scaledY), Math.max(scaledWidth, 1), Math.max(scaledHeight, 1));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Calculates the number of pixels drawn for each timestamp.
|
|
113
|
+
* This method:
|
|
114
|
+
* 1. Reads the pixel data from the canvas
|
|
115
|
+
* 2. Counts pixels of each unique color
|
|
116
|
+
* 3. Maps colors back to their timestamps
|
|
117
|
+
* 4. Adjusts counts based on the scale factor
|
|
118
|
+
*
|
|
119
|
+
* @returns A Map containing timestamp to pixel count mappings
|
|
120
|
+
*/
|
|
121
|
+
async getPixelCounts() {
|
|
122
|
+
const imageData = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);
|
|
123
|
+
const timestampsAmount = this.colorTimeMap.size;
|
|
124
|
+
const pixelCounts = await calculateDrawnPixelsRaw(imageData, this.scaleFactor, timestampsAmount);
|
|
125
|
+
// Convert color counts to timestamp counts
|
|
126
|
+
const timePixelCounts = new Map();
|
|
127
|
+
for (let i = 0; i < pixelCounts.length; i++) {
|
|
128
|
+
const count = pixelCounts[i];
|
|
129
|
+
if (typeof count !== 'number') {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const color = i + 1;
|
|
133
|
+
const rgbColor = getRGBComponents(color);
|
|
134
|
+
const timestamp = this.colorTimeMap.get(rgbColor);
|
|
135
|
+
if (timestamp !== undefined) {
|
|
136
|
+
timePixelCounts.set(timestamp, (timePixelCounts.get(timestamp) || 0) + count);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return timePixelCounts;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Converts a number into RGB components in such a way that they can be recombined
|
|
145
|
+
* to form the original number using bitwise operations.
|
|
146
|
+
* @param number - The input number to be split into RGB components.
|
|
147
|
+
* @returns The RGB color string in the format "rgb(r, g, b)".
|
|
148
|
+
*/
|
|
149
|
+
export function getRGBComponents(n) {
|
|
150
|
+
// Ensure the input is within the valid range for a 24-bit color
|
|
151
|
+
if (n < 0 || n > 0xffffff) {
|
|
152
|
+
throw new Error('Input number must be between 0 and 16777215 (inclusive).');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Extract blue component (bits 0-7)
|
|
156
|
+
const blue = n & 0xff;
|
|
157
|
+
|
|
158
|
+
// Extract green component (bits 8-15)
|
|
159
|
+
const green = n >> 8 & 0xff;
|
|
160
|
+
|
|
161
|
+
// Extract red component (bits 16-23)
|
|
162
|
+
const red = n >> 16 & 0xff;
|
|
163
|
+
return `rgb(${red}, ${green}, ${blue})`;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Calculates the number of pixels drawn for each color in the image data.
|
|
168
|
+
* @param imageData - The image data to analyze.
|
|
169
|
+
* @param scaleFactor - The scale factor applied to the canvas.
|
|
170
|
+
* @param arraySize - The amount of timestamps that were drawn in the viewport
|
|
171
|
+
* @returns A Map containing color to pixel count mappings.
|
|
172
|
+
*/
|
|
173
|
+
export async function calculateDrawnPixelsRaw(imageData, scaleFactor, arraySize) {
|
|
174
|
+
const data = imageData.data;
|
|
175
|
+
const scaleCompensation = Math.round(1 / (scaleFactor * scaleFactor));
|
|
176
|
+
const arr = new Uint32Array(arraySize);
|
|
177
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
178
|
+
// Check alpha
|
|
179
|
+
if (data[i + 3] !== 0) {
|
|
180
|
+
// Combine RGB components into a single 24-bit color value:
|
|
181
|
+
// (data[i] << 16) - Shift red component left 16 bits (bits 16-23)
|
|
182
|
+
// (data[i + 1] << 8) - Shift green component left 8 bits (bits 8-15)
|
|
183
|
+
// data[i + 2] - Blue component stays as is (bits 0-7)
|
|
184
|
+
// The | operator combines all bits together
|
|
185
|
+
const color = data[i] << 16 | data[i + 1] << 8 | data[i + 2];
|
|
186
|
+
const colorIndex = color - 1;
|
|
187
|
+
arr[colorIndex] = (arr[colorIndex] || 0) + scaleCompensation;
|
|
188
|
+
}
|
|
189
|
+
if (i % 10000 === 0) {
|
|
190
|
+
await taskYield();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return arr;
|
|
194
|
+
}
|
package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
2
|
+
import { ViewportCanvas } from './canvas-pixel';
|
|
3
|
+
export default async function calculateTTVCPercentiles({
|
|
4
|
+
viewport,
|
|
5
|
+
orderedEntries,
|
|
6
|
+
percentiles
|
|
7
|
+
}) {
|
|
8
|
+
const canvas = new ViewportCanvas(viewport, fg('platform_ufo_canvas_heatmap_full_precision') ? 1 : 0.25);
|
|
9
|
+
const elementMap = new Map();
|
|
10
|
+
for (const entry of orderedEntries) {
|
|
11
|
+
if (!('rect' in entry.data)) {
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
const rect = entry.data.rect;
|
|
15
|
+
const elementName = entry.data.elementName;
|
|
16
|
+
canvas.drawRect(rect, entry.time);
|
|
17
|
+
if (!elementMap.has(entry.time)) {
|
|
18
|
+
elementMap.set(entry.time, new Set());
|
|
19
|
+
}
|
|
20
|
+
elementMap.get(entry.time).add(elementName);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Get pixel counts
|
|
24
|
+
const timePixelCounts = await canvas.getPixelCounts();
|
|
25
|
+
const viewportTotalPixels = viewport.width * viewport.height;
|
|
26
|
+
return calculatePercentiles(timePixelCounts, elementMap, percentiles, viewportTotalPixels);
|
|
27
|
+
}
|
|
28
|
+
export function calculatePercentiles(timePixelCounts, elementMap, unorderedPercentiles, totalPixels) {
|
|
29
|
+
const results = {};
|
|
30
|
+
let cumulativePixels = 0;
|
|
31
|
+
const percentiles = unorderedPercentiles.sort((a, b) => a - b);
|
|
32
|
+
|
|
33
|
+
// Sort entries by timestamp for consistent processing
|
|
34
|
+
const sortedEntries = Array.from(timePixelCounts.entries()).sort(([timeA], [timeB]) => Number(timeA) - Number(timeB));
|
|
35
|
+
let percentileIndex = 0;
|
|
36
|
+
let domElementsBuffer = new Set();
|
|
37
|
+
for (const [time, pixelCount] of sortedEntries) {
|
|
38
|
+
cumulativePixels += pixelCount;
|
|
39
|
+
const percentCovered = cumulativePixels / totalPixels * 100;
|
|
40
|
+
const elementNames = elementMap.get(time) || new Set();
|
|
41
|
+
elementNames.forEach(elName => domElementsBuffer.add(elName));
|
|
42
|
+
let matchesAnyCheckpoints = false;
|
|
43
|
+
while (percentileIndex < percentiles.length && percentCovered >= percentiles[percentileIndex]) {
|
|
44
|
+
results[`${percentiles[percentileIndex]}`] = {
|
|
45
|
+
t: Number(time),
|
|
46
|
+
e: Array.from(domElementsBuffer)
|
|
47
|
+
};
|
|
48
|
+
percentileIndex++;
|
|
49
|
+
matchesAnyCheckpoints = true;
|
|
50
|
+
}
|
|
51
|
+
if (matchesAnyCheckpoints) {
|
|
52
|
+
domElementsBuffer.clear();
|
|
53
|
+
}
|
|
54
|
+
if (percentileIndex >= percentiles.length) {
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Fill in any missing percentiles
|
|
60
|
+
for (const percentile of percentiles) {
|
|
61
|
+
if (!(percentile in results)) {
|
|
62
|
+
results[`${percentile}`] = {
|
|
63
|
+
t: 0,
|
|
64
|
+
e: []
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return results;
|
|
69
|
+
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import isViewportEntryData from '../../utils/is-viewport-entry-data';
|
|
2
|
+
import taskYield from '../../utils/task-yield';
|
|
3
|
+
const MAX_HEATMAP_SIZE = 1000;
|
|
4
|
+
function createEmptyHeatmapEntry() {
|
|
5
|
+
return {
|
|
6
|
+
head: null,
|
|
7
|
+
previousEntries: []
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
function createEmptyMap(heatmapWidth, heatmapHeight) {
|
|
11
|
+
return Array.from({
|
|
12
|
+
length: heatmapHeight
|
|
13
|
+
}).map(() => Array.from({
|
|
14
|
+
length: heatmapWidth
|
|
15
|
+
}).map(createEmptyHeatmapEntry));
|
|
16
|
+
}
|
|
17
|
+
function isRectInside(a, b) {
|
|
18
|
+
if (!a || !b) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Check if all corners of rectangle a are within the bounds of rectangle b
|
|
23
|
+
return a.left >= b.left && a.right <= b.right && a.top >= b.top && a.bottom <= b.bottom;
|
|
24
|
+
}
|
|
25
|
+
export default class Heatmap {
|
|
26
|
+
/**
|
|
27
|
+
* Heatmap Width
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Heatmap Height
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Heatmap Area (width * height)
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
constructor({
|
|
39
|
+
viewport,
|
|
40
|
+
heatmapSize
|
|
41
|
+
}) {
|
|
42
|
+
// TODO timeOrigin? do we need? for SSR??
|
|
43
|
+
this.viewport = viewport;
|
|
44
|
+
const safeSize = Math.min(heatmapSize, MAX_HEATMAP_SIZE);
|
|
45
|
+
if (viewport.width === 0 || viewport.height === 0) {
|
|
46
|
+
this.width = safeSize;
|
|
47
|
+
this.height = safeSize;
|
|
48
|
+
this.scaleX = 1;
|
|
49
|
+
this.scaleY = 1;
|
|
50
|
+
this.heatmapAreaSize = 0;
|
|
51
|
+
this.map = createEmptyMap(safeSize, safeSize);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const aspectRatio = viewport.width / viewport.height;
|
|
55
|
+
if (aspectRatio > 1) {
|
|
56
|
+
// Landscape orientation
|
|
57
|
+
this.width = safeSize;
|
|
58
|
+
this.height = Math.round(safeSize / aspectRatio);
|
|
59
|
+
} else {
|
|
60
|
+
// Portrait orientation
|
|
61
|
+
this.width = safeSize;
|
|
62
|
+
this.height = Math.round(safeSize * aspectRatio);
|
|
63
|
+
}
|
|
64
|
+
this.scaleX = this.width / viewport.width;
|
|
65
|
+
this.scaleY = this.height / viewport.height;
|
|
66
|
+
this.heatmapAreaSize = this.width * this.height;
|
|
67
|
+
this.map = createEmptyMap(this.width, this.height);
|
|
68
|
+
}
|
|
69
|
+
getHeatmap() {
|
|
70
|
+
return this.map;
|
|
71
|
+
}
|
|
72
|
+
getCell(row, col) {
|
|
73
|
+
var _this$map$row;
|
|
74
|
+
return (_this$map$row = this.map[row]) === null || _this$map$row === void 0 ? void 0 : _this$map$row[col];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Map Dom Rect to Heatmap Rect, rounded up to occupy full cell.
|
|
79
|
+
* @param rect DOM Rect
|
|
80
|
+
* @returns
|
|
81
|
+
*/
|
|
82
|
+
mapDOMRectToHeatmap(rect) {
|
|
83
|
+
const scaledX = rect.x * this.scaleX;
|
|
84
|
+
const scaledY = rect.y * this.scaleY;
|
|
85
|
+
const scaledWidth = rect.width * this.scaleX;
|
|
86
|
+
const scaledHeight = rect.height * this.scaleY;
|
|
87
|
+
return {
|
|
88
|
+
left: Math.floor(scaledX),
|
|
89
|
+
right: Math.ceil(scaledX + scaledWidth),
|
|
90
|
+
top: Math.floor(scaledY),
|
|
91
|
+
bottom: Math.ceil(scaledY + scaledHeight)
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Calculate the ratio of a HeatmapRect compared to the full heatmap
|
|
97
|
+
*
|
|
98
|
+
* This function determines what fraction of the heatmap is covered by the given heatmap rectangle.
|
|
99
|
+
*
|
|
100
|
+
* @param rect
|
|
101
|
+
*/
|
|
102
|
+
getRatio(rect) {
|
|
103
|
+
if (this.viewport.width === 0 || this.viewport.height === 0) {
|
|
104
|
+
return 0;
|
|
105
|
+
}
|
|
106
|
+
const {
|
|
107
|
+
right,
|
|
108
|
+
left,
|
|
109
|
+
bottom,
|
|
110
|
+
top
|
|
111
|
+
} = rect;
|
|
112
|
+
const rectWidth = right - left;
|
|
113
|
+
const rectHeight = bottom - top;
|
|
114
|
+
const rectArea = rectWidth * rectHeight;
|
|
115
|
+
const ratio = rectArea / this.heatmapAreaSize;
|
|
116
|
+
if (ratio > 1) {
|
|
117
|
+
return 1;
|
|
118
|
+
}
|
|
119
|
+
return ratio;
|
|
120
|
+
}
|
|
121
|
+
async applyEntriesToHeatmap(entries) {
|
|
122
|
+
for (let i = 0; i < entries.length; i++) {
|
|
123
|
+
const entry = entries[i];
|
|
124
|
+
const {
|
|
125
|
+
time,
|
|
126
|
+
type,
|
|
127
|
+
data
|
|
128
|
+
} = entry;
|
|
129
|
+
if (isViewportEntryData(data)) {
|
|
130
|
+
const rect = this.mapDOMRectToHeatmap(data.rect);
|
|
131
|
+
const ratio = this.getRatio(rect);
|
|
132
|
+
const heatmapEntryData = {
|
|
133
|
+
time,
|
|
134
|
+
elementName: data.elementName,
|
|
135
|
+
ratio: ratio !== null && ratio !== void 0 ? ratio : null,
|
|
136
|
+
rect,
|
|
137
|
+
source: type
|
|
138
|
+
};
|
|
139
|
+
const roundedTop = Math.floor(rect.top);
|
|
140
|
+
const roundedBottom = Math.min(rect.bottom, this.height);
|
|
141
|
+
const roundedLeft = Math.floor(rect.left);
|
|
142
|
+
const roundedRight = Math.min(rect.right, this.width);
|
|
143
|
+
for (let row = roundedTop; row < roundedBottom; row++) {
|
|
144
|
+
for (let col = roundedLeft; col < roundedRight; col++) {
|
|
145
|
+
const cell = this.getCell(row, col);
|
|
146
|
+
if (!cell) {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
const previousEntry = cell.head;
|
|
150
|
+
|
|
151
|
+
// When elements are added at the same time
|
|
152
|
+
// we try to keep the inner element changes as the head
|
|
153
|
+
if ((previousEntry === null || previousEntry === void 0 ? void 0 : previousEntry.time) === entry.time && isRectInside(previousEntry.rect, heatmapEntryData.rect)) {
|
|
154
|
+
cell.previousEntries.push({
|
|
155
|
+
...heatmapEntryData,
|
|
156
|
+
source: 'mutation:parent-mounted'
|
|
157
|
+
});
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
cell.head = {
|
|
161
|
+
...heatmapEntryData,
|
|
162
|
+
source: heatmapEntryData.source || null
|
|
163
|
+
};
|
|
164
|
+
if (previousEntry !== null) {
|
|
165
|
+
cell.previousEntries.push(previousEntry);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// Every 100 events processed
|
|
171
|
+
// we give the browser the power
|
|
172
|
+
// to process any other high priority task
|
|
173
|
+
if (i % 100 === 0) {
|
|
174
|
+
await taskYield();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
async getVCPercentMetrics(vcPercentCheckpoint) {
|
|
179
|
+
const sortedCheckpoints = [...vcPercentCheckpoint].sort((a, b) => a - b);
|
|
180
|
+
const flattenHeatmap = this.map.flat();
|
|
181
|
+
const totalCells = flattenHeatmap.length;
|
|
182
|
+
const timestampMap = new Map();
|
|
183
|
+
for (let i = 0; i < flattenHeatmap.length; i++) {
|
|
184
|
+
var _cellHead$time, _timestampMap$get;
|
|
185
|
+
const cell = flattenHeatmap[i];
|
|
186
|
+
const cellHead = cell.head;
|
|
187
|
+
const timestamp = Math.trunc((_cellHead$time = cellHead === null || cellHead === void 0 ? void 0 : cellHead.time) !== null && _cellHead$time !== void 0 ? _cellHead$time : 0);
|
|
188
|
+
const elementName = cellHead === null || cellHead === void 0 ? void 0 : cellHead.elementName;
|
|
189
|
+
const curr = (_timestampMap$get = timestampMap.get(timestamp)) !== null && _timestampMap$get !== void 0 ? _timestampMap$get : {
|
|
190
|
+
cellCount: 0,
|
|
191
|
+
domElements: new Set()
|
|
192
|
+
};
|
|
193
|
+
curr.cellCount += 1;
|
|
194
|
+
if (elementName) {
|
|
195
|
+
curr.domElements.add(elementName);
|
|
196
|
+
}
|
|
197
|
+
timestampMap.set(timestamp, curr);
|
|
198
|
+
|
|
199
|
+
// Every 10000 heatmap entries processed
|
|
200
|
+
// we give the browser the power
|
|
201
|
+
// to process any other high priority task
|
|
202
|
+
if (i > 10000 && i % 10000 === 0) {
|
|
203
|
+
await taskYield();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
const sortedTimings = [...timestampMap.keys()].sort((a, b) => a - b);
|
|
207
|
+
let totalCellPainted = 0;
|
|
208
|
+
const result = {};
|
|
209
|
+
let domElementsBuffer = new Set();
|
|
210
|
+
for (let i = 0; i < sortedTimings.length; i++) {
|
|
211
|
+
const timestamp = sortedTimings[i];
|
|
212
|
+
const timestampInfo = timestampMap.get(timestamp);
|
|
213
|
+
if (!timestampInfo) {
|
|
214
|
+
throw new Error('unexpected timestampInfo not found');
|
|
215
|
+
}
|
|
216
|
+
const {
|
|
217
|
+
cellCount,
|
|
218
|
+
domElements
|
|
219
|
+
} = timestampInfo;
|
|
220
|
+
totalCellPainted += cellCount;
|
|
221
|
+
const currVCRatio = totalCellPainted / totalCells;
|
|
222
|
+
const currVCPercent = Math.round(currVCRatio * 100);
|
|
223
|
+
domElements.forEach(domElement => {
|
|
224
|
+
domElementsBuffer.add(domElement);
|
|
225
|
+
});
|
|
226
|
+
let matchesAnyCheckpoints = false;
|
|
227
|
+
while (sortedCheckpoints.length > 0 && currVCPercent >= sortedCheckpoints[0]) {
|
|
228
|
+
const checkpoint = sortedCheckpoints.shift();
|
|
229
|
+
const domElements = [...domElementsBuffer];
|
|
230
|
+
if (!checkpoint) {
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
matchesAnyCheckpoints = true;
|
|
234
|
+
result[checkpoint.toString()] = {
|
|
235
|
+
t: timestamp,
|
|
236
|
+
e: domElements
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
if (matchesAnyCheckpoints) {
|
|
240
|
+
domElementsBuffer.clear();
|
|
241
|
+
}
|
|
242
|
+
if (i % 500 === 0) {
|
|
243
|
+
await taskYield();
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return result;
|
|
247
|
+
}
|
|
248
|
+
}
|