Feature: Scrolling song name in standby mode
This commit is contained in:
parent
31debfe151
commit
767460e08c
@ -130,6 +130,18 @@ export class ProjectionScreen extends SceneFeature {
|
|||||||
this.isVisualizerActive = false;
|
this.isVisualizerActive = false;
|
||||||
this.screens = [];
|
this.screens = [];
|
||||||
this.colorBuffer = new Float32Array(16 * 3);
|
this.colorBuffer = new Float32Array(16 * 3);
|
||||||
|
|
||||||
|
this.standbyState = {
|
||||||
|
active: false,
|
||||||
|
scrollX: 0,
|
||||||
|
text: '',
|
||||||
|
header: '',
|
||||||
|
subtext: ''
|
||||||
|
};
|
||||||
|
this.standbyCanvas = null;
|
||||||
|
this.standbyCtx = null;
|
||||||
|
this.standbyTexture = null;
|
||||||
|
|
||||||
sceneFeatureManager.register(this);
|
sceneFeatureManager.register(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,6 +244,10 @@ export class ProjectionScreen extends SceneFeature {
|
|||||||
|
|
||||||
update(deltaTime) {
|
update(deltaTime) {
|
||||||
updateScreenEffect();
|
updateScreenEffect();
|
||||||
|
|
||||||
|
if (this.standbyState.active) {
|
||||||
|
this.renderStandbyFrame();
|
||||||
|
}
|
||||||
|
|
||||||
// Wobble Logic
|
// Wobble Logic
|
||||||
const time = state.clock.getElapsedTime();
|
const time = state.clock.getElapsedTime();
|
||||||
@ -337,68 +353,129 @@ export class ProjectionScreen extends SceneFeature {
|
|||||||
this.isVisualizerActive = false;
|
this.isVisualizerActive = false;
|
||||||
turnTvScreenOff();
|
turnTvScreenOff();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
activateStandby() {
|
||||||
|
this.isVisualizerActive = false;
|
||||||
|
|
||||||
|
if (!this.standbyCanvas) {
|
||||||
|
this.standbyCanvas = document.createElement('canvas');
|
||||||
|
this.standbyCanvas.width = 1024;
|
||||||
|
this.standbyCanvas.height = 576;
|
||||||
|
this.standbyCtx = this.standbyCanvas.getContext('2d');
|
||||||
|
this.standbyTexture = new THREE.CanvasTexture(this.standbyCanvas);
|
||||||
|
this.standbyTexture.minFilter = THREE.LinearFilter;
|
||||||
|
this.standbyTexture.magFilter = THREE.LinearFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
const songTitle = (state.music && state.music.songTitle) ? state.music.songTitle.toUpperCase() : "";
|
||||||
|
|
||||||
|
this.standbyState = {
|
||||||
|
active: true,
|
||||||
|
scrollX: 0,
|
||||||
|
text: songTitle,
|
||||||
|
header: "PLEASE STAND BY",
|
||||||
|
subtext: "WAITING FOR PARTY START"
|
||||||
|
};
|
||||||
|
|
||||||
|
const material = new THREE.MeshBasicMaterial({
|
||||||
|
map: this.standbyTexture,
|
||||||
|
side: THREE.DoubleSide
|
||||||
|
});
|
||||||
|
|
||||||
|
this.applyMaterialToAll(material);
|
||||||
|
this.setAllVisible(true);
|
||||||
|
state.screenLight.intensity = 0.5;
|
||||||
|
|
||||||
|
// Measure text to set initial scroll
|
||||||
|
this.standbyCtx.font = 'bold 80px monospace';
|
||||||
|
const textWidth = this.standbyCtx.measureText(this.standbyState.text).width;
|
||||||
|
if (textWidth > this.standbyCanvas.width * 0.9) {
|
||||||
|
this.standbyState.scrollX = this.standbyCanvas.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.renderStandbyFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
renderStandbyFrame() {
|
||||||
|
if (!this.standbyState.active || !this.standbyCtx) return;
|
||||||
|
|
||||||
|
const ctx = this.standbyCtx;
|
||||||
|
const width = this.standbyCanvas.width;
|
||||||
|
const height = this.standbyCanvas.height;
|
||||||
|
|
||||||
|
// Draw Background
|
||||||
|
const colors = ['#ffffff', '#ffff00', '#00ffff', '#00ff00', '#ff00ff', '#ff0000', '#0000ff'];
|
||||||
|
const barWidth = width / colors.length;
|
||||||
|
colors.forEach((color, i) => {
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
ctx.fillRect(i * barWidth, 0, barWidth, height);
|
||||||
|
});
|
||||||
|
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
|
||||||
|
ctx.fillRect(0, 0, width, height);
|
||||||
|
|
||||||
|
// Header
|
||||||
|
ctx.fillStyle = '#ffffff';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.textBaseline = 'middle';
|
||||||
|
ctx.font = 'bold 40px monospace';
|
||||||
|
ctx.fillText(this.standbyState.header, width / 2, height * 0.2);
|
||||||
|
|
||||||
|
// Song Title
|
||||||
|
const text = this.standbyState.text;
|
||||||
|
ctx.font = 'bold 80px monospace';
|
||||||
|
const textMetrics = ctx.measureText(text);
|
||||||
|
const textWidth = textMetrics.width;
|
||||||
|
const centerY = height * 0.5;
|
||||||
|
|
||||||
|
if (textWidth > width * 0.9) {
|
||||||
|
// Scroll
|
||||||
|
this.standbyState.scrollX -= 3; // Speed
|
||||||
|
if (this.standbyState.scrollX < -textWidth) {
|
||||||
|
this.standbyState.scrollX = width;
|
||||||
|
}
|
||||||
|
ctx.textAlign = 'left';
|
||||||
|
ctx.fillText(text, this.standbyState.scrollX, centerY);
|
||||||
|
} else {
|
||||||
|
// Center
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.fillText(text, width / 2, centerY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subtext
|
||||||
|
ctx.font = '30px monospace';
|
||||||
|
ctx.fillStyle = '#cccccc';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.fillText(this.standbyState.subtext, width / 2, height * 0.8);
|
||||||
|
|
||||||
|
if (this.standbyTexture) this.standbyTexture.needsUpdate = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Exported Control Functions ---
|
// --- Exported Control Functions ---
|
||||||
|
|
||||||
export function showStandbyScreen() {
|
export function showStandbyScreen() {
|
||||||
if (projectionScreenInstance) projectionScreenInstance.isVisualizerActive = false;
|
if (projectionScreenInstance) {
|
||||||
|
projectionScreenInstance.isVisualizerActive = false;
|
||||||
let texture;
|
projectionScreenInstance.standbyState.active = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (state.posterImage) {
|
if (state.posterImage) {
|
||||||
texture = new THREE.TextureLoader().load(state.posterImage);
|
const texture = new THREE.TextureLoader().load(state.posterImage);
|
||||||
} else {
|
const material = new THREE.MeshBasicMaterial({
|
||||||
const canvas = document.createElement('canvas');
|
map: texture,
|
||||||
canvas.width = 1024;
|
side: THREE.DoubleSide
|
||||||
canvas.height = 576;
|
|
||||||
const ctx = canvas.getContext('2d');
|
|
||||||
|
|
||||||
// Draw Color Bars
|
|
||||||
const colors = ['#ffffff', '#ffff00', '#00ffff', '#00ff00', '#ff00ff', '#ff0000', '#0000ff'];
|
|
||||||
const barWidth = canvas.width / colors.length;
|
|
||||||
colors.forEach((color, i) => {
|
|
||||||
ctx.fillStyle = color;
|
|
||||||
ctx.fillRect(i * barWidth, 0, barWidth, canvas.height);
|
|
||||||
});
|
});
|
||||||
|
if (projectionScreenInstance) {
|
||||||
// Semi-transparent overlay
|
projectionScreenInstance.applyMaterialToAll(material);
|
||||||
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
|
projectionScreenInstance.setAllVisible(true);
|
||||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
||||||
|
|
||||||
// Text settings
|
|
||||||
ctx.fillStyle = '#ffffff';
|
|
||||||
ctx.textAlign = 'center';
|
|
||||||
ctx.textBaseline = 'middle';
|
|
||||||
|
|
||||||
// Song Title
|
|
||||||
let text = "PLEASE STAND BY";
|
|
||||||
if (state.music && state.music.songTitle) {
|
|
||||||
text = state.music.songTitle.toUpperCase();
|
|
||||||
}
|
}
|
||||||
|
state.screenLight.intensity = 0.5;
|
||||||
ctx.font = 'bold 60px monospace';
|
return;
|
||||||
if (ctx.measureText(text).width > canvas.width * 0.9) {
|
|
||||||
ctx.font = 'bold 40px monospace';
|
|
||||||
}
|
|
||||||
ctx.fillText(text, canvas.width / 2, canvas.height / 2 - 20);
|
|
||||||
|
|
||||||
// Subtext
|
|
||||||
ctx.font = '30px monospace';
|
|
||||||
ctx.fillStyle = '#cccccc';
|
|
||||||
ctx.fillText("WAITING FOR PARTY START", canvas.width / 2, canvas.height / 2 + 50);
|
|
||||||
|
|
||||||
texture = new THREE.CanvasTexture(canvas);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const material = new THREE.MeshBasicMaterial({
|
if (projectionScreenInstance) {
|
||||||
map: texture,
|
projectionScreenInstance.activateStandby();
|
||||||
side: THREE.DoubleSide
|
}
|
||||||
});
|
|
||||||
if (projectionScreenInstance) projectionScreenInstance.applyMaterialToAll(material);
|
|
||||||
if (projectionScreenInstance) projectionScreenInstance.setAllVisible(true);
|
|
||||||
|
|
||||||
state.screenLight.intensity = 0.5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function turnTvScreenOn() {
|
export function turnTvScreenOn() {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user