From e6ff4756df06fdb96a6f25c7ecbf3186cba2c82e Mon Sep 17 00:00:00 2001 From: Linzm <1015157648@qq.com> Date: Tue, 20 Oct 2026 18:22:14 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=EF=BC=9A=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E7=82=B9=E4=BA=91=E8=BF=87=E6=B8=A1=E6=95=88=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/image-point-cloud/constants.js | 2 +- .../components/image-point-cloud/state.js | 94 ++++++++++--------- 2 files changed, 51 insertions(+), 45 deletions(-) diff --git a/src/views/douyin/components/image-point-cloud/constants.js b/src/views/douyin/components/image-point-cloud/constants.js index 999b8f1..8475425 100644 --- a/src/views/douyin/components/image-point-cloud/constants.js +++ b/src/views/douyin/components/image-point-cloud/constants.js @@ -48,7 +48,7 @@ const TIMING = { SUBJECT_OUTLINE_MS: 1600, /** 灰色人物抠图停留 5s 后再上色 */ SUBJECT_GRAY_MS: 5000, - /** 人物点云最短停留(彩色段);结果 ready 可提前打断 */ + /** 彩色人物点云最短停留后再切效果图点云 */ SUBJECT_HOLD_MS: 1200, RESULT_TRANSITION_MS: 1200, /** 效果图灰色点云 */ diff --git a/src/views/douyin/components/image-point-cloud/state.js b/src/views/douyin/components/image-point-cloud/state.js index d7e90f8..20392dc 100644 --- a/src/views/douyin/components/image-point-cloud/state.js +++ b/src/views/douyin/components/image-point-cloud/state.js @@ -60,6 +60,7 @@ function createStateMachine(hooks) { subjectReady = false subjectFailed = false subjectEntered = false + resultReadyAt = 0 enter(PHASE.LOGO_POINT_CLOUD) } @@ -110,10 +111,11 @@ function createStateMachine(hooks) { if (phase !== PHASE.IMAGE_COLOR_POINT_CLOUD && phase !== PHASE.RESULT_PRELOADING) { return false } - // 等效果图点云采样完成后再进 mask 人物段,避免真机提前切空缓冲黑屏 - if (resultPreloadStatus !== 'ready') return false - // 原图彩色点云需先完整展示 - if (Date.now() - phaseStartedAt < TIMING.IMAGE_COLOR_MS && phase === PHASE.IMAGE_COLOR_POINT_CLOUD) { + // 原图彩色点云需先完整展示,再进 mask 人物段(不因效果图就绪而跳过) + if ( + phase === PHASE.IMAGE_COLOR_POINT_CLOUD && + Date.now() - phaseStartedAt < TIMING.IMAGE_COLOR_MS + ) { return false } subjectEntered = true @@ -123,22 +125,28 @@ function createStateMachine(hooks) { return true } + /** + * 切到效果图点云。人物段未走完时禁止跳转: + * - subjectReady:必须播完 SUBJECT_* 到彩色人物点云后才可切 + * - subjectFailed:可从原图彩/预加载直接切效果图 + */ function tryStartResultTransition() { if (!colorWaitReached) return false if (resultPreloadStatus !== 'ready') return false - if ( - phase !== PHASE.IMAGE_COLOR_POINT_CLOUD && - phase !== PHASE.RESULT_PRELOADING && - phase !== PHASE.SUBJECT_DETECTING && - phase !== PHASE.SUBJECT_OUTLINE && - phase !== PHASE.SUBJECT_CLOUD_GRAY && - phase !== PHASE.SUBJECT_CLOUD_COLORIZE && - phase !== PHASE.SUBJECT_CLOUD_COLOR && - phase !== PHASE.SUBJECT_POINT_CLOUD - ) { + + const inSubjectColor = + phase === PHASE.SUBJECT_CLOUD_COLOR || phase === PHASE.SUBJECT_POINT_CLOUD + const canSkipSubject = + subjectFailed && + (phase === PHASE.IMAGE_COLOR_POINT_CLOUD || phase === PHASE.RESULT_PRELOADING) + + if (inSubjectColor) { + // 彩色人物点云至少停留 SUBJECT_HOLD_MS + if (Date.now() - phaseStartedAt < TIMING.SUBJECT_HOLD_MS) return false + } else if (!canSkipSubject) { return false } - // 效果图阶段清零人物进度,保证 RESULT_* 观感不变 + subjectProgress = 0 outlineProgress = 0 reveal = 0 @@ -215,8 +223,7 @@ function createStateMachine(hooks) { colorProgress = 1 colorWaitReached = true enter(PHASE.IMAGE_COLOR_POINT_CLOUD) - // 不立刻进人物:先展示原图彩色点云 IMAGE_COLOR_MS - tryStartResultTransition() + // 先展示原图彩色点云;人物段由 IMAGE_COLOR_MS 结束后再进 } return getUniforms() } @@ -232,38 +239,40 @@ function createStateMachine(hooks) { subjectProgress = 0 outlineProgress = 0 if (!morphing) assembly = 1 - if ( - resultPreloadStatus === 'loading' && - phase === PHASE.IMAGE_COLOR_POINT_CLOUD && + + const colorHoldDone = + phase === PHASE.RESULT_PRELOADING || Date.now() - phaseStartedAt >= TIMING.IMAGE_COLOR_MS - ) { - // 彩色原图已展示够再标预加载;人物段优先于 RESULT_PRELOADING 文案 - } - // 效果图点云就绪后再进 mask 人物段;识别失败或超时则直接切效果图点云 - if (resultPreloadStatus === 'ready') { + + if (colorHoldDone) { + // 优先进 mask 人物点云,绝不因效果图已就绪而跳过 const entered = tryEnterSubject() - const subjectWaitedOut = - !subjectReady && - !subjectFailed && - resultReadyAt && - Date.now() - resultReadyAt > 12000 - if (!entered && (subjectFailed || subjectWaitedOut)) { + if (!entered) { + // mask 尚未就绪:继续等;超时或失败才直切效果图 + const waitMs = + phase === PHASE.RESULT_PRELOADING + ? 12000 + : TIMING.IMAGE_COLOR_MS + 12000 + const subjectWaitedOut = + !subjectReady && + !subjectFailed && + Date.now() - phaseStartedAt > waitMs if (subjectWaitedOut) subjectFailed = true - tryStartResultTransition() + if (subjectFailed) { + tryStartResultTransition() + } else if ( + phase === PHASE.IMAGE_COLOR_POINT_CLOUD && + (resultPreloadStatus === 'loading' || resultPreloadStatus === 'idle') + ) { + enter(PHASE.RESULT_PRELOADING) + } } - } else if ( - phase === PHASE.IMAGE_COLOR_POINT_CLOUD && - Date.now() - phaseStartedAt >= TIMING.IMAGE_COLOR_MS && - (resultPreloadStatus === 'loading' || resultPreloadStatus === 'idle') - ) { - enter(PHASE.RESULT_PRELOADING) } return getUniforms() } - // —— 人物粗轮廓段(效果图前):彩原图 → 灰抠图 → 约10s → 彩抠图 —— + // —— 人物粗轮廓段:检测 → 轮廓 → 灰抠图 → 上色 → 彩抠图(不可中途跳到效果图)—— if (phase === PHASE.SUBJECT_DETECTING) { - // Buffer 已就绪:短过渡进 OUTLINE,不固定空等满 SUBJECT_DETECT_MS const detectDur = subjectReady ? Math.min(TIMING.SUBJECT_DETECT_MS, 420) : TIMING.SUBJECT_DETECT_MS @@ -275,7 +284,6 @@ function createStateMachine(hooks) { if (!morphing) assembly = 1 subjectProgress = smootherstep(t) * 0.75 outlineProgress = smootherstep(Math.max(0, (t - 0.2) / 0.8)) * 0.5 - if (tryStartResultTransition()) return getUniforms() if (!morphing && t >= 1) { colorProgress = 0 enter(PHASE.SUBJECT_OUTLINE) @@ -292,7 +300,6 @@ function createStateMachine(hooks) { const t = Math.min(1, elapsed / TIMING.SUBJECT_OUTLINE_MS) subjectProgress = 0.7 + smootherstep(t) * 0.3 outlineProgress = 0.45 + smootherstep(t) * 0.55 - if (tryStartResultTransition()) return getUniforms() if (!morphing && t >= 1) { enter(PHASE.SUBJECT_CLOUD_GRAY) } @@ -307,7 +314,6 @@ function createStateMachine(hooks) { if (!morphing) assembly = 1 subjectProgress = 1 outlineProgress = Math.max(0.35, 1 - Math.min(1, elapsed / 2400) * 0.55) - if (tryStartResultTransition()) return getUniforms() if (!morphing && elapsed >= TIMING.SUBJECT_GRAY_MS) { enter(PHASE.SUBJECT_CLOUD_COLORIZE) } @@ -323,7 +329,6 @@ function createStateMachine(hooks) { if (!morphing) assembly = 1 subjectProgress = 1 outlineProgress = 0.35 - if (tryStartResultTransition()) return getUniforms() if (t >= 1) { colorProgress = 1 enter(PHASE.SUBJECT_CLOUD_COLOR) @@ -342,6 +347,7 @@ function createStateMachine(hooks) { if (!morphing) assembly = 1 subjectProgress = 1 outlineProgress = 0.3 + // 人物彩色点云播完(且效果图就绪)后再切效果图点云 tryStartResultTransition() return getUniforms() }