Feature: Custom poster for standby
This commit is contained in:
@@ -136,6 +136,40 @@ export class ConfigUI extends SceneFeature {
|
||||
});
|
||||
statusContainer.appendChild(this.tapeList);
|
||||
|
||||
// Load Poster Button
|
||||
const loadPosterBtn = document.createElement('button');
|
||||
loadPosterBtn.innerText = 'Load Poster';
|
||||
Object.assign(loadPosterBtn.style, {
|
||||
marginTop: '10px',
|
||||
padding: '8px',
|
||||
cursor: 'pointer',
|
||||
backgroundColor: '#555',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
fontSize: '14px'
|
||||
});
|
||||
|
||||
const posterInput = document.createElement('input');
|
||||
posterInput.type = 'file';
|
||||
posterInput.accept = 'image/*';
|
||||
posterInput.style.display = 'none';
|
||||
posterInput.onchange = (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
if (state.posterImage) URL.revokeObjectURL(state.posterImage);
|
||||
state.posterImage = URL.createObjectURL(file);
|
||||
MediaStorage.savePoster(file);
|
||||
showStandbyScreen();
|
||||
}
|
||||
};
|
||||
document.body.appendChild(posterInput);
|
||||
|
||||
loadPosterBtn.onclick = () => {
|
||||
posterInput.click();
|
||||
};
|
||||
statusContainer.appendChild(loadPosterBtn);
|
||||
|
||||
// Load Tapes Button
|
||||
const loadTapesBtn = document.createElement('button');
|
||||
loadTapesBtn.id = 'loadTapeButton';
|
||||
@@ -235,6 +269,11 @@ export class ConfigUI extends SceneFeature {
|
||||
}
|
||||
}
|
||||
|
||||
if (state.posterImage) {
|
||||
URL.revokeObjectURL(state.posterImage);
|
||||
state.posterImage = null;
|
||||
}
|
||||
|
||||
showStandbyScreen();
|
||||
|
||||
const defaults = {
|
||||
@@ -259,6 +298,14 @@ export class ConfigUI extends SceneFeature {
|
||||
document.body.appendChild(container);
|
||||
this.container = container;
|
||||
this.updateStatus();
|
||||
|
||||
// Restore poster
|
||||
MediaStorage.getPoster().then(file => {
|
||||
if (file) {
|
||||
state.posterImage = URL.createObjectURL(file);
|
||||
showStandbyScreen();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updateStatus() {
|
||||
|
||||
@@ -298,47 +298,53 @@ export class ProjectionScreen extends SceneFeature {
|
||||
export function showStandbyScreen() {
|
||||
if (projectionScreenInstance) projectionScreenInstance.isVisualizerActive = false;
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = 1024;
|
||||
canvas.height = 576;
|
||||
const ctx = canvas.getContext('2d');
|
||||
let texture;
|
||||
|
||||
// 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 (state.posterImage) {
|
||||
texture = new THREE.TextureLoader().load(state.posterImage);
|
||||
} else {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = 1024;
|
||||
canvas.height = 576;
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// Semi-transparent overlay
|
||||
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
// 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);
|
||||
});
|
||||
|
||||
// Text settings
|
||||
ctx.fillStyle = '#ffffff';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
// Semi-transparent overlay
|
||||
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// Song Title
|
||||
let text = "PLEASE STAND BY";
|
||||
if (state.music && state.music.songTitle) {
|
||||
text = state.music.songTitle.toUpperCase();
|
||||
// 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();
|
||||
}
|
||||
|
||||
ctx.font = 'bold 60px monospace';
|
||||
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);
|
||||
}
|
||||
|
||||
ctx.font = 'bold 60px monospace';
|
||||
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);
|
||||
|
||||
const texture = new THREE.CanvasTexture(canvas);
|
||||
|
||||
const material = new THREE.MeshBasicMaterial({
|
||||
map: texture,
|
||||
side: THREE.DoubleSide
|
||||
|
||||
Reference in New Issue
Block a user