Skip to main content

SpotLight

Represents a spot light, which emits light from a single point in a cone shape.

TypeNameInterface Description
VariablesattenuationRange: number

Function: Gets the attenuation range of the spot light. This determines how far the light can reach.

VariablesinnerAngle: number

Function: Gets the inner angle of the spot light's cone, in degrees. Within this angle, the light is at its full intensity.

VariablesouterAngle: number

Function: Gets the outer angle of the spot light's cone, in degrees. The light intensity gradually falls off from the inner angle to this outer angle.

Functionsconstructor()

Examples

constructor()

let obj = new APJS.SpotLight();

Use Case

Index-space spotlight sweep over an N-tile grid (§Pattern 7). Decelerating hop lands on a pre-decided winnerIdx; the winning tile then pulses.

const STATE_SWEEPING = 2;
const STATE_PULSING = 3;

@component()
export class SpotlightSweep extends APJS.BasicScriptComponent {
@serializeProperty() spotlight!: APJS.SceneObject;
// Array @serializeProperty fields must use `= []` (not `!:`) to avoid TS1263.
@serializeProperty() tiles: APJS.SceneObject[] = [];
@serializeProperty() tilePositions: APJS.Vector2f[] = [];

private state = 0;
private step = 0;
private totalSteps = 0;
private winnerIdx = 0;
private elapsed = 0;
private nextInterval = 0;
private pulseElapsed = 0;

private readonly INTERVAL_MIN = 0.08;
private readonly INTERVAL_MAX = 0.50;
private readonly LAPS = 3;
private readonly PULSE_DURATION = 0.5;

startReveal(): void {
if (this.tilePositions.length === 0) return;
const n = this.tilePositions.length;
this.winnerIdx = Math.floor(Math.random() * n);
this.totalSteps = n * this.LAPS + this.winnerIdx;
this.step = 0;
this.elapsed = 0;
this.nextInterval = this.INTERVAL_MIN;
this.pulseElapsed = 0;
if (this.spotlight) this.spotlight.setEnabledInHierarchy(true);
this.state = STATE_SWEEPING;
}

onUpdate(dt: number): void {
if (this.state === STATE_SWEEPING) this.sweepTick(dt);
else if (this.state === STATE_PULSING) this.pulseTick(dt);
}

private sweepTick(dt: number): void {
this.elapsed += dt;
if (this.elapsed < this.nextInterval) return;
this.elapsed -= this.nextInterval;
this.step += 1;
if (this.step >= this.totalSteps) { this.land(); return; }
const idx = this.step % this.tilePositions.length;
const st = this.spotlight.getComponent('ScreenTransform') as APJS.ScreenTransform;
if (st) st.anchoredPosition = this.tilePositions[idx];
const t = this.step / this.totalSteps;
this.nextInterval = this.INTERVAL_MIN + (this.INTERVAL_MAX - this.INTERVAL_MIN) * t;
}

private land(): void {
const st = this.spotlight.getComponent('ScreenTransform') as APJS.ScreenTransform;
if (st) st.anchoredPosition = this.tilePositions[this.winnerIdx];
this.pulseElapsed = 0;
this.state = STATE_PULSING;
}

private pulseTick(dt: number): void {
this.pulseElapsed += dt;
const u = Math.min(1, this.pulseElapsed / this.PULSE_DURATION);
// ScreenTransform.scale is Vector2f — native 2D scale (APJS.d.ts:5711).
const tile = this.tiles[this.winnerIdx];
const tileST = tile ? (tile.getComponent('ScreenTransform') as APJS.ScreenTransform) : null;
if (tileST) {
const s = 1 + 0.2 * Math.sin(Math.PI * u);
tileST.scale = { x: s, y: s };
}
if (u >= 1) this.state = 4;
}
}
Copyright © 2026 TikTok. All rights reserved.
About TikTokHelp CenterCareersContactLegalTerms of ServicePrivacy PolicyCookies