@midscene/visualizer 1.7.7-beta-20260429033400.0 → 1.7.7
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/es/component/player/index.css +27 -2
- package/dist/es/component/player/index.mjs +123 -50
- package/dist/es/component/player/playback-controls.mjs +4 -0
- package/dist/es/component/player/scenes/StepScene.mjs +16 -13
- package/dist/es/component/player/scenes/derive-frame-state.mjs +12 -2
- package/dist/es/component/player/scenes/export-branded-video.mjs +182 -47
- package/dist/es/component/player/scenes/pointer-layout.mjs +36 -0
- package/dist/lib/component/player/index.css +27 -2
- package/dist/lib/component/player/index.js +122 -49
- package/dist/lib/component/player/playback-controls.js +38 -0
- package/dist/lib/component/player/scenes/StepScene.js +15 -12
- package/dist/lib/component/player/scenes/derive-frame-state.js +16 -3
- package/dist/lib/component/player/scenes/export-branded-video.js +188 -47
- package/dist/lib/component/player/scenes/pointer-layout.js +88 -0
- package/dist/types/component/player/index.d.ts +1 -1
- package/dist/types/component/player/playback-controls.d.ts +1 -0
- package/dist/types/component/player/scenes/derive-frame-state.d.ts +2 -0
- package/dist/types/component/player/scenes/export-branded-video.d.ts +16 -0
- package/dist/types/component/player/scenes/pointer-layout.d.ts +20 -0
- package/package.json +5 -5
|
@@ -198,9 +198,11 @@
|
|
|
198
198
|
|
|
199
199
|
.player-container .player-custom-controls {
|
|
200
200
|
flex-direction: row;
|
|
201
|
+
justify-content: center;
|
|
201
202
|
align-items: center;
|
|
202
|
-
gap:
|
|
203
|
-
|
|
203
|
+
gap: 2px;
|
|
204
|
+
min-width: 58px;
|
|
205
|
+
margin-left: 4px;
|
|
204
206
|
display: flex;
|
|
205
207
|
}
|
|
206
208
|
|
|
@@ -208,6 +210,23 @@
|
|
|
208
210
|
color: #fff;
|
|
209
211
|
}
|
|
210
212
|
|
|
213
|
+
.player-container.player-container-empty {
|
|
214
|
+
justify-content: center;
|
|
215
|
+
align-items: center;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.player-container.player-container-empty .player-empty-state {
|
|
219
|
+
color: #6b7280;
|
|
220
|
+
flex-direction: column;
|
|
221
|
+
align-items: center;
|
|
222
|
+
gap: 12px;
|
|
223
|
+
display: flex;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.player-container.player-container-empty .player-empty-state .player-empty-text {
|
|
227
|
+
font-size: 14px;
|
|
228
|
+
}
|
|
229
|
+
|
|
211
230
|
.chapter-tooltip .ant-tooltip-inner {
|
|
212
231
|
-webkit-backdrop-filter: blur(8px);
|
|
213
232
|
backdrop-filter: blur(8px);
|
|
@@ -250,6 +269,12 @@
|
|
|
250
269
|
margin: 4px 0;
|
|
251
270
|
}
|
|
252
271
|
|
|
272
|
+
.player-export-label {
|
|
273
|
+
font-variant-numeric: tabular-nums;
|
|
274
|
+
font-feature-settings: "tnum";
|
|
275
|
+
font-size: 14px;
|
|
276
|
+
}
|
|
277
|
+
|
|
253
278
|
.player-speed-option:hover, .player-settings-item:hover {
|
|
254
279
|
background: rgba(0, 0, 0, .04);
|
|
255
280
|
}
|
|
@@ -3,10 +3,11 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4
4
|
import "./index.css";
|
|
5
5
|
import { CaretRightOutlined, CompressOutlined, DownloadOutlined, ExpandOutlined, ExportOutlined, FontSizeOutlined, PauseOutlined, ThunderboltOutlined } from "@ant-design/icons";
|
|
6
|
-
import { Dropdown,
|
|
6
|
+
import { Button, Dropdown, Progress, Switch, Tooltip, message } from "antd";
|
|
7
7
|
import global_perspective from "../../icons/global-perspective.mjs";
|
|
8
8
|
import player_setting from "../../icons/player-setting.mjs";
|
|
9
9
|
import { useGlobalPreference } from "../../store/store.mjs";
|
|
10
|
+
import { shouldRestartPlaybackFromBeginning } from "./playback-controls.mjs";
|
|
10
11
|
import { triggerReportDownload } from "./report-download.mjs";
|
|
11
12
|
import { StepsTimeline } from "./scenes/StepScene.mjs";
|
|
12
13
|
import { exportBrandedVideo } from "./scenes/export-branded-video.mjs";
|
|
@@ -111,20 +112,38 @@ function Player(props) {
|
|
|
111
112
|
loop: false,
|
|
112
113
|
playbackRate: playbackSpeed
|
|
113
114
|
});
|
|
115
|
+
const effectiveEndFrame = useMemo(()=>{
|
|
116
|
+
if (!frameMap) return 0;
|
|
117
|
+
for(let i = frameMap.scriptFrames.length - 1; i >= 0; i--){
|
|
118
|
+
const sf = frameMap.scriptFrames[i];
|
|
119
|
+
if (sf.taskId) return sf.startFrame + sf.durationInFrames - 1;
|
|
120
|
+
}
|
|
121
|
+
return Math.max(0, frameMap.totalDurationInFrames - 1);
|
|
122
|
+
}, [
|
|
123
|
+
frameMap
|
|
124
|
+
]);
|
|
125
|
+
const handlePlaybackToggle = useCallback(()=>{
|
|
126
|
+
if (player.playing) return void player.pause();
|
|
127
|
+
if (shouldRestartPlaybackFromBeginning(player.currentFrame, effectiveEndFrame)) player.seekTo(0);
|
|
128
|
+
player.play();
|
|
129
|
+
}, [
|
|
130
|
+
effectiveEndFrame,
|
|
131
|
+
player.currentFrame,
|
|
132
|
+
player.pause,
|
|
133
|
+
player.play,
|
|
134
|
+
player.playing,
|
|
135
|
+
player.seekTo
|
|
136
|
+
]);
|
|
114
137
|
useEffect(()=>{
|
|
115
138
|
if (!frameMap || player.playing) return;
|
|
116
|
-
|
|
117
|
-
if (
|
|
118
|
-
for(let i = scriptFrames.length - 1; i >= 0; i--){
|
|
119
|
-
const sf = scriptFrames[i];
|
|
120
|
-
if (sf.taskId) {
|
|
121
|
-
player.seekTo(sf.startFrame + sf.durationInFrames - 1);
|
|
122
|
-
break;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
139
|
+
if (player.currentFrame < frameMap.totalDurationInFrames - 1) return;
|
|
140
|
+
if (effectiveEndFrame > 0) player.seekTo(effectiveEndFrame);
|
|
125
141
|
}, [
|
|
142
|
+
effectiveEndFrame,
|
|
126
143
|
frameMap,
|
|
127
|
-
player.
|
|
144
|
+
player.currentFrame,
|
|
145
|
+
player.playing,
|
|
146
|
+
player.seekTo
|
|
128
147
|
]);
|
|
129
148
|
useEffect(()=>{
|
|
130
149
|
if (!frameMap || !(null == props ? void 0 : props.onTaskChange)) return;
|
|
@@ -178,21 +197,40 @@ function Player(props) {
|
|
|
178
197
|
}, [
|
|
179
198
|
currentFrameState
|
|
180
199
|
]);
|
|
200
|
+
const [isExporting, setIsExporting] = useState(false);
|
|
201
|
+
const [exportProgress, setExportProgress] = useState(0);
|
|
202
|
+
const exportInFlightRef = useRef(false);
|
|
181
203
|
const [controlsVisible, setControlsVisible] = useState(true);
|
|
182
204
|
const hideTimerRef = useRef(null);
|
|
183
205
|
const showControls = useCallback(()=>{
|
|
184
206
|
setControlsVisible(true);
|
|
185
207
|
if (hideTimerRef.current) clearTimeout(hideTimerRef.current);
|
|
208
|
+
if (isExporting) return;
|
|
186
209
|
hideTimerRef.current = setTimeout(()=>setControlsVisible(false), 3000);
|
|
187
|
-
}, [
|
|
210
|
+
}, [
|
|
211
|
+
isExporting
|
|
212
|
+
]);
|
|
188
213
|
const onMouseEnter = useCallback(()=>{
|
|
189
214
|
setControlsVisible(true);
|
|
190
215
|
if (hideTimerRef.current) clearTimeout(hideTimerRef.current);
|
|
191
216
|
}, []);
|
|
192
217
|
const onMouseLeave = useCallback(()=>{
|
|
193
218
|
if (hideTimerRef.current) clearTimeout(hideTimerRef.current);
|
|
219
|
+
if (isExporting) return;
|
|
194
220
|
hideTimerRef.current = setTimeout(()=>setControlsVisible(false), 1000);
|
|
195
|
-
}, [
|
|
221
|
+
}, [
|
|
222
|
+
isExporting
|
|
223
|
+
]);
|
|
224
|
+
useEffect(()=>{
|
|
225
|
+
if (!isExporting) return;
|
|
226
|
+
setControlsVisible(true);
|
|
227
|
+
if (hideTimerRef.current) {
|
|
228
|
+
clearTimeout(hideTimerRef.current);
|
|
229
|
+
hideTimerRef.current = null;
|
|
230
|
+
}
|
|
231
|
+
}, [
|
|
232
|
+
isExporting
|
|
233
|
+
]);
|
|
196
234
|
useEffect(()=>{
|
|
197
235
|
const handleKeyDown = (e)=>{
|
|
198
236
|
var _e_target;
|
|
@@ -200,13 +238,13 @@ function Player(props) {
|
|
|
200
238
|
if ('INPUT' === tag || 'TEXTAREA' === tag || 'SELECT' === tag) return;
|
|
201
239
|
if ('Space' === e.code) {
|
|
202
240
|
e.preventDefault();
|
|
203
|
-
|
|
241
|
+
handlePlaybackToggle();
|
|
204
242
|
}
|
|
205
243
|
};
|
|
206
244
|
document.addEventListener('keydown', handleKeyDown);
|
|
207
245
|
return ()=>document.removeEventListener('keydown', handleKeyDown);
|
|
208
246
|
}, [
|
|
209
|
-
|
|
247
|
+
handlePlaybackToggle
|
|
210
248
|
]);
|
|
211
249
|
const seekBarRef = useRef(null);
|
|
212
250
|
const handleSeekPointerDown = useCallback((e)=>{
|
|
@@ -216,7 +254,7 @@ function Player(props) {
|
|
|
216
254
|
const seek = (clientX)=>{
|
|
217
255
|
const rect = bar.getBoundingClientRect();
|
|
218
256
|
const ratio = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
|
|
219
|
-
player.seekTo(Math.round(ratio *
|
|
257
|
+
player.seekTo(Math.round(ratio * effectiveEndFrame));
|
|
220
258
|
};
|
|
221
259
|
seek(e.clientX);
|
|
222
260
|
const onMove = (ev)=>seek(ev.clientX);
|
|
@@ -228,7 +266,8 @@ function Player(props) {
|
|
|
228
266
|
bar.addEventListener('pointerup', onUp);
|
|
229
267
|
}, [
|
|
230
268
|
frameMap,
|
|
231
|
-
player
|
|
269
|
+
player,
|
|
270
|
+
effectiveEndFrame
|
|
232
271
|
]);
|
|
233
272
|
const [isFullscreen, setIsFullscreen] = useState(false);
|
|
234
273
|
const toggleFullscreen = useCallback(()=>{
|
|
@@ -242,10 +281,9 @@ function Player(props) {
|
|
|
242
281
|
document.addEventListener('fullscreenchange', handler);
|
|
243
282
|
return ()=>document.removeEventListener('fullscreenchange', handler);
|
|
244
283
|
}, []);
|
|
245
|
-
const [isExporting, setIsExporting] = useState(false);
|
|
246
|
-
const [exportProgress, setExportProgress] = useState(0);
|
|
247
284
|
const handleExportVideo = useCallback(()=>_async_to_generator(function*() {
|
|
248
|
-
if (!frameMap ||
|
|
285
|
+
if (!frameMap || exportInFlightRef.current) return;
|
|
286
|
+
exportInFlightRef.current = true;
|
|
249
287
|
setIsExporting(true);
|
|
250
288
|
setExportProgress(0);
|
|
251
289
|
try {
|
|
@@ -255,25 +293,24 @@ function Player(props) {
|
|
|
255
293
|
message.success('Video exported');
|
|
256
294
|
} catch (e) {
|
|
257
295
|
console.error('Export failed:', e);
|
|
258
|
-
message
|
|
296
|
+
const errorMessage = e instanceof Error ? e.message : 'Export failed';
|
|
297
|
+
message.error(errorMessage);
|
|
259
298
|
} finally{
|
|
299
|
+
exportInFlightRef.current = false;
|
|
260
300
|
setIsExporting(false);
|
|
261
301
|
setExportProgress(0);
|
|
262
302
|
}
|
|
263
303
|
})(), [
|
|
264
304
|
autoZoom,
|
|
265
|
-
frameMap
|
|
266
|
-
isExporting
|
|
305
|
+
frameMap
|
|
267
306
|
]);
|
|
268
307
|
const chapterMarkers = useMemo(()=>{
|
|
269
|
-
if (!frameMap) return [];
|
|
270
|
-
const { scriptFrames, totalDurationInFrames } = frameMap;
|
|
271
|
-
if (0 === totalDurationInFrames) return [];
|
|
308
|
+
if (!frameMap || effectiveEndFrame <= 0) return [];
|
|
272
309
|
const markers = [];
|
|
273
|
-
for (const sf of scriptFrames){
|
|
310
|
+
for (const sf of frameMap.scriptFrames){
|
|
274
311
|
if ('img' !== sf.type && 'insight' !== sf.type || 0 === sf.durationInFrames) continue;
|
|
275
312
|
const globalFrame = sf.startFrame;
|
|
276
|
-
const percent = globalFrame /
|
|
313
|
+
const percent = globalFrame / effectiveEndFrame * 100;
|
|
277
314
|
if (percent > 1 && percent < 99) {
|
|
278
315
|
const parts = [
|
|
279
316
|
sf.title,
|
|
@@ -288,16 +325,38 @@ function Player(props) {
|
|
|
288
325
|
}
|
|
289
326
|
return markers;
|
|
290
327
|
}, [
|
|
291
|
-
frameMap
|
|
328
|
+
frameMap,
|
|
329
|
+
effectiveEndFrame
|
|
292
330
|
]);
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
331
|
+
var _props_reportFileContent;
|
|
332
|
+
const reportFileContent = null != (_props_reportFileContent = null == props ? void 0 : props.reportFileContent) ? _props_reportFileContent : null;
|
|
333
|
+
const canDownloadReport = (null == props ? void 0 : props.canDownloadReport) !== false;
|
|
334
|
+
if (!scripts || 0 === scripts.length || !frameMap) {
|
|
335
|
+
if (reportFileContent && canDownloadReport) return /*#__PURE__*/ jsx("div", {
|
|
336
|
+
className: "player-container player-container-empty",
|
|
337
|
+
children: /*#__PURE__*/ jsxs("div", {
|
|
338
|
+
className: "player-empty-state",
|
|
339
|
+
children: [
|
|
340
|
+
/*#__PURE__*/ jsx("span", {
|
|
341
|
+
className: "player-empty-text",
|
|
342
|
+
children: "No replay available"
|
|
343
|
+
}),
|
|
344
|
+
/*#__PURE__*/ jsx(Button, {
|
|
345
|
+
icon: /*#__PURE__*/ jsx(DownloadOutlined, {}),
|
|
346
|
+
onClick: ()=>{
|
|
347
|
+
handleDownloadReport();
|
|
348
|
+
},
|
|
349
|
+
children: "Download report"
|
|
350
|
+
})
|
|
351
|
+
]
|
|
352
|
+
})
|
|
353
|
+
});
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
296
356
|
const compositionWidth = (null == currentFrameState ? void 0 : currentFrameState.imageWidth) || frameMap.imageWidth;
|
|
297
357
|
const compositionHeight = (null == currentFrameState ? void 0 : currentFrameState.imageHeight) || frameMap.imageHeight;
|
|
298
358
|
const isPortraitCanvas = compositionHeight > compositionWidth;
|
|
299
|
-
const
|
|
300
|
-
const seekPercent = totalFrames > 1 ? player.currentFrame / (totalFrames - 1) * 100 : 0;
|
|
359
|
+
const seekPercent = effectiveEndFrame > 0 ? Math.min(100, player.currentFrame / effectiveEndFrame * 100) : 0;
|
|
301
360
|
return /*#__PURE__*/ jsx("div", {
|
|
302
361
|
className: "player-container",
|
|
303
362
|
"data-fit-mode": null == props ? void 0 : props.fitMode,
|
|
@@ -324,7 +383,7 @@ function Player(props) {
|
|
|
324
383
|
height: '100%',
|
|
325
384
|
overflow: 'hidden'
|
|
326
385
|
},
|
|
327
|
-
onClick:
|
|
386
|
+
onClick: handlePlaybackToggle,
|
|
328
387
|
children: (()=>{
|
|
329
388
|
const scale = containerSize.width > 0 && containerSize.height > 0 ? Math.min(containerSize.width / compositionWidth, containerSize.height / compositionHeight) : 1;
|
|
330
389
|
return /*#__PURE__*/ jsx("div", {
|
|
@@ -374,16 +433,16 @@ function Player(props) {
|
|
|
374
433
|
children: [
|
|
375
434
|
/*#__PURE__*/ jsx("div", {
|
|
376
435
|
className: "status-icon",
|
|
377
|
-
onClick:
|
|
436
|
+
onClick: handlePlaybackToggle,
|
|
378
437
|
children: player.playing ? /*#__PURE__*/ jsx(PauseOutlined, {}) : /*#__PURE__*/ jsx(CaretRightOutlined, {})
|
|
379
438
|
}),
|
|
380
439
|
/*#__PURE__*/ jsxs("span", {
|
|
381
440
|
className: "time-display",
|
|
382
441
|
children: [
|
|
383
|
-
formatTime(player.currentFrame, frameMap.fps),
|
|
384
|
-
" /",
|
|
442
|
+
formatTime(Math.min(player.currentFrame, effectiveEndFrame), frameMap.fps),
|
|
385
443
|
' ',
|
|
386
|
-
|
|
444
|
+
"/ ",
|
|
445
|
+
formatTime(effectiveEndFrame + 1, frameMap.fps)
|
|
387
446
|
]
|
|
388
447
|
}),
|
|
389
448
|
/*#__PURE__*/ jsxs("div", {
|
|
@@ -422,7 +481,7 @@ function Player(props) {
|
|
|
422
481
|
/*#__PURE__*/ jsxs("div", {
|
|
423
482
|
className: "player-custom-controls",
|
|
424
483
|
children: [
|
|
425
|
-
|
|
484
|
+
reportFileContent && canDownloadReport ? /*#__PURE__*/ jsx(Tooltip, {
|
|
426
485
|
title: "Download Report",
|
|
427
486
|
children: /*#__PURE__*/ jsx("div", {
|
|
428
487
|
className: "status-icon",
|
|
@@ -458,18 +517,32 @@ function Player(props) {
|
|
|
458
517
|
},
|
|
459
518
|
onClick: isExporting ? void 0 : handleExportVideo,
|
|
460
519
|
children: [
|
|
461
|
-
isExporting ? /*#__PURE__*/ jsx(Spin, {
|
|
462
|
-
size: "small"
|
|
463
|
-
}) : /*#__PURE__*/ jsx(ExportOutlined, {
|
|
464
|
-
style: {
|
|
465
|
-
width: '16px',
|
|
466
|
-
height: '16px'
|
|
467
|
-
}
|
|
468
|
-
}),
|
|
469
520
|
/*#__PURE__*/ jsx("span", {
|
|
470
521
|
style: {
|
|
471
|
-
|
|
522
|
+
width: 16,
|
|
523
|
+
height: 16,
|
|
524
|
+
display: 'inline-flex',
|
|
525
|
+
alignItems: 'center',
|
|
526
|
+
justifyContent: 'center',
|
|
527
|
+
flexShrink: 0
|
|
472
528
|
},
|
|
529
|
+
children: isExporting ? /*#__PURE__*/ jsx(Progress, {
|
|
530
|
+
type: "circle",
|
|
531
|
+
percent: exportProgress,
|
|
532
|
+
size: 16,
|
|
533
|
+
strokeWidth: 14,
|
|
534
|
+
showInfo: false,
|
|
535
|
+
strokeColor: "#1677ff",
|
|
536
|
+
trailColor: "rgba(0, 0, 0, 0.12)"
|
|
537
|
+
}) : /*#__PURE__*/ jsx(ExportOutlined, {
|
|
538
|
+
style: {
|
|
539
|
+
width: '16px',
|
|
540
|
+
height: '16px'
|
|
541
|
+
}
|
|
542
|
+
})
|
|
543
|
+
}),
|
|
544
|
+
/*#__PURE__*/ jsx("span", {
|
|
545
|
+
className: "player-export-label",
|
|
473
546
|
children: isExporting ? `Exporting ${exportProgress}%` : 'Export video'
|
|
474
547
|
})
|
|
475
548
|
]
|
|
@@ -593,7 +666,7 @@ function Player(props) {
|
|
|
593
666
|
style: {
|
|
594
667
|
height: '32px',
|
|
595
668
|
lineHeight: '32px',
|
|
596
|
-
padding: '0 8px 0
|
|
669
|
+
padding: '0 8px 0 28px',
|
|
597
670
|
fontSize: '14px',
|
|
598
671
|
cursor: 'pointer',
|
|
599
672
|
borderRadius: '4px'
|
|
@@ -2,8 +2,9 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useMemo } from "react";
|
|
3
3
|
import { mouseLoading } from "../../../utils/index.mjs";
|
|
4
4
|
import { getCenterHighlightBox } from "../../../utils/highlight-element.mjs";
|
|
5
|
-
import { deriveFrameState } from "./derive-frame-state.mjs";
|
|
5
|
+
import { deriveFrameState, shouldRenderCursor } from "./derive-frame-state.mjs";
|
|
6
6
|
import { getPlaybackViewport } from "./playback-layout.mjs";
|
|
7
|
+
import { resolvePointerLayout, resolveSpinnerLayout } from "./pointer-layout.mjs";
|
|
7
8
|
const POINTER_PHASE = 0.375;
|
|
8
9
|
const CROSSFADE_FRAMES = 10;
|
|
9
10
|
const StepsTimeline = ({ frameMap, autoZoom, frame, width: compWidth, height: compHeight, fps })=>{
|
|
@@ -16,8 +17,8 @@ const StepsTimeline = ({ frameMap, autoZoom, frame, width: compWidth, height: co
|
|
|
16
17
|
fps
|
|
17
18
|
]);
|
|
18
19
|
if (!state.img) return null;
|
|
19
|
-
const { img, imageWidth: imgW, imageHeight: imgH, prevImg, camera, prevCamera, insights, spinning: spinningPointer, spinningElapsedMs, currentPointerImg, title, subTitle, frameInScript, imageChanged, pointerMoved, rawProgress } = state;
|
|
20
|
-
const pT = pointerMoved ? Math.min(rawProgress / POINTER_PHASE, 1) : rawProgress;
|
|
20
|
+
const { img, imageWidth: imgW, imageHeight: imgH, prevImg, camera, prevCamera, insights, spinning: spinningPointer, spinningElapsedMs, currentPointerImg, pointerVisible, title, subTitle, frameInScript, imageChanged, pointerMoved, rawProgress } = state;
|
|
21
|
+
const pT = autoZoom ? pointerMoved ? Math.min(rawProgress / POINTER_PHASE, 1) : rawProgress : 1;
|
|
21
22
|
const cT = pointerMoved ? rawProgress <= POINTER_PHASE ? 0 : Math.min((rawProgress - POINTER_PHASE) / (1 - POINTER_PHASE), 1) : rawProgress;
|
|
22
23
|
const pointerLeft = prevCamera.pointerLeft + (camera.pointerLeft - prevCamera.pointerLeft) * pT;
|
|
23
24
|
const pointerTop = prevCamera.pointerTop + (camera.pointerTop - prevCamera.pointerTop) * pT;
|
|
@@ -32,8 +33,10 @@ const StepsTimeline = ({ frameMap, autoZoom, frame, width: compWidth, height: co
|
|
|
32
33
|
const camH = imgH / imgW * cameraWidth;
|
|
33
34
|
const ptrX = (pointerLeft - cameraLeft) / cameraWidth * contentWidth;
|
|
34
35
|
const ptrY = (pointerTop - cameraTop) / camH * contentHeight;
|
|
35
|
-
const showCursor =
|
|
36
|
-
const
|
|
36
|
+
const showCursor = shouldRenderCursor(pointerVisible, camera, prevCamera, imgW, imgH);
|
|
37
|
+
const pointerLayout = resolvePointerLayout(imgW);
|
|
38
|
+
const spinnerLayout = resolveSpinnerLayout(pointerLayout);
|
|
39
|
+
const resScale = pointerLayout.scale;
|
|
37
40
|
const crossfadeAlpha = imageChanged ? Math.min(frameInScript / CROSSFADE_FRAMES, 1) : 1;
|
|
38
41
|
const spinRotation = spinningPointer ? (Math.sin(spinningElapsedMs / 500 - Math.PI / 2) + 1) / 2 * Math.PI * 2 : 0;
|
|
39
42
|
const renderInsightOverlays = ()=>{
|
|
@@ -143,10 +146,10 @@ const StepsTimeline = ({ frameMap, autoZoom, frame, width: compWidth, height: co
|
|
|
143
146
|
src: mouseLoading,
|
|
144
147
|
style: {
|
|
145
148
|
position: 'absolute',
|
|
146
|
-
left: ptrX -
|
|
147
|
-
top: ptrY -
|
|
148
|
-
width:
|
|
149
|
-
height:
|
|
149
|
+
left: ptrX - spinnerLayout.centerOffset,
|
|
150
|
+
top: ptrY - spinnerLayout.centerOffset,
|
|
151
|
+
width: spinnerLayout.size,
|
|
152
|
+
height: spinnerLayout.size,
|
|
150
153
|
transform: `rotate(${spinRotation}rad)`,
|
|
151
154
|
transformOrigin: 'center center',
|
|
152
155
|
filter: 'drop-shadow(0 1px 2px rgba(0,0,0,0.3))'
|
|
@@ -157,10 +160,10 @@ const StepsTimeline = ({ frameMap, autoZoom, frame, width: compWidth, height: co
|
|
|
157
160
|
src: currentPointerImg,
|
|
158
161
|
style: {
|
|
159
162
|
position: 'absolute',
|
|
160
|
-
left: ptrX -
|
|
161
|
-
top: ptrY -
|
|
162
|
-
width:
|
|
163
|
-
height:
|
|
163
|
+
left: ptrX - pointerLayout.hotspotX,
|
|
164
|
+
top: ptrY - pointerLayout.hotspotY,
|
|
165
|
+
width: pointerLayout.width,
|
|
166
|
+
height: pointerLayout.height,
|
|
164
167
|
filter: 'drop-shadow(0 1px 2px rgba(0,0,0,0.3))'
|
|
165
168
|
}
|
|
166
169
|
})
|
|
@@ -72,6 +72,11 @@ function updateImage(acc, sf, baseW, baseH) {
|
|
|
72
72
|
function checkPointerMoved(prev, cur) {
|
|
73
73
|
return Math.abs(prev.pointerLeft - cur.pointerLeft) > 1 || Math.abs(prev.pointerTop - cur.pointerTop) > 1;
|
|
74
74
|
}
|
|
75
|
+
function shouldRenderCursor(pointerVisible, camera, prevCamera, imageWidth, imageHeight) {
|
|
76
|
+
const centerX = Math.round(imageWidth / 2);
|
|
77
|
+
const centerY = Math.round(imageHeight / 2);
|
|
78
|
+
return pointerVisible || Math.abs(camera.pointerLeft - centerX) > 1 || Math.abs(camera.pointerTop - centerY) > 1 || Math.abs(prevCamera.pointerLeft - centerX) > 1 || Math.abs(prevCamera.pointerTop - centerY) > 1;
|
|
79
|
+
}
|
|
75
80
|
function handleImg(acc, sf, frame, baseW, baseH) {
|
|
76
81
|
updateImage(acc, sf, baseW, baseH);
|
|
77
82
|
const sfEnd = sf.startFrame + sf.durationInFrames;
|
|
@@ -132,6 +137,7 @@ function deriveFrameState(scriptFrames, frame, baseW, baseH, fps) {
|
|
|
132
137
|
spinning: false,
|
|
133
138
|
spinningElapsedMs: 0,
|
|
134
139
|
pointerImg: mousePointer,
|
|
140
|
+
pointerVisible: false,
|
|
135
141
|
title: '',
|
|
136
142
|
subTitle: '',
|
|
137
143
|
taskId: void 0,
|
|
@@ -146,7 +152,10 @@ function deriveFrameState(scriptFrames, frame, baseW, baseH, fps) {
|
|
|
146
152
|
const sfEnd = sf.startFrame + sf.durationInFrames;
|
|
147
153
|
if (0 === sf.durationInFrames) {
|
|
148
154
|
if (sf.startFrame <= frame) {
|
|
149
|
-
if ('pointer' === sf.type && sf.pointerImg)
|
|
155
|
+
if ('pointer' === sf.type && sf.pointerImg) {
|
|
156
|
+
acc.pointerImg = sf.pointerImg;
|
|
157
|
+
acc.pointerVisible = true;
|
|
158
|
+
}
|
|
150
159
|
acc.title = sf.title || acc.title;
|
|
151
160
|
acc.subTitle = sf.subTitle || acc.subTitle;
|
|
152
161
|
var _sf_taskId;
|
|
@@ -206,6 +215,7 @@ function deriveFrameState(scriptFrames, frame, baseW, baseH, fps) {
|
|
|
206
215
|
spinning: acc.spinning,
|
|
207
216
|
spinningElapsedMs: acc.spinningElapsedMs,
|
|
208
217
|
currentPointerImg: acc.pointerImg,
|
|
218
|
+
pointerVisible: acc.pointerVisible,
|
|
209
219
|
title: acc.title,
|
|
210
220
|
subTitle: acc.subTitle,
|
|
211
221
|
taskId: acc.taskId,
|
|
@@ -216,4 +226,4 @@ function deriveFrameState(scriptFrames, frame, baseW, baseH, fps) {
|
|
|
216
226
|
rawProgress: acc.rawProgress
|
|
217
227
|
};
|
|
218
228
|
}
|
|
219
|
-
export { deriveFrameState };
|
|
229
|
+
export { deriveFrameState, shouldRenderCursor };
|