|
|
@@ -12,8 +12,6 @@ type TabPulseOptions = RenderableOptions<TabPulseRenderable> & {
|
|
|
outerComplete?: boolean
|
|
|
glow?: boolean
|
|
|
outerGlow?: boolean
|
|
|
- breathe?: boolean
|
|
|
- outerBreathe?: boolean
|
|
|
color?: RGBA
|
|
|
outerColor?: RGBA
|
|
|
glowColor?: RGBA
|
|
|
@@ -49,8 +47,6 @@ const GLOW_IGNITION_DURATION = 600
|
|
|
const GLOW_IGNITION_PEAK = 1.5
|
|
|
const GLOW_IGNITION_ATTACK = 0.3
|
|
|
const GLOW_FADE_OUT = 200
|
|
|
-const GLOW_BREATHE_PERIOD = 3_600
|
|
|
-const GLOW_BREATHE_RISE = 0.25
|
|
|
const GLOW_TAIL = 12
|
|
|
const GLOW_OPACITY = 0.16
|
|
|
const DEFAULT_FOREGROUND = RGBA.defaultForeground()
|
|
|
@@ -91,18 +87,30 @@ export function blendTabPulseColor(
|
|
|
flash: number,
|
|
|
completion: number,
|
|
|
) {
|
|
|
- output.r = background.r + (glowColor.r - background.r) * glow
|
|
|
- output.g = background.g + (glowColor.g - background.g) * glow
|
|
|
- output.b = background.b + (glowColor.b - background.b) * glow
|
|
|
- output.r += (runningColor.r - output.r) * running
|
|
|
- output.g += (runningColor.g - output.g) * running
|
|
|
- output.b += (runningColor.b - output.b) * running
|
|
|
- output.r += (flashColor.r - output.r) * flash
|
|
|
- output.g += (flashColor.g - output.g) * flash
|
|
|
- output.b += (flashColor.b - output.b) * flash
|
|
|
- output.r += (completionColor.r - output.r) * completion
|
|
|
- output.g += (completionColor.g - output.g) * completion
|
|
|
- output.b += (completionColor.b - output.b) * completion
|
|
|
+ if (glow === 0) {
|
|
|
+ output.r = background.r
|
|
|
+ output.g = background.g
|
|
|
+ output.b = background.b
|
|
|
+ } else {
|
|
|
+ output.r = background.r + (glowColor.r - background.r) * glow
|
|
|
+ output.g = background.g + (glowColor.g - background.g) * glow
|
|
|
+ output.b = background.b + (glowColor.b - background.b) * glow
|
|
|
+ }
|
|
|
+ if (running !== 0) {
|
|
|
+ output.r += (runningColor.r - output.r) * running
|
|
|
+ output.g += (runningColor.g - output.g) * running
|
|
|
+ output.b += (runningColor.b - output.b) * running
|
|
|
+ }
|
|
|
+ if (flash !== 0) {
|
|
|
+ output.r += (flashColor.r - output.r) * flash
|
|
|
+ output.g += (flashColor.g - output.g) * flash
|
|
|
+ output.b += (flashColor.b - output.b) * flash
|
|
|
+ }
|
|
|
+ if (completion !== 0) {
|
|
|
+ output.r += (completionColor.r - output.r) * completion
|
|
|
+ output.g += (completionColor.g - output.g) * completion
|
|
|
+ output.b += (completionColor.b - output.b) * completion
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/** A one-shot animation clock: level() follows shape over duration, scaled by the value passed to start. */
|
|
|
@@ -154,7 +162,6 @@ type PulseStateOptions = {
|
|
|
promptPulse: number
|
|
|
complete: boolean
|
|
|
glow: boolean
|
|
|
- breathe: boolean
|
|
|
}
|
|
|
|
|
|
class PulseState {
|
|
|
@@ -163,9 +170,7 @@ class PulseState {
|
|
|
private promptPulse: number
|
|
|
private complete: boolean
|
|
|
private glow: boolean
|
|
|
- private breathe: boolean
|
|
|
private clock = 0
|
|
|
- private breatheClock = 0
|
|
|
private completionPending = false
|
|
|
private runAttack = new Envelope(RUN_ATTACK, smootherstep)
|
|
|
private runFade = new Envelope(RUN_FADE_OUT, fadeOut)
|
|
|
@@ -181,16 +186,11 @@ class PulseState {
|
|
|
this.promptPulse = options.promptPulse
|
|
|
this.complete = options.complete
|
|
|
this.glow = options.glow
|
|
|
- this.breathe = options.breathe
|
|
|
if (this.enabled && this.active) this.runAttack.start()
|
|
|
}
|
|
|
|
|
|
- private get breathing() {
|
|
|
- return this.enabled && this.glow && this.breathe
|
|
|
- }
|
|
|
-
|
|
|
get live() {
|
|
|
- return this.enabled && (this.active || this.breathing || this.envelopes.some(envelopeActive))
|
|
|
+ return this.enabled && (this.active || this.envelopes.some(envelopeActive))
|
|
|
}
|
|
|
|
|
|
get running() {
|
|
|
@@ -208,11 +208,7 @@ class PulseState {
|
|
|
|
|
|
get glowLevel() {
|
|
|
if (!this.glow) return this.glowOff.level()
|
|
|
- const base = this.ignition.active ? this.ignition.level() : 1
|
|
|
- if (!this.breathing) return base
|
|
|
- return (
|
|
|
- base * (1 + GLOW_BREATHE_RISE * 0.5 * (1 - Math.cos((2 * Math.PI * this.breatheClock) / GLOW_BREATHE_PERIOD)))
|
|
|
- )
|
|
|
+ return this.ignition.active ? this.ignition.level() : 1
|
|
|
}
|
|
|
|
|
|
setEnabled(value: boolean) {
|
|
|
@@ -221,7 +217,6 @@ class PulseState {
|
|
|
if (!value) {
|
|
|
for (const envelope of this.envelopes) envelope.stop()
|
|
|
this.completionPending = false
|
|
|
- this.breatheClock = 0
|
|
|
} else if (this.active) {
|
|
|
this.runAttack.restart()
|
|
|
}
|
|
|
@@ -274,7 +269,6 @@ class PulseState {
|
|
|
if (this.enabled && !value) this.glowOff.start(this.glowLevel)
|
|
|
this.glow = value
|
|
|
this.ignition.stop()
|
|
|
- this.breatheClock = 0
|
|
|
if (this.enabled && value) {
|
|
|
this.glowOff.stop()
|
|
|
this.ignition.start()
|
|
|
@@ -282,17 +276,9 @@ class PulseState {
|
|
|
return true
|
|
|
}
|
|
|
|
|
|
- setBreathe(value: boolean) {
|
|
|
- if (value === this.breathe) return false
|
|
|
- this.breathe = value
|
|
|
- this.breatheClock = 0
|
|
|
- return true
|
|
|
- }
|
|
|
-
|
|
|
advance(deltaTime: number) {
|
|
|
- if (!this.enabled) return
|
|
|
+ if (!this.live) return
|
|
|
if (this.active || this.runFade.active) this.clock += deltaTime
|
|
|
- if (this.breathing) this.breatheClock += deltaTime
|
|
|
for (const envelope of this.envelopes) envelope.advance(deltaTime)
|
|
|
if (!this.completionPending) return
|
|
|
if (this.complete) {
|
|
|
@@ -340,17 +326,13 @@ class TabPulseRenderable extends Renderable {
|
|
|
const enabled = options.enabled ?? true
|
|
|
const active = options.active ?? false
|
|
|
const glow = options.glow ?? false
|
|
|
- const breathe = options.breathe ?? false
|
|
|
const edge = options.edge
|
|
|
const outerActive = options.outerActive ?? active
|
|
|
const outerGlow = options.outerGlow ?? glow
|
|
|
- const outerBreathe = options.outerBreathe ?? breathe
|
|
|
super(ctx, {
|
|
|
...options,
|
|
|
height: 1,
|
|
|
- live:
|
|
|
- enabled &&
|
|
|
- (active || (glow && breathe) || (edge !== undefined && (outerActive || (outerGlow && outerBreathe)))),
|
|
|
+ live: enabled && (active || (edge !== undefined && outerActive)),
|
|
|
})
|
|
|
this._enabled = enabled
|
|
|
this.inner = new PulseState({
|
|
|
@@ -359,7 +341,6 @@ class TabPulseRenderable extends Renderable {
|
|
|
promptPulse: options.promptPulse ?? 0,
|
|
|
complete: options.complete ?? false,
|
|
|
glow,
|
|
|
- breathe,
|
|
|
})
|
|
|
this.outer = new PulseState({
|
|
|
enabled: enabled && edge !== undefined,
|
|
|
@@ -367,7 +348,6 @@ class TabPulseRenderable extends Renderable {
|
|
|
promptPulse: options.outerPromptPulse ?? options.promptPulse ?? 0,
|
|
|
complete: options.outerComplete ?? options.complete ?? false,
|
|
|
glow: outerGlow,
|
|
|
- breathe: outerBreathe,
|
|
|
})
|
|
|
this._color = options.color ?? RGBA.defaultForeground()
|
|
|
this._outerColor = options.outerColor ?? this._color
|
|
|
@@ -439,14 +419,6 @@ class TabPulseRenderable extends Renderable {
|
|
|
if (this.outer.setGlow(value)) this.changed()
|
|
|
}
|
|
|
|
|
|
- set breathe(value: boolean) {
|
|
|
- if (this.inner.setBreathe(value)) this.changed()
|
|
|
- }
|
|
|
-
|
|
|
- set outerBreathe(value: boolean) {
|
|
|
- if (this.outer.setBreathe(value)) this.changed()
|
|
|
- }
|
|
|
-
|
|
|
private changed() {
|
|
|
this.live = this.inner.live || this.outer.live
|
|
|
this.requestRender()
|
|
|
@@ -539,7 +511,7 @@ class TabPulseRenderable extends Renderable {
|
|
|
}
|
|
|
|
|
|
protected override onUpdate(deltaTime: number): void {
|
|
|
- if (!this._enabled) return
|
|
|
+ if (!this.live) return
|
|
|
this.inner.advance(deltaTime)
|
|
|
this.outer.advance(deltaTime)
|
|
|
this.live = this.inner.live || this.outer.live
|
|
|
@@ -568,13 +540,13 @@ class TabPulseRenderable extends Renderable {
|
|
|
this.emitLevel(0)
|
|
|
return
|
|
|
}
|
|
|
- const [front, secondFront] = this.inner.fronts(this.width)
|
|
|
- const [outerFront, outerSecondFront] = this.outer.fronts(this.width)
|
|
|
+ const fronts = running === 0 ? undefined : this.inner.fronts(this.width)
|
|
|
+ const outerFronts = outerRunning === 0 ? undefined : this.outer.fronts(this.width)
|
|
|
if (this._onLevel)
|
|
|
this.emitLevel(
|
|
|
running === 0
|
|
|
? 0
|
|
|
- : Math.max(intensityAt(1, front, RUN_HEAD, RUN_TAIL), intensityAt(1, secondFront, RUN_HEAD, RUN_TAIL)) *
|
|
|
+ : Math.max(intensityAt(1, fronts![0], RUN_HEAD, RUN_TAIL), intensityAt(1, fronts![1], RUN_HEAD, RUN_TAIL)) *
|
|
|
running,
|
|
|
)
|
|
|
const glowTail = Math.min(this._glowTail, Math.max(1, this.width - 2))
|
|
|
@@ -588,8 +560,8 @@ class TabPulseRenderable extends Renderable {
|
|
|
running === 0
|
|
|
? 0
|
|
|
: Math.max(
|
|
|
- intensityAt(index, front, RUN_HEAD, RUN_TAIL),
|
|
|
- intensityAt(index, secondFront, RUN_HEAD, RUN_TAIL),
|
|
|
+ intensityAt(index, fronts![0], RUN_HEAD, RUN_TAIL),
|
|
|
+ intensityAt(index, fronts![1], RUN_HEAD, RUN_TAIL),
|
|
|
) *
|
|
|
0.14 *
|
|
|
running
|
|
|
@@ -597,8 +569,8 @@ class TabPulseRenderable extends Renderable {
|
|
|
outerRunning === 0
|
|
|
? 0
|
|
|
: Math.max(
|
|
|
- intensityAt(index, outerFront, RUN_HEAD, RUN_TAIL),
|
|
|
- intensityAt(index, outerSecondFront, RUN_HEAD, RUN_TAIL),
|
|
|
+ intensityAt(index, outerFronts![0], RUN_HEAD, RUN_TAIL),
|
|
|
+ intensityAt(index, outerFronts![1], RUN_HEAD, RUN_TAIL),
|
|
|
) *
|
|
|
0.14 *
|
|
|
outerRunning
|
|
|
@@ -662,8 +634,6 @@ export function TabPulse(props: {
|
|
|
outerComplete?: boolean
|
|
|
glow?: boolean
|
|
|
outerGlow?: boolean
|
|
|
- breathe?: boolean
|
|
|
- outerBreathe?: boolean
|
|
|
color: RGBA
|
|
|
outerColor?: RGBA
|
|
|
glowColor?: RGBA
|
|
|
@@ -695,8 +665,6 @@ export function TabPulse(props: {
|
|
|
outerComplete={props.outerComplete ?? props.complete ?? false}
|
|
|
glow={props.glow ?? false}
|
|
|
outerGlow={props.outerGlow ?? props.glow ?? false}
|
|
|
- breathe={props.breathe ?? false}
|
|
|
- outerBreathe={props.outerBreathe ?? props.breathe ?? false}
|
|
|
color={props.color}
|
|
|
outerColor={props.outerColor ?? props.color}
|
|
|
glowColor={props.glowColor ?? props.color}
|