@reuters-graphics/graphics-components 3.0.12 → 3.0.13
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/@types/global.d.ts +45 -0
- package/dist/components/Scroller/Scroller.mdx +2 -2
- package/dist/components/ScrollerBase/ScrollerBase.mdx +0 -5
- package/dist/components/ScrollerVideo/Debug.svelte +207 -0
- package/dist/components/ScrollerVideo/Debug.svelte.d.ts +5 -0
- package/dist/components/ScrollerVideo/ScrollerVideo.mdx +462 -0
- package/dist/components/ScrollerVideo/ScrollerVideo.stories.svelte +190 -0
- package/dist/components/ScrollerVideo/ScrollerVideo.stories.svelte.d.ts +19 -0
- package/dist/components/ScrollerVideo/ScrollerVideo.svelte +292 -0
- package/dist/components/ScrollerVideo/ScrollerVideo.svelte.d.ts +58 -0
- package/dist/components/ScrollerVideo/ScrollerVideoForeground.svelte +164 -0
- package/dist/components/ScrollerVideo/ScrollerVideoForeground.svelte.d.ts +17 -0
- package/dist/components/ScrollerVideo/demo/AdvancedUsecases.svelte +114 -0
- package/dist/components/ScrollerVideo/demo/AdvancedUsecases.svelte.d.ts +3 -0
- package/dist/components/ScrollerVideo/demo/Embedded.svelte +94 -0
- package/dist/components/ScrollerVideo/demo/Embedded.svelte.d.ts +3 -0
- package/dist/components/ScrollerVideo/demo/WithAi2svelteForegrounds.svelte +117 -0
- package/dist/components/ScrollerVideo/demo/WithAi2svelteForegrounds.svelte.d.ts +3 -0
- package/dist/components/ScrollerVideo/demo/WithScrollerBase.svelte +80 -0
- package/dist/components/ScrollerVideo/demo/WithScrollerBase.svelte.d.ts +3 -0
- package/dist/components/ScrollerVideo/demo/WithTextForegrounds.svelte +72 -0
- package/dist/components/ScrollerVideo/demo/WithTextForegrounds.svelte.d.ts +18 -0
- package/dist/components/ScrollerVideo/demo/graphic/ai2svelte/ai-chart.svelte +631 -0
- package/dist/components/ScrollerVideo/demo/graphic/ai2svelte/ai-chart.svelte.d.ts +3 -0
- package/dist/components/ScrollerVideo/demo/graphic/ai2svelte/annotation1.svelte +428 -0
- package/dist/components/ScrollerVideo/demo/graphic/ai2svelte/annotation1.svelte.d.ts +26 -0
- package/dist/components/ScrollerVideo/demo/graphic/ai2svelte/annotation2.svelte +402 -0
- package/dist/components/ScrollerVideo/demo/graphic/ai2svelte/annotation2.svelte.d.ts +26 -0
- package/dist/components/ScrollerVideo/demo/graphic/ai2svelte/annotation3.svelte +398 -0
- package/dist/components/ScrollerVideo/demo/graphic/ai2svelte/annotation3.svelte.d.ts +26 -0
- package/dist/components/ScrollerVideo/demo/graphic/ai2svelte/annotation4.svelte +360 -0
- package/dist/components/ScrollerVideo/demo/graphic/ai2svelte/annotation4.svelte.d.ts +26 -0
- package/dist/components/ScrollerVideo/demo/graphic/imgs/ai-chart-md.png +0 -0
- package/dist/components/ScrollerVideo/demo/graphic/imgs/ai-chart-sm.png +0 -0
- package/dist/components/ScrollerVideo/demo/graphic/imgs/ai-chart-xs.png +0 -0
- package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation1-lg.png +0 -0
- package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation1-md.png +0 -0
- package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation1-sm.png +0 -0
- package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation1-xl.png +0 -0
- package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation1-xs.png +0 -0
- package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation2-lg.png +0 -0
- package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation2-md.png +0 -0
- package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation2-sm.png +0 -0
- package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation2-xl.png +0 -0
- package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation2-xs.png +0 -0
- package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation3-lg.png +0 -0
- package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation3-md.png +0 -0
- package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation3-sm.png +0 -0
- package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation3-xl.png +0 -0
- package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation3-xs.png +0 -0
- package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation4-lg.png +0 -0
- package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation4-md.png +0 -0
- package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation4-sm.png +0 -0
- package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation4-xl.png +0 -0
- package/dist/components/ScrollerVideo/demo/graphic/imgs/annotation4-xs.png +0 -0
- package/dist/components/ScrollerVideo/ts/ScrollerVideo.d.ts +248 -0
- package/dist/components/ScrollerVideo/ts/ScrollerVideo.js +762 -0
- package/dist/components/ScrollerVideo/ts/mp4box.d.ts +137 -0
- package/dist/components/ScrollerVideo/ts/state.svelte.d.ts +51 -0
- package/dist/components/ScrollerVideo/ts/state.svelte.js +25 -0
- package/dist/components/ScrollerVideo/ts/utils.d.ts +70 -0
- package/dist/components/ScrollerVideo/ts/utils.js +92 -0
- package/dist/components/ScrollerVideo/ts/videoDecoder.d.ts +11 -0
- package/dist/components/ScrollerVideo/ts/videoDecoder.js +193 -0
- package/dist/components/ScrollerVideo/videos/HPO.mp4 +0 -0
- package/dist/components/ScrollerVideo/videos/drone.mp4 +0 -0
- package/dist/components/ScrollerVideo/videos/goldengate.mp4 +0 -0
- package/dist/components/ScrollerVideo/videos/tennis.mp4 +0 -0
- package/dist/components/ScrollerVideo/videos/waves_lg.mp4 +0 -0
- package/dist/components/ScrollerVideo/videos/waves_md.mp4 +0 -0
- package/dist/components/ScrollerVideo/videos/waves_sm.mp4 +0 -0
- package/dist/components/SiteHeadline/SiteHeadline.mdx +4 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -0
- package/package.json +3 -1
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
declare module 'mp4box' {
|
|
2
|
+
export interface MP4MediaTrack {
|
|
3
|
+
id: number;
|
|
4
|
+
created: Date;
|
|
5
|
+
modified: Date;
|
|
6
|
+
movie_duration: number;
|
|
7
|
+
movie_timescale: number;
|
|
8
|
+
layer: number;
|
|
9
|
+
alternate_group: number;
|
|
10
|
+
volume: number;
|
|
11
|
+
track_width: number;
|
|
12
|
+
track_height: number;
|
|
13
|
+
timescale: number;
|
|
14
|
+
duration: number;
|
|
15
|
+
bitrate: number;
|
|
16
|
+
codec: string;
|
|
17
|
+
language: string;
|
|
18
|
+
nb_samples: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface MP4VideoData {
|
|
22
|
+
width: number;
|
|
23
|
+
height: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface MP4VideoTrack extends MP4MediaTrack {
|
|
27
|
+
video: MP4VideoData;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface MP4AudioData {
|
|
31
|
+
sample_rate: number;
|
|
32
|
+
channel_count: number;
|
|
33
|
+
sample_size: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface MP4AudioTrack extends MP4MediaTrack {
|
|
37
|
+
audio: MP4AudioData;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type MP4Track = MP4VideoTrack | MP4AudioTrack;
|
|
41
|
+
|
|
42
|
+
export interface MP4Info {
|
|
43
|
+
duration: number;
|
|
44
|
+
timescale: number;
|
|
45
|
+
fragment_duration: number;
|
|
46
|
+
isFragmented: boolean;
|
|
47
|
+
isProgressive: boolean;
|
|
48
|
+
hasIOD: boolean;
|
|
49
|
+
brands: string[];
|
|
50
|
+
created: Date;
|
|
51
|
+
modified: Date;
|
|
52
|
+
tracks: MP4Track[];
|
|
53
|
+
audioTracks: MP4AudioTrack[];
|
|
54
|
+
videoTracks: MP4VideoTrack[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface MP4Sample {
|
|
58
|
+
alreadyRead: number;
|
|
59
|
+
chunk_index: number;
|
|
60
|
+
chunk_run_index: number;
|
|
61
|
+
cts: number;
|
|
62
|
+
data: Uint8Array;
|
|
63
|
+
degradation_priority: number;
|
|
64
|
+
depends_on: number;
|
|
65
|
+
description: unknown;
|
|
66
|
+
description_index: number;
|
|
67
|
+
dts: number;
|
|
68
|
+
duration: number;
|
|
69
|
+
has_redundancy: number;
|
|
70
|
+
is_depended_on: number;
|
|
71
|
+
is_leading: number;
|
|
72
|
+
is_sync: boolean;
|
|
73
|
+
number: number;
|
|
74
|
+
offset: number;
|
|
75
|
+
size: number;
|
|
76
|
+
timescale: number;
|
|
77
|
+
track_id: number;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export type MP4ArrayBuffer = ArrayBuffer & { fileStart: number };
|
|
81
|
+
|
|
82
|
+
export class DataStream {
|
|
83
|
+
static BIG_ENDIAN: boolean;
|
|
84
|
+
static LITTLE_ENDIAN: boolean;
|
|
85
|
+
buffer: ArrayBuffer;
|
|
86
|
+
constructor(
|
|
87
|
+
arrayBuffer?: ArrayBuffer,
|
|
88
|
+
byteOffset: number,
|
|
89
|
+
endianness: boolean
|
|
90
|
+
): void;
|
|
91
|
+
// TODO: Complete interface
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface Trak {
|
|
95
|
+
mdia?: {
|
|
96
|
+
minf?: {
|
|
97
|
+
stbl?: {
|
|
98
|
+
stsd?: {
|
|
99
|
+
entries: {
|
|
100
|
+
avcC?: {
|
|
101
|
+
write: (stream: DataStream) => void;
|
|
102
|
+
};
|
|
103
|
+
hvcC?: {
|
|
104
|
+
write: (stream: DataStream) => void;
|
|
105
|
+
};
|
|
106
|
+
}[];
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
// TODO: Complete interface
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface MP4File {
|
|
115
|
+
[x: string]: unknown;
|
|
116
|
+
onMoovStart?: () => void;
|
|
117
|
+
onReady?: (info: MP4Info) => void;
|
|
118
|
+
onError?: (e: string) => void;
|
|
119
|
+
onSamples?: (id: number, user: unknown, samples: MP4Sample[]) => unknown;
|
|
120
|
+
|
|
121
|
+
appendBuffer(data: MP4ArrayBuffer): number;
|
|
122
|
+
start(): void;
|
|
123
|
+
stop(): void;
|
|
124
|
+
flush(): void;
|
|
125
|
+
releaseUsedSamples(trackId: number, sampleNumber: number): void;
|
|
126
|
+
setExtractionOptions(
|
|
127
|
+
trackId: number,
|
|
128
|
+
user?: unknown,
|
|
129
|
+
options?: { nbSamples?: number; rapAlignment?: number }
|
|
130
|
+
): void;
|
|
131
|
+
getTrackById(trackId: number): Trak;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function createFile(): MP4File;
|
|
135
|
+
|
|
136
|
+
export {};
|
|
137
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* General video data for ScrollerVideo state.
|
|
3
|
+
* @typedef {Object} GeneralData
|
|
4
|
+
* @property {string} src - Video source URL.
|
|
5
|
+
* @property {number} videoPercentage - Current video percentage (0-1).
|
|
6
|
+
* @property {number} frameRate - Video frame rate.
|
|
7
|
+
* @property {number} currentTime - Current video time in seconds.
|
|
8
|
+
* @property {number} totalTime - Total video duration in seconds.
|
|
9
|
+
*/
|
|
10
|
+
type GeneralData = {
|
|
11
|
+
src: string;
|
|
12
|
+
videoPercentage: number;
|
|
13
|
+
frameRate: number;
|
|
14
|
+
currentTime: number;
|
|
15
|
+
totalTime: number;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Frame-level data for ScrollerVideo state.
|
|
19
|
+
* @typedef {Object} FramesData
|
|
20
|
+
* @property {string} codec - Video codec string.
|
|
21
|
+
* @property {number} currentFrame - Current frame index.
|
|
22
|
+
* @property {number} totalFrames - Total number of frames.
|
|
23
|
+
*/
|
|
24
|
+
type FramesData = {
|
|
25
|
+
codec: string;
|
|
26
|
+
currentFrame: number;
|
|
27
|
+
totalFrames: number;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* State object for ScrollerVideo component.
|
|
31
|
+
* @typedef {Object} ScrollerVideoState
|
|
32
|
+
* @property {GeneralData} generalData - General video data.
|
|
33
|
+
* @property {boolean} usingWebCodecs - Whether WebCodecs is used.
|
|
34
|
+
* @property {FramesData} framesData - Frame-level data.
|
|
35
|
+
* @property {boolean} isAutoPlaying - Whether video is autoplaying.
|
|
36
|
+
* @property {number} autoplayProgress - Progress of autoplay (0-1).
|
|
37
|
+
*/
|
|
38
|
+
export type ScrollerVideoState = {
|
|
39
|
+
generalData: GeneralData;
|
|
40
|
+
usingWebCodecs: boolean;
|
|
41
|
+
framesData: FramesData;
|
|
42
|
+
willAutoPlay: boolean;
|
|
43
|
+
isAutoPlaying: boolean;
|
|
44
|
+
autoplayProgress: number;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Creates a new ScrollerVideoState object with default values.
|
|
48
|
+
* @returns {ScrollerVideoState} The initialized state object.
|
|
49
|
+
*/
|
|
50
|
+
export declare function createComponentState(): ScrollerVideoState;
|
|
51
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a new ScrollerVideoState object with default values.
|
|
3
|
+
* @returns {ScrollerVideoState} The initialized state object.
|
|
4
|
+
*/
|
|
5
|
+
export function createComponentState() {
|
|
6
|
+
const scrollerVideoState = $state({
|
|
7
|
+
generalData: {
|
|
8
|
+
src: '',
|
|
9
|
+
videoPercentage: 0,
|
|
10
|
+
frameRate: 0,
|
|
11
|
+
currentTime: 0,
|
|
12
|
+
totalTime: 0,
|
|
13
|
+
},
|
|
14
|
+
usingWebCodecs: false,
|
|
15
|
+
framesData: {
|
|
16
|
+
codec: '',
|
|
17
|
+
currentFrame: 0,
|
|
18
|
+
totalFrames: 0,
|
|
19
|
+
},
|
|
20
|
+
willAutoPlay: false,
|
|
21
|
+
isAutoPlaying: false,
|
|
22
|
+
autoplayProgress: 0,
|
|
23
|
+
});
|
|
24
|
+
return scrollerVideoState;
|
|
25
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { ScrollerVideoState } from './state.svelte';
|
|
2
|
+
/**
|
|
3
|
+
* Flattened version of ScrollerVideoState for easier access to all properties.
|
|
4
|
+
* @typedef {Object} FlattenedScrollerVideoState
|
|
5
|
+
* @property {string} src - Video source URL.
|
|
6
|
+
* @property {number} videoPercentage - Current video percentage (0-1).
|
|
7
|
+
* @property {number} frameRate - Video frame rate.
|
|
8
|
+
* @property {number} currentTime - Current video time in seconds.
|
|
9
|
+
* @property {number} totalTime - Total video duration in seconds.
|
|
10
|
+
* @property {boolean} usingWebCodecs - Whether WebCodecs is used.
|
|
11
|
+
* @property {string} codec - Video codec string.
|
|
12
|
+
* @property {number} currentFrame - Current frame index.
|
|
13
|
+
* @property {number} totalFrames - Total number of frames.
|
|
14
|
+
* @property {boolean} isAutoPlaying - Whether video is autoplaying.
|
|
15
|
+
* @property {number} autoplayProgress - Progress of autoplay (0-1).
|
|
16
|
+
*/
|
|
17
|
+
type FlattenedScrollerVideoState = {
|
|
18
|
+
src: string;
|
|
19
|
+
videoPercentage: number;
|
|
20
|
+
frameRate: number;
|
|
21
|
+
currentTime: number;
|
|
22
|
+
totalTime: number;
|
|
23
|
+
usingWebCodecs: boolean;
|
|
24
|
+
codec: string;
|
|
25
|
+
currentFrame: number;
|
|
26
|
+
totalFrames: number;
|
|
27
|
+
isAutoPlaying: boolean;
|
|
28
|
+
autoplayProgress: number;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Returns a debounced version of the given function.
|
|
32
|
+
* @template T
|
|
33
|
+
* @param {T} func - The function to debounce.
|
|
34
|
+
* @param {number} [delay=0] - The debounce delay in milliseconds.
|
|
35
|
+
* @returns {(...args: Parameters<T>) => void} The debounced function.
|
|
36
|
+
*/
|
|
37
|
+
export declare function debounce<T extends (...args: unknown[]) => void>(func: T, delay?: number): (...args: Parameters<T>) => void;
|
|
38
|
+
/**
|
|
39
|
+
* Checks if the current scroll position is at the target position within a threshold.
|
|
40
|
+
* @param {number} targetScrollPosition - The target scroll position in pixels.
|
|
41
|
+
* @param {number} [threshold=1] - The allowed threshold in pixels.
|
|
42
|
+
* @returns {boolean} True if the current scroll position is within the threshold of the target.
|
|
43
|
+
*/
|
|
44
|
+
export declare const isScrollPositionAtTarget: (targetScrollPosition: number, threshold?: number) => boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Constrains a number between a lower and upper bound.
|
|
47
|
+
* @param {number} n - The number to constrain.
|
|
48
|
+
* @param {number} low - The lower bound.
|
|
49
|
+
* @param {number} high - The upper bound.
|
|
50
|
+
* @returns {number} The constrained value.
|
|
51
|
+
*/
|
|
52
|
+
export declare function constrain(n: number, low: number, high: number): number;
|
|
53
|
+
/**
|
|
54
|
+
* Maps a number from one range to another.
|
|
55
|
+
* @param {number} n - The number to map.
|
|
56
|
+
* @param {number} start1 - Lower bound of the value's current range.
|
|
57
|
+
* @param {number} stop1 - Upper bound of the value's current range.
|
|
58
|
+
* @param {number} start2 - Lower bound of the value's target range.
|
|
59
|
+
* @param {number} stop2 - Upper bound of the value's target range.
|
|
60
|
+
* @param {boolean} [withinBounds=true] - Whether to constrain the result within the target range.
|
|
61
|
+
* @returns {number} The mapped value.
|
|
62
|
+
*/
|
|
63
|
+
export declare function map(n: number, start1: number, stop1: number, start2: number, stop2: number, withinBounds?: boolean): number;
|
|
64
|
+
/**
|
|
65
|
+
* Flattens a ScrollerVideoState object into a single-level object for easier access.
|
|
66
|
+
* @param {ScrollerVideoState} obj - The state object to flatten.
|
|
67
|
+
* @returns {FlattenedScrollerVideoState} The flattened state object.
|
|
68
|
+
*/
|
|
69
|
+
export declare function flattenObject(obj: ScrollerVideoState): FlattenedScrollerVideoState;
|
|
70
|
+
export {};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns a debounced version of the given function.
|
|
3
|
+
* @template T
|
|
4
|
+
* @param {T} func - The function to debounce.
|
|
5
|
+
* @param {number} [delay=0] - The debounce delay in milliseconds.
|
|
6
|
+
* @returns {(...args: Parameters<T>) => void} The debounced function.
|
|
7
|
+
*/
|
|
8
|
+
export function debounce(func, delay = 0) {
|
|
9
|
+
let timeoutId;
|
|
10
|
+
return (...args) => {
|
|
11
|
+
clearTimeout(timeoutId);
|
|
12
|
+
timeoutId = setTimeout(() => {
|
|
13
|
+
func(...args);
|
|
14
|
+
}, delay);
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Checks if the current scroll position is at the target position within a threshold.
|
|
19
|
+
* @param {number} targetScrollPosition - The target scroll position in pixels.
|
|
20
|
+
* @param {number} [threshold=1] - The allowed threshold in pixels.
|
|
21
|
+
* @returns {boolean} True if the current scroll position is within the threshold of the target.
|
|
22
|
+
*/
|
|
23
|
+
export const isScrollPositionAtTarget = (targetScrollPosition, threshold = 1) => {
|
|
24
|
+
const currentScrollPosition = window.pageYOffset;
|
|
25
|
+
const difference = Math.abs(currentScrollPosition - targetScrollPosition);
|
|
26
|
+
return difference < threshold;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Constrains a number between a lower and upper bound.
|
|
30
|
+
* @param {number} n - The number to constrain.
|
|
31
|
+
* @param {number} low - The lower bound.
|
|
32
|
+
* @param {number} high - The upper bound.
|
|
33
|
+
* @returns {number} The constrained value.
|
|
34
|
+
*/
|
|
35
|
+
export function constrain(n, low, high) {
|
|
36
|
+
return Math.max(Math.min(n, high), low);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Maps a number from one range to another.
|
|
40
|
+
* @param {number} n - The number to map.
|
|
41
|
+
* @param {number} start1 - Lower bound of the value's current range.
|
|
42
|
+
* @param {number} stop1 - Upper bound of the value's current range.
|
|
43
|
+
* @param {number} start2 - Lower bound of the value's target range.
|
|
44
|
+
* @param {number} stop2 - Upper bound of the value's target range.
|
|
45
|
+
* @param {boolean} [withinBounds=true] - Whether to constrain the result within the target range.
|
|
46
|
+
* @returns {number} The mapped value.
|
|
47
|
+
*/
|
|
48
|
+
export function map(n, start1, stop1, start2, stop2, withinBounds = true) {
|
|
49
|
+
const newval = ((n - start1) / (stop1 - start1)) * (stop2 - start2) + start2;
|
|
50
|
+
if (!withinBounds) {
|
|
51
|
+
return newval;
|
|
52
|
+
}
|
|
53
|
+
if (start2 < stop2) {
|
|
54
|
+
return constrain(newval, start2, stop2);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
return constrain(newval, stop2, start2);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Flattens a ScrollerVideoState object into a single-level object for easier access.
|
|
62
|
+
* @param {ScrollerVideoState} obj - The state object to flatten.
|
|
63
|
+
* @returns {FlattenedScrollerVideoState} The flattened state object.
|
|
64
|
+
*/
|
|
65
|
+
export function flattenObject(obj) {
|
|
66
|
+
const result = {};
|
|
67
|
+
function flatten(current, property) {
|
|
68
|
+
if (Object(current) !== current) {
|
|
69
|
+
result[property] = current;
|
|
70
|
+
}
|
|
71
|
+
else if (Array.isArray(current)) {
|
|
72
|
+
for (let i = 0, l = current.length; i < l; i++) {
|
|
73
|
+
flatten(current[i], property + '[' + i + ']');
|
|
74
|
+
if (l === 0) {
|
|
75
|
+
result[property] = [];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else if (typeof current === 'object') {
|
|
80
|
+
let isEmpty = true;
|
|
81
|
+
for (const p in current) {
|
|
82
|
+
isEmpty = false;
|
|
83
|
+
flatten(current[p], p);
|
|
84
|
+
}
|
|
85
|
+
if (isEmpty && property) {
|
|
86
|
+
result[property] = {};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
flatten(obj, '');
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The main function for decoding video. Deals with the polyfill cases first,
|
|
3
|
+
* then calls our decodeVideo.
|
|
4
|
+
*
|
|
5
|
+
* @param src
|
|
6
|
+
* @param emitFrame
|
|
7
|
+
* @param debug
|
|
8
|
+
* @returns {Promise<never>|Promise<void>}
|
|
9
|
+
*/
|
|
10
|
+
declare const _default: (src: string, emitFrame: (frame: ImageBitmap) => void, debug?: boolean) => Promise<unknown>;
|
|
11
|
+
export default _default;
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import * as MP4Box from 'mp4box';
|
|
2
|
+
/**
|
|
3
|
+
* Taken from https://github.com/w3c/webcodecs/blob/main/samples/mp4-decode/mp4_demuxer.js
|
|
4
|
+
*/
|
|
5
|
+
class Writer {
|
|
6
|
+
data;
|
|
7
|
+
idx;
|
|
8
|
+
size;
|
|
9
|
+
constructor(size) {
|
|
10
|
+
this.data = new Uint8Array(size);
|
|
11
|
+
this.idx = 0;
|
|
12
|
+
this.size = size;
|
|
13
|
+
}
|
|
14
|
+
getData() {
|
|
15
|
+
if (this.idx !== this.size)
|
|
16
|
+
throw new Error('Mismatch between size reserved and sized used');
|
|
17
|
+
return this.data.slice(0, this.idx);
|
|
18
|
+
}
|
|
19
|
+
writeUint8(value) {
|
|
20
|
+
this.data.set([value], this.idx);
|
|
21
|
+
this.idx += 1;
|
|
22
|
+
}
|
|
23
|
+
writeUint16(value) {
|
|
24
|
+
const arr = new Uint16Array(1);
|
|
25
|
+
arr[0] = value;
|
|
26
|
+
const buffer = new Uint8Array(arr.buffer);
|
|
27
|
+
this.data.set([buffer[1], buffer[0]], this.idx);
|
|
28
|
+
this.idx += 2;
|
|
29
|
+
}
|
|
30
|
+
writeUint8Array(value) {
|
|
31
|
+
this.data.set(value, this.idx);
|
|
32
|
+
this.idx += value.length;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const getExtradata = (avccBox) => {
|
|
36
|
+
let i;
|
|
37
|
+
let size = 7;
|
|
38
|
+
for (i = 0; i < avccBox.SPS.length; i += 1) {
|
|
39
|
+
// nalu length is encoded as a uint16.
|
|
40
|
+
size += 2 + avccBox.SPS[i].length;
|
|
41
|
+
}
|
|
42
|
+
for (i = 0; i < avccBox.PPS.length; i += 1) {
|
|
43
|
+
// nalu length is encoded as a uint16.
|
|
44
|
+
size += 2 + avccBox.PPS[i].length;
|
|
45
|
+
}
|
|
46
|
+
const writer = new Writer(size);
|
|
47
|
+
writer.writeUint8(avccBox.configurationVersion);
|
|
48
|
+
writer.writeUint8(avccBox.AVCProfileIndication);
|
|
49
|
+
writer.writeUint8(avccBox.profile_compatibility);
|
|
50
|
+
writer.writeUint8(avccBox.AVCLevelIndication);
|
|
51
|
+
writer.writeUint8(avccBox.lengthSizeMinusOne + (63 << 2));
|
|
52
|
+
writer.writeUint8(avccBox.nb_SPS_nalus + (7 << 5));
|
|
53
|
+
for (i = 0; i < avccBox.SPS.length; i += 1) {
|
|
54
|
+
writer.writeUint16(avccBox.SPS[i].length);
|
|
55
|
+
writer.writeUint8Array(avccBox.SPS[i].nalu);
|
|
56
|
+
}
|
|
57
|
+
writer.writeUint8(avccBox.nb_PPS_nalus);
|
|
58
|
+
for (i = 0; i < avccBox.PPS.length; i += 1) {
|
|
59
|
+
writer.writeUint16(avccBox.PPS[i].length);
|
|
60
|
+
writer.writeUint8Array(avccBox.PPS[i].nalu);
|
|
61
|
+
}
|
|
62
|
+
return writer.getData();
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* decodeVideo takes an url to a mp4 file and converts it into frames.
|
|
66
|
+
*
|
|
67
|
+
* The steps for this are:
|
|
68
|
+
* 1. Determine the codec for this video file and demux it into chunks.
|
|
69
|
+
* 2. Read the chunks with VideoDecoder as fast as possible.
|
|
70
|
+
* 3. Return an array of frames that we can efficiently draw to a canvas.
|
|
71
|
+
*
|
|
72
|
+
* @param src
|
|
73
|
+
* @param VideoDecoder
|
|
74
|
+
* @param EncodedVideoChunk
|
|
75
|
+
* @param emitFrame
|
|
76
|
+
* @param debug
|
|
77
|
+
* @returns {Promise<unknown>}
|
|
78
|
+
*/
|
|
79
|
+
const decodeVideo = (src, emitFrame, { VideoDecoder, EncodedVideoChunk, debug, }) => new Promise((resolve, reject) => {
|
|
80
|
+
if (debug)
|
|
81
|
+
console.info('Decoding video from', src);
|
|
82
|
+
try {
|
|
83
|
+
// Uses mp4box for demuxing
|
|
84
|
+
const mp4boxfile = MP4Box.createFile();
|
|
85
|
+
// Holds the codec value
|
|
86
|
+
let codec = 'N/A';
|
|
87
|
+
// Creates a VideoDecoder instance
|
|
88
|
+
const decoder = new VideoDecoder({
|
|
89
|
+
output: (frame) => {
|
|
90
|
+
createImageBitmap(frame, { resizeQuality: 'high' }).then((bitmap) => {
|
|
91
|
+
emitFrame(bitmap);
|
|
92
|
+
frame.close();
|
|
93
|
+
if (decoder.decodeQueueSize <= 0) {
|
|
94
|
+
// Give it an extra half second to finish everything
|
|
95
|
+
setTimeout(() => {
|
|
96
|
+
if (decoder.state !== 'closed') {
|
|
97
|
+
decoder.close();
|
|
98
|
+
resolve(codec);
|
|
99
|
+
}
|
|
100
|
+
}, 500);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
},
|
|
104
|
+
error: (e) => {
|
|
105
|
+
console.error(e);
|
|
106
|
+
reject(e);
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
mp4boxfile.onReady = (info) => {
|
|
110
|
+
if (info && info.videoTracks && info.videoTracks[0]) {
|
|
111
|
+
[{ codec }] = info.videoTracks;
|
|
112
|
+
if (debug)
|
|
113
|
+
console.info('Video with codec:', codec);
|
|
114
|
+
// Gets the avccbox used for reading extradata
|
|
115
|
+
const moov = mp4boxfile.moov;
|
|
116
|
+
const avccBox = moov?.traks[0].mdia.minf.stbl.stsd.entries[0].avcC;
|
|
117
|
+
if (!avccBox) {
|
|
118
|
+
reject(new Error('Could not find avcC box for extradata.'));
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const extradata = getExtradata(avccBox);
|
|
122
|
+
// configure decoder
|
|
123
|
+
decoder.configure({ codec, description: extradata });
|
|
124
|
+
// Setup mp4box file for breaking it into chunks
|
|
125
|
+
mp4boxfile.setExtractionOptions(info.videoTracks[0].id);
|
|
126
|
+
mp4boxfile.start();
|
|
127
|
+
}
|
|
128
|
+
else
|
|
129
|
+
reject(new Error('URL provided is not a valid mp4 video file.'));
|
|
130
|
+
};
|
|
131
|
+
mp4boxfile.onSamples = (_track_id, _ref, samples) => {
|
|
132
|
+
for (let i = 0; i < samples.length; i += 1) {
|
|
133
|
+
const sample = samples[i];
|
|
134
|
+
const type = sample.is_sync ? 'key' : 'delta';
|
|
135
|
+
const chunk = new EncodedVideoChunk({
|
|
136
|
+
type,
|
|
137
|
+
timestamp: sample.cts,
|
|
138
|
+
duration: sample.duration,
|
|
139
|
+
data: sample.data,
|
|
140
|
+
});
|
|
141
|
+
decoder.decode(chunk);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
// Fetches the file into arraybuffers
|
|
145
|
+
fetch(src).then((res) => {
|
|
146
|
+
if (!res.body)
|
|
147
|
+
throw new Error('Response body is null');
|
|
148
|
+
const reader = res.body.getReader();
|
|
149
|
+
let offset = 0;
|
|
150
|
+
function appendBuffers(result) {
|
|
151
|
+
if (result.done) {
|
|
152
|
+
mp4boxfile.flush();
|
|
153
|
+
return Promise.resolve();
|
|
154
|
+
}
|
|
155
|
+
const buf = result.value.buffer;
|
|
156
|
+
buf.fileStart = offset;
|
|
157
|
+
offset += buf.byteLength;
|
|
158
|
+
mp4boxfile.appendBuffer(buf);
|
|
159
|
+
return reader.read().then(appendBuffers);
|
|
160
|
+
}
|
|
161
|
+
return reader.read().then(appendBuffers);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
catch (e) {
|
|
165
|
+
reject(e);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
/**
|
|
169
|
+
* The main function for decoding video. Deals with the polyfill cases first,
|
|
170
|
+
* then calls our decodeVideo.
|
|
171
|
+
*
|
|
172
|
+
* @param src
|
|
173
|
+
* @param emitFrame
|
|
174
|
+
* @param debug
|
|
175
|
+
* @returns {Promise<never>|Promise<void>}
|
|
176
|
+
*/
|
|
177
|
+
export default (src, emitFrame, debug = false) => {
|
|
178
|
+
// If our browser supports WebCodecs natively
|
|
179
|
+
if (typeof VideoDecoder === 'function' &&
|
|
180
|
+
typeof EncodedVideoChunk === 'function') {
|
|
181
|
+
if (debug)
|
|
182
|
+
console.info('WebCodecs is natively supported, using native version...');
|
|
183
|
+
return decodeVideo(src, emitFrame, {
|
|
184
|
+
VideoDecoder,
|
|
185
|
+
EncodedVideoChunk,
|
|
186
|
+
debug,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
// Otherwise, resolve nothing
|
|
190
|
+
if (debug)
|
|
191
|
+
console.info('WebCodecs is not available in this browser.');
|
|
192
|
+
return Promise.resolve('N/A');
|
|
193
|
+
};
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -39,7 +39,10 @@ With the graphics kit, you'll likely get your text value from an ArchieML doc...
|
|
|
39
39
|
section: Global News # Optional
|
|
40
40
|
sectionUrl: https://www.reuters.com/graphics/ # Optional
|
|
41
41
|
hed: A beautiful page
|
|
42
|
-
authors
|
|
42
|
+
[authors]
|
|
43
|
+
* Samuel Granados
|
|
44
|
+
* Dea Bankova
|
|
45
|
+
[]
|
|
43
46
|
published: 2022-09-12T08:30:00.000Z
|
|
44
47
|
updated:
|
|
45
48
|
```
|
package/dist/index.d.ts
CHANGED
|
@@ -36,10 +36,12 @@ export { default as SiteHeader } from './components/SiteHeader/SiteHeader.svelte
|
|
|
36
36
|
export { default as SiteHeadline } from './components/SiteHeadline/SiteHeadline.svelte';
|
|
37
37
|
export { default as Spinner } from './components/Spinner/Spinner.svelte';
|
|
38
38
|
export { default as ScrollerBase } from './components/ScrollerBase/ScrollerBase.svelte';
|
|
39
|
+
export { default as ScrollerVideo } from './components/ScrollerVideo/ScrollerVideo.svelte';
|
|
40
|
+
export { default as ScrollerVideoForeground } from './components/ScrollerVideo/ScrollerVideoForeground.svelte';
|
|
39
41
|
export { default as SponsorshipAd } from './components/AdSlot/SponsorshipAd.svelte';
|
|
40
42
|
export { default as Table } from './components/Table/Table.svelte';
|
|
41
43
|
export { default as Theme, themes } from './components/Theme/Theme.svelte';
|
|
42
44
|
export { default as ToolsHeader } from './components/ToolsHeader/ToolsHeader.svelte';
|
|
43
45
|
export { default as Video } from './components/Video/Video.svelte';
|
|
44
46
|
export { default as Visible } from './components/Visible/Visible.svelte';
|
|
45
|
-
export type { ContainerWidth, HeadlineSize } from './components/@types/global';
|
|
47
|
+
export type { ContainerWidth, HeadlineSize, ScrollerVideoInstance, } from './components/@types/global';
|
package/dist/index.js
CHANGED
|
@@ -38,6 +38,8 @@ export { default as SiteHeader } from './components/SiteHeader/SiteHeader.svelte
|
|
|
38
38
|
export { default as SiteHeadline } from './components/SiteHeadline/SiteHeadline.svelte';
|
|
39
39
|
export { default as Spinner } from './components/Spinner/Spinner.svelte';
|
|
40
40
|
export { default as ScrollerBase } from './components/ScrollerBase/ScrollerBase.svelte';
|
|
41
|
+
export { default as ScrollerVideo } from './components/ScrollerVideo/ScrollerVideo.svelte';
|
|
42
|
+
export { default as ScrollerVideoForeground } from './components/ScrollerVideo/ScrollerVideoForeground.svelte';
|
|
41
43
|
export { default as SponsorshipAd } from './components/AdSlot/SponsorshipAd.svelte';
|
|
42
44
|
export { default as Table } from './components/Table/Table.svelte';
|
|
43
45
|
export { default as Theme, themes } from './components/Theme/Theme.svelte';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reuters-graphics/graphics-components",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.13",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"private": false,
|
|
6
6
|
"homepage": "https://reuters-graphics.github.io/graphics-components",
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
"eslint-plugin-storybook": "^0.12.0",
|
|
64
64
|
"knip": "^5.50.5",
|
|
65
65
|
"mermaid": "^10.9.3",
|
|
66
|
+
"mp4box": "^0.5.4",
|
|
66
67
|
"postcss": "^8.5.3",
|
|
67
68
|
"prettier": "^3.5.3",
|
|
68
69
|
"prettier-plugin-svelte": "^3.3.3",
|
|
@@ -79,6 +80,7 @@
|
|
|
79
80
|
"svelte": "^5.28.1",
|
|
80
81
|
"svelte-check": "^4.1.6",
|
|
81
82
|
"typescript": "^5.8.3",
|
|
83
|
+
"ua-parser-js": "^2.0.3",
|
|
82
84
|
"vite": "^6.3.2"
|
|
83
85
|
},
|
|
84
86
|
"dependencies": {
|