修改:优化点云过渡效果
continuous-integration/drone/push Build was killed
continuous-integration/drone/promote/prod Build is passing

This commit is contained in:
Linzm committed 2026-10-20 18:22:14 +08:00
1 parent 704f6a30e0
commit e6ff4756df
2 files changed
+43 -37

No files matched your search

@@ -48,7 +48,7 @@ const TIMING = {
SUBJECT_OUTLINE_MS: 1600, SUBJECT_OUTLINE_MS: 1600,
/** 灰色人物抠图停留 5s 后再上色 */ /** 灰色人物抠图停留 5s 后再上色 */
SUBJECT_GRAY_MS: 5000, SUBJECT_GRAY_MS: 5000,
/** 人物点云最短停留(彩色段);结果 ready 可提前打断 */ /** 彩色人物点云最短停留后再切效果图点云 */
SUBJECT_HOLD_MS: 1200, SUBJECT_HOLD_MS: 1200,
RESULT_TRANSITION_MS: 1200, RESULT_TRANSITION_MS: 1200,
/** 效果图灰色点云 */ /** 效果图灰色点云 */
@@ -60,6 +60,7 @@ function createStateMachine(hooks) {
subjectReady = false subjectReady = false
subjectFailed = false subjectFailed = false
subjectEntered = false subjectEntered = false
resultReadyAt = 0
enter(PHASE.LOGO_POINT_CLOUD) enter(PHASE.LOGO_POINT_CLOUD)
} }
@@ -110,10 +111,11 @@ function createStateMachine(hooks) {
if (phase !== PHASE.IMAGE_COLOR_POINT_CLOUD && phase !== PHASE.RESULT_PRELOADING) { if (phase !== PHASE.IMAGE_COLOR_POINT_CLOUD && phase !== PHASE.RESULT_PRELOADING) {
return false return false
} }
// 等效果图点云采样完成后再进 mask 人物段,避免真机提前切空缓冲黑屏 // 原图彩色点云需先完整展示,再进 mask 人物段(不因效果图就绪而跳过)
if (resultPreloadStatus !== 'ready') return false if (
// 原图彩色点云需先完整展示 phase === PHASE.IMAGE_COLOR_POINT_CLOUD &&
if (Date.now() - phaseStartedAt < TIMING.IMAGE_COLOR_MS && phase === PHASE.IMAGE_COLOR_POINT_CLOUD) { Date.now() - phaseStartedAt < TIMING.IMAGE_COLOR_MS
) {
return false return false
} }
subjectEntered = true subjectEntered = true
@@ -123,22 +125,28 @@ function createStateMachine(hooks) {
return true return true
} }
/**
* 切到效果图点云。人物段未走完时禁止跳转:
* - subjectReady:必须播完 SUBJECT_* 到彩色人物点云后才可切
* - subjectFailed:可从原图彩/预加载直接切效果图
*/
function tryStartResultTransition() { function tryStartResultTransition() {
if (!colorWaitReached) return false if (!colorWaitReached) return false
if (resultPreloadStatus !== 'ready') return false if (resultPreloadStatus !== 'ready') return false
if (
phase !== PHASE.IMAGE_COLOR_POINT_CLOUD && const inSubjectColor =
phase !== PHASE.RESULT_PRELOADING && phase === PHASE.SUBJECT_CLOUD_COLOR || phase === PHASE.SUBJECT_POINT_CLOUD
phase !== PHASE.SUBJECT_DETECTING && const canSkipSubject =
phase !== PHASE.SUBJECT_OUTLINE && subjectFailed &&
phase !== PHASE.SUBJECT_CLOUD_GRAY && (phase === PHASE.IMAGE_COLOR_POINT_CLOUD || phase === PHASE.RESULT_PRELOADING)
phase !== PHASE.SUBJECT_CLOUD_COLORIZE &&
phase !== PHASE.SUBJECT_CLOUD_COLOR && if (inSubjectColor) {
phase !== PHASE.SUBJECT_POINT_CLOUD // 彩色人物点云至少停留 SUBJECT_HOLD_MS
) { if (Date.now() - phaseStartedAt < TIMING.SUBJECT_HOLD_MS) return false
} else if (!canSkipSubject) {
return false return false
} }
// 效果图阶段清零人物进度,保证 RESULT_* 观感不变
subjectProgress = 0 subjectProgress = 0
outlineProgress = 0 outlineProgress = 0
reveal = 0 reveal = 0
@@ -215,8 +223,7 @@ function createStateMachine(hooks) {
colorProgress = 1 colorProgress = 1
colorWaitReached = true colorWaitReached = true
enter(PHASE.IMAGE_COLOR_POINT_CLOUD) enter(PHASE.IMAGE_COLOR_POINT_CLOUD)
// 不立刻进人物:先展示原图彩色点云 IMAGE_COLOR_MS // 先展示原图彩色点云;人物段由 IMAGE_COLOR_MS 结束后再进
tryStartResultTransition()
} }
return getUniforms() return getUniforms()
} }
@@ -232,38 +239,40 @@ function createStateMachine(hooks) {
subjectProgress = 0 subjectProgress = 0
outlineProgress = 0 outlineProgress = 0
if (!morphing) assembly = 1 if (!morphing) assembly = 1
if (
resultPreloadStatus === 'loading' && const colorHoldDone =
phase === PHASE.IMAGE_COLOR_POINT_CLOUD && phase === PHASE.RESULT_PRELOADING ||
Date.now() - phaseStartedAt >= TIMING.IMAGE_COLOR_MS Date.now() - phaseStartedAt >= TIMING.IMAGE_COLOR_MS
) {
// 彩色原图已展示够再标预加载;人物段优先于 RESULT_PRELOADING 文案 if (colorHoldDone) {
} // 优先进 mask 人物点云,绝不因效果图已就绪而跳过
// 效果图点云就绪后再进 mask 人物段;识别失败或超时则直接切效果图点云
if (resultPreloadStatus === 'ready') {
const entered = tryEnterSubject() const entered = tryEnterSubject()
if (!entered) {
// mask 尚未就绪:继续等;超时或失败才直切效果图
const waitMs =
phase === PHASE.RESULT_PRELOADING
? 12000
: TIMING.IMAGE_COLOR_MS + 12000
const subjectWaitedOut = const subjectWaitedOut =
!subjectReady && !subjectReady &&
!subjectFailed && !subjectFailed &&
resultReadyAt && Date.now() - phaseStartedAt > waitMs
Date.now() - resultReadyAt > 12000
if (!entered && (subjectFailed || subjectWaitedOut)) {
if (subjectWaitedOut) subjectFailed = true if (subjectWaitedOut) subjectFailed = true
if (subjectFailed) {
tryStartResultTransition() tryStartResultTransition()
}
} else if ( } else if (
phase === PHASE.IMAGE_COLOR_POINT_CLOUD && phase === PHASE.IMAGE_COLOR_POINT_CLOUD &&
Date.now() - phaseStartedAt >= TIMING.IMAGE_COLOR_MS &&
(resultPreloadStatus === 'loading' || resultPreloadStatus === 'idle') (resultPreloadStatus === 'loading' || resultPreloadStatus === 'idle')
) { ) {
enter(PHASE.RESULT_PRELOADING) enter(PHASE.RESULT_PRELOADING)
} }
}
}
return getUniforms() return getUniforms()
} }
// —— 人物粗轮廓段(效果图前):彩原图 → 灰抠图 → 约10s → 彩抠图 —— // —— 人物粗轮廓段:检测 → 轮廓 → 灰抠图 → 上色 → 彩抠图(不可中途跳到效果图)——
if (phase === PHASE.SUBJECT_DETECTING) { if (phase === PHASE.SUBJECT_DETECTING) {
// Buffer 已就绪:短过渡进 OUTLINE,不固定空等满 SUBJECT_DETECT_MS
const detectDur = subjectReady const detectDur = subjectReady
? Math.min(TIMING.SUBJECT_DETECT_MS, 420) ? Math.min(TIMING.SUBJECT_DETECT_MS, 420)
: TIMING.SUBJECT_DETECT_MS : TIMING.SUBJECT_DETECT_MS
@@ -275,7 +284,6 @@ function createStateMachine(hooks) {
if (!morphing) assembly = 1 if (!morphing) assembly = 1
subjectProgress = smootherstep(t) * 0.75 subjectProgress = smootherstep(t) * 0.75
outlineProgress = smootherstep(Math.max(0, (t - 0.2) / 0.8)) * 0.5 outlineProgress = smootherstep(Math.max(0, (t - 0.2) / 0.8)) * 0.5
if (tryStartResultTransition()) return getUniforms()
if (!morphing && t >= 1) { if (!morphing && t >= 1) {
colorProgress = 0 colorProgress = 0
enter(PHASE.SUBJECT_OUTLINE) enter(PHASE.SUBJECT_OUTLINE)
@@ -292,7 +300,6 @@ function createStateMachine(hooks) {
const t = Math.min(1, elapsed / TIMING.SUBJECT_OUTLINE_MS) const t = Math.min(1, elapsed / TIMING.SUBJECT_OUTLINE_MS)
subjectProgress = 0.7 + smootherstep(t) * 0.3 subjectProgress = 0.7 + smootherstep(t) * 0.3
outlineProgress = 0.45 + smootherstep(t) * 0.55 outlineProgress = 0.45 + smootherstep(t) * 0.55
if (tryStartResultTransition()) return getUniforms()
if (!morphing && t >= 1) { if (!morphing && t >= 1) {
enter(PHASE.SUBJECT_CLOUD_GRAY) enter(PHASE.SUBJECT_CLOUD_GRAY)
} }
@@ -307,7 +314,6 @@ function createStateMachine(hooks) {
if (!morphing) assembly = 1 if (!morphing) assembly = 1
subjectProgress = 1 subjectProgress = 1
outlineProgress = Math.max(0.35, 1 - Math.min(1, elapsed / 2400) * 0.55) outlineProgress = Math.max(0.35, 1 - Math.min(1, elapsed / 2400) * 0.55)
if (tryStartResultTransition()) return getUniforms()
if (!morphing && elapsed >= TIMING.SUBJECT_GRAY_MS) { if (!morphing && elapsed >= TIMING.SUBJECT_GRAY_MS) {
enter(PHASE.SUBJECT_CLOUD_COLORIZE) enter(PHASE.SUBJECT_CLOUD_COLORIZE)
} }
@@ -323,7 +329,6 @@ function createStateMachine(hooks) {
if (!morphing) assembly = 1 if (!morphing) assembly = 1
subjectProgress = 1 subjectProgress = 1
outlineProgress = 0.35 outlineProgress = 0.35
if (tryStartResultTransition()) return getUniforms()
if (t >= 1) { if (t >= 1) {
colorProgress = 1 colorProgress = 1
enter(PHASE.SUBJECT_CLOUD_COLOR) enter(PHASE.SUBJECT_CLOUD_COLOR)
@@ -342,6 +347,7 @@ function createStateMachine(hooks) {
if (!morphing) assembly = 1 if (!morphing) assembly = 1
subjectProgress = 1 subjectProgress = 1
outlineProgress = 0.3 outlineProgress = 0.3
// 人物彩色点云播完(且效果图就绪)后再切效果图点云
tryStartResultTransition() tryStartResultTransition()
return getUniforms() return getUniforms()
} }