Task 2: Modify ECGAnimation for PMR flatline transition
- Changed exit phase from fade-to-white to clinical flatline transition - After name tracing: hold 300ms, draw flatline rightward 300ms - Fade canvas to black 200ms, then transition background to #1E293B (login screen color) - Total ECG phase timing preserved (~5-6 seconds), exit adds ~1 second - Prepares for LoginScreen component in Task 3
This commit is contained in:
@@ -124,12 +124,15 @@ export function ECGAnimation({ onComplete }: ECGAnimationProps) {
|
|||||||
const SPACE_W = 30 * scale
|
const SPACE_W = 30 * scale
|
||||||
const TRACE_SPEED = 450 * scale
|
const TRACE_SPEED = 450 * scale
|
||||||
const FLAT_GAP = 0.4
|
const FLAT_GAP = 0.4
|
||||||
const HOLD_TIME = 0.75
|
const FLATLINE_HOLD = 0.3
|
||||||
const EXIT_TIME = 0.8
|
const FLATLINE_DRAW = 0.3
|
||||||
|
const FADE_TO_BLACK = 0.2
|
||||||
|
const BG_TRANSITION = 0.2
|
||||||
const baselineY = vh * 0.5
|
const baselineY = vh * 0.5
|
||||||
const ecgMaxDefl = vh * 0.25
|
const ecgMaxDefl = vh * 0.25
|
||||||
const textMaxDefl = vh * 0.08
|
const textMaxDefl = vh * 0.08
|
||||||
const lineColor = '#00ff41'
|
const lineColor = '#00ff41'
|
||||||
|
const loginBgColor = '#1E293B'
|
||||||
|
|
||||||
const beats: Beat[] = [
|
const beats: Beat[] = [
|
||||||
{ startTime: 0.5, widthPx: 60 * scale, amplitude: 0.3, startWX: 0 },
|
{ startTime: 0.5, widthPx: 60 * scale, amplitude: 0.3, startWX: 0 },
|
||||||
@@ -150,8 +153,11 @@ export function ECGAnimation({ onComplete }: ECGAnimationProps) {
|
|||||||
const headScreenRatio = 0.75
|
const headScreenRatio = 0.75
|
||||||
const finalHeadSX = (vw - totalTextW) / 2 + totalTextW
|
const finalHeadSX = (vw - totalTextW) / 2 + totalTextW
|
||||||
const textEndTime = textEndWX / TRACE_SPEED
|
const textEndTime = textEndWX / TRACE_SPEED
|
||||||
const holdEndTime = textEndTime + HOLD_TIME
|
const holdEndTime = textEndTime + FLATLINE_HOLD
|
||||||
const exitEndTime = holdEndTime + EXIT_TIME
|
const flatlineEndTime = holdEndTime + FLATLINE_DRAW
|
||||||
|
const fadeEndTime = flatlineEndTime + FADE_TO_BLACK
|
||||||
|
const bgTransitionEndTime = fadeEndTime + BG_TRANSITION
|
||||||
|
const exitEndTime = bgTransitionEndTime
|
||||||
|
|
||||||
const getYAtX = (wx: number): number => {
|
const getYAtX = (wx: number): number => {
|
||||||
for (let i = 0; i < beats.length; i++) {
|
for (let i = 0; i < beats.length; i++) {
|
||||||
@@ -186,10 +192,12 @@ export function ECGAnimation({ onComplete }: ECGAnimationProps) {
|
|||||||
ctx.clearRect(0, 0, vw, vh)
|
ctx.clearRect(0, 0, vw, vh)
|
||||||
|
|
||||||
let headWX = elapsed * TRACE_SPEED
|
let headWX = elapsed * TRACE_SPEED
|
||||||
const isExitPhase = elapsed >= holdEndTime
|
const isFlatlinePhase = elapsed >= holdEndTime && elapsed < flatlineEndTime
|
||||||
|
const isFadePhase = elapsed >= flatlineEndTime && elapsed < fadeEndTime
|
||||||
|
const isBgTransitionPhase = elapsed >= fadeEndTime
|
||||||
|
|
||||||
if (isExitPhase) {
|
if (elapsed >= textEndTime) {
|
||||||
headWX = textEndWX + (elapsed - holdEndTime) * TRACE_SPEED * 1.5
|
headWX = textEndWX
|
||||||
}
|
}
|
||||||
|
|
||||||
let headSX: number
|
let headSX: number
|
||||||
@@ -199,7 +207,7 @@ export function ECGAnimation({ onComplete }: ECGAnimationProps) {
|
|||||||
if (headWX <= textStartWX) {
|
if (headWX <= textStartWX) {
|
||||||
viewOff = Math.max(0, headWX - headSXEcg)
|
viewOff = Math.max(0, headWX - headSXEcg)
|
||||||
headSX = headWX - viewOff
|
headSX = headWX - viewOff
|
||||||
} else if (headWX >= textEndWX || isExitPhase) {
|
} else if (headWX >= textEndWX || elapsed >= textEndTime) {
|
||||||
viewOff = textEndWX - finalHeadSX
|
viewOff = textEndWX - finalHeadSX
|
||||||
headSX = headWX - viewOff
|
headSX = headWX - viewOff
|
||||||
} else {
|
} else {
|
||||||
@@ -208,19 +216,24 @@ export function ECGAnimation({ onComplete }: ECGAnimationProps) {
|
|||||||
viewOff = headWX - headSX
|
viewOff = headWX - headSX
|
||||||
}
|
}
|
||||||
|
|
||||||
const fadeAlpha = isExitPhase ? Math.max(0, 1 - (elapsed - holdEndTime) / EXIT_TIME) : 1
|
let fadeAlpha = 1
|
||||||
|
if (isFadePhase) {
|
||||||
|
fadeAlpha = Math.max(0, 1 - (elapsed - flatlineEndTime) / FADE_TO_BLACK)
|
||||||
|
} else if (isBgTransitionPhase) {
|
||||||
|
fadeAlpha = 0
|
||||||
|
}
|
||||||
|
|
||||||
if (!bgTransitionedRef.current && elapsed >= textEndTime - 0.3) {
|
if (!bgTransitionedRef.current && elapsed >= flatlineEndTime) {
|
||||||
bgTransitionedRef.current = true
|
bgTransitionedRef.current = true
|
||||||
container.style.transition = 'background 1200ms ease-out'
|
container.style.transition = `background ${BG_TRANSITION * 1000}ms ease-out`
|
||||||
container.style.background = '#FFFFFF'
|
container.style.background = loginBgColor
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.save()
|
ctx.save()
|
||||||
ctx.globalAlpha = fadeAlpha
|
ctx.globalAlpha = fadeAlpha
|
||||||
|
|
||||||
const traceStart = Math.max(0, Math.floor(viewOff))
|
const traceStart = Math.max(0, Math.floor(viewOff))
|
||||||
const traceEnd = Math.min(Math.ceil(isExitPhase ? textEndWX : headWX), Math.ceil(viewOff + vw))
|
const traceEnd = Math.min(Math.ceil(elapsed >= textEndTime ? textEndWX : headWX), Math.ceil(viewOff + vw))
|
||||||
|
|
||||||
if (traceEnd > traceStart) {
|
if (traceEnd > traceStart) {
|
||||||
ctx.beginPath()
|
ctx.beginPath()
|
||||||
@@ -251,15 +264,16 @@ export function ECGAnimation({ onComplete }: ECGAnimationProps) {
|
|||||||
ctx.stroke()
|
ctx.stroke()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isExitPhase) {
|
if (isFlatlinePhase) {
|
||||||
const exitStartSX = textEndWX - viewOff
|
const flatlineProgress = (elapsed - holdEndTime) / FLATLINE_DRAW
|
||||||
const exitEndSX = headWX - viewOff
|
const flatlineEndSX = finalHeadSX + flatlineProgress * (vw - finalHeadSX + 50)
|
||||||
ctx.beginPath()
|
ctx.beginPath()
|
||||||
ctx.strokeStyle = lineColor
|
ctx.strokeStyle = lineColor
|
||||||
ctx.lineWidth = 2
|
ctx.lineWidth = 2
|
||||||
ctx.shadowBlur = 8
|
ctx.shadowBlur = 8
|
||||||
ctx.moveTo(exitStartSX, baselineY)
|
ctx.shadowColor = lineColor
|
||||||
ctx.lineTo(exitEndSX, baselineY)
|
ctx.moveTo(finalHeadSX, baselineY)
|
||||||
|
ctx.lineTo(flatlineEndSX, baselineY)
|
||||||
ctx.stroke()
|
ctx.stroke()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,8 +298,8 @@ export function ECGAnimation({ onComplete }: ECGAnimationProps) {
|
|||||||
|
|
||||||
ctx.globalAlpha = fadeAlpha
|
ctx.globalAlpha = fadeAlpha
|
||||||
ctx.shadowBlur = 0
|
ctx.shadowBlur = 0
|
||||||
if (headSX >= -20 && headSX <= vw + 20) {
|
if (headSX >= -20 && headSX <= vw + 20 && elapsed < flatlineEndTime) {
|
||||||
const headY = isExitPhase ? baselineY : getYAtX(headWX)
|
const headY = isFlatlinePhase ? baselineY : getYAtX(headWX)
|
||||||
const grad = ctx.createRadialGradient(headSX, headY, 0, headSX, headY, 20 * scale)
|
const grad = ctx.createRadialGradient(headSX, headY, 0, headSX, headY, 20 * scale)
|
||||||
grad.addColorStop(0, 'rgba(255,255,255,0.8)')
|
grad.addColorStop(0, 'rgba(255,255,255,0.8)')
|
||||||
grad.addColorStop(0.3, 'rgba(0,255,65,0.6)')
|
grad.addColorStop(0.3, 'rgba(0,255,65,0.6)')
|
||||||
|
|||||||
Reference in New Issue
Block a user