Add image opacity

This commit is contained in:
Joey Yakimowich-Payne 2025-04-17 21:37:37 -06:00
commit 19531d6430
No known key found for this signature in database
GPG key ID: 6BFE655FA5ABD1E1
3 changed files with 148 additions and 33 deletions

View file

@ -32,18 +32,54 @@ body {
border-style: solid; /* Keep border style */
/* border-color will be set by JS */
/* background-color or background-image will be set by JS */
text-align: center;
padding: 8px;
word-wrap: break-word;
overflow-wrap: break-word;
hyphens: auto;
/* Add transition for background color changes */
text-align: center; /* Keep for potential fallback/spacing */
padding: 8px; /* Keep padding on the main cell */
/* REMOVED text styling from here */
/* word-wrap: break-word; */
/* overflow-wrap: break-word; */
/* hyphens: auto; */
/* font-size: 1.5rem; */
transition: background-color 0.2s ease-in-out, transform 0.1s ease-in-out, box-shadow 0.1s ease-in-out, filter 0.15s ease-in-out, border-color 0.2s ease-in-out, opacity 0.2s ease-in-out;
display: flex;
justify-content: center;
align-items: center;
border-radius: 8px;
font-size: 1.5rem;
position: relative; /* Ensure stacking context for z-index */
overflow: hidden; /* Hide potential image overflow */
}
/* NEW: Pseudo-element for background image layer */
.bingo-cell::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-image: var(--bg-image-url, none);
background-size: cover;
background-position: center center;
background-repeat: no-repeat;
z-index: 1; /* Place ABOVE the text content */
opacity: var(--bg-image-opacity, 1); /* NEW: Use variable for opacity */
/* Inherit border radius if needed */
/* border-radius: inherit; */
}
/* NEW: Styles for the text span inside the cell */
.bingo-cell-text {
display: inline-block; /* Or block, depending on desired wrapping */
word-wrap: break-word;
overflow-wrap: break-word;
hyphens: auto;
font-size: 1.5rem; /* Moved font size here */
color: #000; /* Default text color (can be customized later if needed) */
position: relative; /* Needed for z-index */
z-index: 0; /* Place BELOW the pseudo-element */
/* Remove background to allow parent background to show */
background: transparent;
/* Max width to help with wrapping within padding */
max-width: 100%;
}
/* Add darken filter on hover */

View file

@ -187,6 +187,10 @@
<label for="marked-image-url-input" class="block text-sm font-medium text-gray-700">Marked Cell Background Image URL (Optional):</label>
<input type="url" id="marked-image-url-input" name="marked-image-url-input" placeholder="https://example.com/marker.png" class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-green-500 focus:border-green-500 sm:text-sm">
<p class="mt-1 text-xs text-gray-500">If set, this image is layered over the background color.</p>
<div class="mt-1 w-28 flex flex-col items-center ml-auto">
<label for="marked-image-opacity-slider" class="block text-xs font-medium text-gray-500">Image Opacity: <span id="marked-image-opacity-value">100</span>%</label>
<input type="range" id="marked-image-opacity-slider" name="marked-image-opacity-slider" min="0" max="100" value="100" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700">
</div>
</div>
<div class="mt-2">
<label for="marked-border-color-picker" class="block text-sm font-medium text-gray-700">Marked Cell Border Color:</label>
@ -227,6 +231,10 @@
<div>
<label for="cell-background-image-url-input" class="block text-sm font-medium text-gray-700">Background Image URL (Optional):</label>
<input type="url" id="cell-background-image-url-input" name="cell-background-image-url-input" placeholder="Clear to use background color" class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-green-500 focus:border-green-500 sm:text-sm">
<div class="mt-1 w-28 flex flex-col items-center ml-auto">
<label for="cell-background-image-opacity-slider" class="block text-xs font-medium text-gray-500">Image Opacity: <span id="cell-background-image-opacity-value">100</span>%</label>
<input type="range" id="cell-background-image-opacity-slider" name="cell-background-image-opacity-slider" min="0" max="100" value="100" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700">
</div>
</div>
</div>
</details>

View file

@ -24,11 +24,13 @@ const LS_HEADER_TEXT_COLOR_OPACITY = 'beango_headerTextColorOpacity'; // New
const LS_MARKED_COLOR = 'beango_markedColor';
const LS_MARKED_COLOR_OPACITY = 'beango_markedColorOpacity'; // New (replaces LS_MARKED_OPACITY)
const LS_MARKED_IMAGE_URL = 'beango_markedImageUrl';
const LS_MARKED_IMAGE_OPACITY = 'beango_markedImageOpacity'; // New
const LS_CELL_BORDER_COLOR = 'beango_cellBorderColor';
const LS_CELL_BORDER_OPACITY = 'beango_cellBorderOpacity'; // New
const LS_CELL_BG_COLOR = 'beango_cellBgColor';
const LS_CELL_BG_OPACITY = 'beango_cellBgOpacity'; // New
const LS_CELL_BG_IMAGE_URL = 'beango_cellBgImageUrl';
const LS_CELL_BG_IMAGE_OPACITY = 'beango_cellBgImageOpacity'; // New
const LS_MARKED_BORDER_COLOR = 'beango_markedBorderColor';
const LS_MARKED_BORDER_OPACITY = 'beango_markedBorderOpacity'; // New
const LS_BOARD_BG_COLOR = 'beango_boardBgColor';
@ -50,11 +52,13 @@ const DEFAULT_HEADER_TEXT_COLOR_OPACITY = 100; // New
const DEFAULT_MARKED_COLOR = '#fde047'; // Default yellow
const DEFAULT_MARKED_COLOR_OPACITY = 80; // New (replaces DEFAULT_MARKED_OPACITY)
const DEFAULT_MARKED_IMAGE_URL = '';
const DEFAULT_MARKED_IMAGE_OPACITY = 100; // New
const DEFAULT_CELL_BORDER_COLOR = '#8B4513'; // Default brown
const DEFAULT_CELL_BORDER_OPACITY = 100; // New
const DEFAULT_CELL_BG_COLOR = '#F5F5DC'; // Default beige
const DEFAULT_CELL_BG_OPACITY = 100; // New
const DEFAULT_CELL_BG_IMAGE_URL = ''; // Default no image
const DEFAULT_CELL_BG_IMAGE_OPACITY = 100; // New
const DEFAULT_MARKED_BORDER_COLOR = '#ca8a04'; // Default darker yellow/orange (Tailwind yellow-600)
const DEFAULT_MARKED_BORDER_OPACITY = 100; // New
const DEFAULT_BOARD_BG_COLOR = '#ffffff'; // Default white
@ -258,6 +262,7 @@ function saveMarkedStyleSettings() {
localStorage.setItem(LS_MARKED_COLOR, document.getElementById('marked-color-picker').value);
localStorage.setItem(LS_MARKED_COLOR_OPACITY, document.getElementById('marked-color-opacity-slider').value);
localStorage.setItem(LS_MARKED_IMAGE_URL, document.getElementById('marked-image-url-input').value);
localStorage.setItem(LS_MARKED_IMAGE_OPACITY, document.getElementById('marked-image-opacity-slider').value); // Save image opacity
// Save marked border color and its opacity
localStorage.setItem(LS_MARKED_BORDER_COLOR, document.getElementById('marked-border-color-picker').value);
@ -272,6 +277,7 @@ function applyMarkedCellStyle(cell) {
const color = localStorage.getItem(LS_MARKED_COLOR) || DEFAULT_MARKED_COLOR;
const colorOpacity = parseInt(localStorage.getItem(LS_MARKED_COLOR_OPACITY) || DEFAULT_MARKED_COLOR_OPACITY, 10);
const imageUrl = localStorage.getItem(LS_MARKED_IMAGE_URL) || DEFAULT_MARKED_IMAGE_URL;
const imageOpacity = parseInt(localStorage.getItem(LS_MARKED_IMAGE_OPACITY) || DEFAULT_MARKED_IMAGE_OPACITY, 10);
const borderColor = localStorage.getItem(LS_MARKED_BORDER_COLOR) || DEFAULT_MARKED_BORDER_COLOR;
const borderOpacity = parseInt(localStorage.getItem(LS_MARKED_BORDER_OPACITY) || DEFAULT_MARKED_BORDER_OPACITY, 10);
@ -287,15 +293,16 @@ function applyMarkedCellStyle(cell) {
// Always apply the background color + opacity
cell.style.backgroundColor = hexToRgba(color, colorOpacity);
// Apply background image if URL exists
// Apply background image if URL exists via custom property
if (imageUrl && imageUrl.trim() !== '') {
cell.style.backgroundImage = `url('${imageUrl}')`;
cell.style.backgroundSize = 'cover'; // Or adjust as needed
cell.style.setProperty('--bg-image-url', `url('${imageUrl}')`);
cell.style.setProperty('--bg-image-opacity', imageOpacity / 100);
cell.style.backgroundSize = 'cover';
cell.style.backgroundPosition = 'center center';
cell.style.backgroundRepeat = 'no-repeat';
} else {
// Explicitly clear background image if no URL
cell.style.backgroundImage = 'none';
// Explicitly clear background image custom property
cell.style.removeProperty('--bg-image-url');
}
} else {
@ -398,6 +405,7 @@ function saveCellStyleSettings() {
localStorage.setItem(LS_CELL_BG_COLOR, document.getElementById('cell-background-color-picker').value);
localStorage.setItem(LS_CELL_BG_OPACITY, document.getElementById('cell-background-opacity-slider').value);
localStorage.setItem(LS_CELL_BG_IMAGE_URL, document.getElementById('cell-background-image-url-input').value);
localStorage.setItem(LS_CELL_BG_IMAGE_OPACITY, document.getElementById('cell-background-image-opacity-slider').value); // Save image opacity
}
// --- Apply saved default cell styles to a cell ---
@ -409,19 +417,23 @@ function applyCellStyle(cell) {
const bgColor = localStorage.getItem(LS_CELL_BG_COLOR) || DEFAULT_CELL_BG_COLOR;
const bgOpacity = parseInt(localStorage.getItem(LS_CELL_BG_OPACITY) || DEFAULT_CELL_BG_OPACITY, 10);
const bgImageUrl = localStorage.getItem(LS_CELL_BG_IMAGE_URL) || DEFAULT_CELL_BG_IMAGE_URL;
const bgImageOpacity = parseInt(localStorage.getItem(LS_CELL_BG_IMAGE_OPACITY) || DEFAULT_CELL_BG_IMAGE_OPACITY, 10);
cell.style.borderColor = hexToRgba(borderColor, borderOpacity);
cell.style.opacity = ''; // Reset direct opacity, handled by color
if (bgImageUrl) {
cell.style.backgroundImage = `url('${bgImageUrl}')`;
cell.style.setProperty('--bg-image-url', `url('${bgImageUrl}')`); // NEW: Set CSS custom property
cell.style.setProperty('--bg-image-opacity', bgImageOpacity / 100); // Set image opacity property
cell.style.backgroundSize = 'cover';
cell.style.backgroundPosition = 'center center';
cell.style.backgroundRepeat = 'no-repeat';
// Set fallback color with its specific opacity
cell.style.backgroundColor = hexToRgba(bgColor, bgOpacity);
} else {
cell.style.backgroundImage = ''; // Clear image if URL is removed
// Clear image properties if no URL
cell.style.removeProperty('--bg-image-url'); // NEW: Remove CSS custom property
cell.style.removeProperty('--bg-image-opacity'); // Remove image opacity property
cell.style.backgroundColor = hexToRgba(bgColor, bgOpacity);
}
}
@ -530,17 +542,24 @@ function generateBoard() {
// Shuffle the items specifically for display (use currentItems which has the right size)
displayedItems = shuffleArray([...currentItems]);
const board = document.getElementById('bingo-board');
board.innerHTML = ''; // Clear previous board
const board = document.getElementById("bingo-board");
board.innerHTML = ""; // Clear previous board
board.style.gridTemplateColumns = `repeat(${size}, minmax(0, 1fr))`;
displayedItems.forEach((item, index) => {
const cell = document.createElement('div');
cell.classList.add('bingo-cell', 'cursor-pointer');
cell.textContent = item;
const cell = document.createElement("div");
cell.classList.add("bingo-cell", "cursor-pointer");
// cell.textContent = item; // OLD WAY
cell.dataset.index = index; // Add index for saving marks
cell.onclick = () => selectCell(cell);
applyCellStyle(cell); // Apply default styles upon creation
// NEW: Create span for text content
const textSpan = document.createElement("span");
textSpan.classList.add("bingo-cell-text");
textSpan.textContent = item;
cell.appendChild(textSpan);
board.appendChild(cell);
});
@ -646,8 +665,8 @@ function randomizeBoard() {
displayedItems = shuffleArray([...itemsToShuffle]); // Shuffle the items for display
const board = document.getElementById('bingo-board');
const cells = board.querySelectorAll('.bingo-cell');
const board = document.getElementById("bingo-board");
const cells = board.querySelectorAll(".bingo-cell");
if (cells.length !== displayedItems.length) {
showNotification("Cannot randomize: Board size mismatch. Please generate the board again.", 'error');
@ -655,10 +674,19 @@ function randomizeBoard() {
}
cells.forEach((cell, index) => {
cell.textContent = displayedItems[index];
cell.classList.remove('marked'); // Clear marks visually
// cell.textContent = displayedItems[index]; // OLD WAY
// NEW: Update or create text span
let textSpan = cell.querySelector(".bingo-cell-text");
if (!textSpan) { // Should exist, but create if missing
textSpan = document.createElement("span");
textSpan.classList.add("bingo-cell-text");
cell.innerHTML =
cell.appendChild(textSpan);
}
textSpan.textContent = displayedItems[index];
cell.classList.remove("marked"); // Clear marks visually
applyMarkedCellStyle(cell); // Reset marked styles (which also calls applyCellStyle)
// applyCellStyle(cell); // Ensure default styles are applied (covered by applyMarkedCellStyle reset)
});
clearMarks(false); // Clear visual marks
@ -807,11 +835,15 @@ function loadFromLocalStorage() {
const savedMarkedColor = localStorage.getItem(LS_MARKED_COLOR) || DEFAULT_MARKED_COLOR;
const savedMarkedColorOpacity = localStorage.getItem(LS_MARKED_COLOR_OPACITY) || DEFAULT_MARKED_COLOR_OPACITY;
const savedMarkedImageUrl = localStorage.getItem(LS_MARKED_IMAGE_URL) || DEFAULT_MARKED_IMAGE_URL;
const savedMarkedImageOpacity = localStorage.getItem(LS_MARKED_IMAGE_OPACITY) || DEFAULT_MARKED_IMAGE_OPACITY;
console.log(`LOAD: LS_MARKED_IMAGE_OPACITY = ${localStorage.getItem(LS_MARKED_IMAGE_OPACITY)}, using: ${savedMarkedImageOpacity}`); // DEBUG LOG
const savedCellBorderColor = localStorage.getItem(LS_CELL_BORDER_COLOR) || DEFAULT_CELL_BORDER_COLOR;
const savedCellBorderOpacity = localStorage.getItem(LS_CELL_BORDER_OPACITY) || DEFAULT_CELL_BORDER_OPACITY;
const savedCellBgColor = localStorage.getItem(LS_CELL_BG_COLOR) || DEFAULT_CELL_BG_COLOR;
const savedCellBgOpacity = localStorage.getItem(LS_CELL_BG_OPACITY) || DEFAULT_CELL_BG_OPACITY;
const savedCellBgImageUrl = localStorage.getItem(LS_CELL_BG_IMAGE_URL) || DEFAULT_CELL_BG_IMAGE_URL;
const savedCellBgImageOpacity = localStorage.getItem(LS_CELL_BG_IMAGE_OPACITY) || DEFAULT_CELL_BG_IMAGE_OPACITY;
console.log(`LOAD: LS_CELL_BG_IMAGE_OPACITY = ${localStorage.getItem(LS_CELL_BG_IMAGE_OPACITY)}, using: ${savedCellBgImageOpacity}`); // DEBUG LOG
const savedMarkedBorderColor = localStorage.getItem(LS_MARKED_BORDER_COLOR) || DEFAULT_MARKED_BORDER_COLOR;
const savedMarkedBorderOpacity = localStorage.getItem(LS_MARKED_BORDER_OPACITY) || DEFAULT_MARKED_BORDER_OPACITY;
const savedBoardBgColor = localStorage.getItem(LS_BOARD_BG_COLOR) || DEFAULT_BOARD_BG_COLOR;
@ -885,12 +917,19 @@ function loadFromLocalStorage() {
displayedItems.forEach((item, index) => {
const cell = document.createElement('div');
cell.classList.add('bingo-cell', 'cursor-pointer');
cell.textContent = item;
// cell.textContent = item; // OLD WAY
cell.dataset.index = index; // Ensure index is set for loading marks correctly
cell.onclick = () => selectCell(cell);
applyCellStyle(cell); // Apply default cell style first
// NEW: Create span for text content
const textSpan = document.createElement("span");
textSpan.classList.add("bingo-cell-text");
textSpan.textContent = item;
cell.appendChild(textSpan);
if (markedIndices.includes(index)) {
cell.classList.add('marked'); // Apply saved mark using 'marked' class
cell.classList.add('marked'); // Apply saved mark using \'marked\' class
// Apply dynamic marked styles AFTER adding the class and default styles
applyMarkedCellStyle(cell);
}
@ -947,20 +986,39 @@ function loadFromLocalStorage() {
document.getElementById('marked-color-opacity-slider').value = savedMarkedColorOpacity;
if(document.getElementById('marked-color-opacity-value')) document.getElementById('marked-color-opacity-value').textContent = savedMarkedColorOpacity;
document.getElementById('marked-image-url-input').value = savedMarkedImageUrl;
const markedImageOpacitySlider = document.getElementById('marked-image-opacity-slider');
const markedImageOpacityValueSpan = document.getElementById('marked-image-opacity-value');
if (markedImageOpacitySlider) markedImageOpacitySlider.value = savedMarkedImageOpacity;
if (markedImageOpacityValueSpan) markedImageOpacityValueSpan.textContent = savedMarkedImageOpacity;
document.getElementById('marked-border-color-picker').value = savedMarkedBorderColor;
document.getElementById('marked-border-opacity-slider').value = savedMarkedBorderOpacity;
if(document.getElementById('marked-border-opacity-value')) document.getElementById('marked-border-opacity-value').textContent = savedMarkedBorderOpacity;
const markedBorderOpacitySlider = document.getElementById('marked-border-opacity-slider');
const markedBorderOpacityValueSpan = document.getElementById('marked-border-opacity-value');
if (markedBorderOpacitySlider) markedBorderOpacitySlider.value = savedMarkedBorderOpacity;
if (markedBorderOpacityValueSpan) markedBorderOpacityValueSpan.textContent = savedMarkedBorderOpacity;
refreshMarkedCellStyles(); // Apply the loaded marked styles
// --- End Restore Marked Style Settings ---
// --- Restore Default Cell Style Settings ---
document.getElementById('cell-border-color-picker').value = savedCellBorderColor;
document.getElementById('cell-border-opacity-slider').value = savedCellBorderOpacity;
if(document.getElementById('cell-border-opacity-value')) document.getElementById('cell-border-opacity-value').textContent = savedCellBorderOpacity;
const cellBorderOpacitySlider = document.getElementById('cell-border-opacity-slider');
const cellBorderOpacityValueSpan = document.getElementById('cell-border-opacity-value');
if (cellBorderOpacitySlider) cellBorderOpacitySlider.value = savedCellBorderOpacity;
if (cellBorderOpacityValueSpan) cellBorderOpacityValueSpan.textContent = savedCellBorderOpacity;
document.getElementById('cell-background-color-picker').value = savedCellBgColor;
document.getElementById('cell-background-opacity-slider').value = savedCellBgOpacity;
if(document.getElementById('cell-background-opacity-value')) document.getElementById('cell-background-opacity-value').textContent = savedCellBgOpacity;
const cellBackgroundOpacitySlider = document.getElementById('cell-background-opacity-slider');
const cellBackgroundOpacityValueSpan = document.getElementById('cell-background-opacity-value');
if (cellBackgroundOpacitySlider) cellBackgroundOpacitySlider.value = savedCellBgOpacity;
if (cellBackgroundOpacityValueSpan) cellBackgroundOpacityValueSpan.textContent = savedCellBgOpacity;
document.getElementById('cell-background-image-url-input').value = savedCellBgImageUrl;
const cellBackgroundImageOpacitySlider = document.getElementById('cell-background-image-opacity-slider');
const cellBackgroundImageOpacityValueSpan = document.getElementById('cell-background-image-opacity-value');
if (cellBackgroundImageOpacitySlider) cellBackgroundImageOpacitySlider.value = savedCellBgImageOpacity;
if (cellBackgroundImageOpacityValueSpan) cellBackgroundImageOpacityValueSpan.textContent = savedCellBgImageOpacity;
// Styles are applied during board creation loop above
// --- End Restore Default Cell Style Settings ---
@ -968,7 +1026,10 @@ function loadFromLocalStorage() {
document.getElementById('board-bg-color-picker').value = savedBoardBgColor;
document.getElementById('board-bg-image-url-input').value = savedBoardBgImageUrl;
document.getElementById('board-bg-color-opacity-slider').value = savedBoardBgColorOpacity;
if(document.getElementById('board-bg-color-opacity-value')) document.getElementById('board-bg-color-opacity-value').textContent = savedBoardBgColorOpacity;
const boardBgOpacitySlider = document.getElementById('board-bg-color-opacity-slider');
const boardBgOpacityValueSpan = document.getElementById('board-bg-color-opacity-value');
if (boardBgOpacitySlider) boardBgOpacitySlider.value = savedBoardBgColorOpacity;
if (boardBgOpacityValueSpan) boardBgOpacityValueSpan.textContent = savedBoardBgColorOpacity;
applyBoardBgStyle(); // Apply loaded style
// --- End Restore Board Background Settings ---
@ -1282,6 +1343,11 @@ document.addEventListener('DOMContentLoaded', () => {
saveMarkedStyleSettings();
refreshMarkedCellStyles();
});
document.getElementById('marked-image-opacity-slider').addEventListener('input', (e) => {
if(document.getElementById('marked-image-opacity-value')) document.getElementById('marked-image-opacity-value').textContent = e.target.value;
saveMarkedStyleSettings();
refreshMarkedCellStyles();
});
document.getElementById('marked-border-color-picker').addEventListener('input', () => {
saveMarkedStyleSettings();
refreshMarkedCellStyles();
@ -1315,6 +1381,11 @@ document.addEventListener('DOMContentLoaded', () => {
saveCellStyleSettings();
refreshCellStyles();
});
document.getElementById('cell-background-image-opacity-slider').addEventListener('input', (e) => {
if(document.getElementById('cell-background-image-opacity-value')) document.getElementById('cell-background-image-opacity-value').textContent = e.target.value;
saveCellStyleSettings();
refreshCellStyles();
});
// --- Add Event Listeners for Board Background Controls ---
document.getElementById('board-bg-color-picker').addEventListener('input', () => {