Update script handle and description for gallery TV slideshow in enqueue.php

This commit is contained in:
Matt Batchelder
2026-02-21 14:11:56 -05:00
parent 2edbf9732b
commit 2826a3ec4a
6 changed files with 170 additions and 928 deletions

View File

@@ -5424,10 +5424,10 @@ p:last-child { margin-bottom: 0; }
}
/* ═══════════════════════════════════════════════════════════════
INDUSTRY MOCKUP ANIMATIONS (.platform-visual.has-industry)
GALLERY TV SLIDESHOW (.platform-visual.has-gallery-tv)
═══════════════════════════════════════════════════════════════ */
.platform-visual.has-industry {
.platform-visual.has-gallery-tv {
background: none !important;
border: none !important;
border-radius: 0;
@@ -5438,93 +5438,76 @@ p:last-child { margin-bottom: 0; }
font-size: inherit;
}
.ind-stage {
.gtv-stage {
width: 100%;
max-width: 480px;
max-width: 520px;
margin: 0 auto;
}
.ind-stage svg {
/* TV frame (mirrors dashboard-tv pattern) */
.gtv-tv {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
height: auto;
}
.gtv-tv__body {
width: 100%;
background: #111;
border: 4px solid #111;
border-radius: 6px 6px 4px 4px;
outline: 1px solid #000;
padding: 3px;
position: relative;
box-shadow: 0 14px 48px rgba(0,0,0,0.55);
}
.gtv-tv__screen {
width: 100%;
aspect-ratio: 16/9;
background: var(--color-bg);
border-radius: 2px;
overflow: hidden;
position: relative;
}
.gtv-tv__feet {
display: flex;
justify-content: space-between;
width: 60%;
max-width: 300px;
}
.gtv-tv__foot {
width: 12px;
height: 8px;
background: #111;
border: 1px solid #000;
border-radius: 0 0 4px 4px;
}
/* Slides */
.gtv-slides {
position: relative;
width: 100%;
height: 100%;
}
.gtv-slide {
position: absolute;
inset: 0;
opacity: 0;
transition: opacity 0.8s ease;
}
.gtv-slide.is-active {
opacity: 1;
}
.gtv-slide img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
border-radius: var(--radius-md);
box-shadow: 0 8px 32px rgba(0,0,0,.3);
}
/* Subtle glow around active screens */
.ind-stage svg rect[fill="#1c2333"] {
transition: filter .6s ease;
}
/* Animated content transitions */
.ind-menu-price,
.ind-sale-tag,
.ind-footfall-val,
.ind-kpi-val,
.ind-meet-status,
.ind-alert-text,
.ind-weather-icon,
.ind-weather-temp,
.ind-busy-label,
.ind-ld-val,
.ind-ld-alert-text {
transition: opacity .3s ease;
}
.ind-menu-bar,
.ind-rev-bar,
.ind-vendor-bar,
.ind-ld-bar {
transition: height .4s ease, y .4s ease;
}
.ind-product-slot {
transition: stroke-width .3s ease;
}
.ind-sched-slot {
transition: opacity .4s ease, stroke .3s ease;
}
.ind-wf-dot {
transition: opacity .5s ease;
}
.ind-alert-bar {
transition: opacity .4s ease;
}
.ind-meet-dot,
.ind-busy-dot,
.ind-ld-alert {
transition: fill .4s ease;
}
.ind-corp-line,
.ind-ld-line {
transition: d .3s ease;
}
.ind-ld-pie {
transition: d .4s ease;
}
/* ── Reduced-motion overrides for industry animations ── */
/* ── Reduced-motion overrides for gallery TV ── */
@media (prefers-reduced-motion: reduce) {
.ind-menu-bar,
.ind-rev-bar,
.ind-vendor-bar,
.ind-ld-bar,
.ind-product-slot,
.ind-sched-slot,
.ind-wf-dot,
.ind-alert-bar,
.ind-corp-line,
.ind-ld-line,
.ind-ld-pie,
.ind-meet-dot,
.ind-busy-dot,
.ind-ld-alert {
.gtv-slide {
transition: none !important;
}
}

View File

@@ -1,380 +1,56 @@
/**
* Industry Mockup Animator
* Animates SVG environment mockups for each industry on the solutions page.
* Each mockup shows devices in real-world use with animated screen content.
* Gallery TV Slideshow
* Cycles through images inside gallery-TV blocks.
* Pauses off-screen via IntersectionObserver for performance.
* Respects prefers-reduced-motion.
*/
(function () {
'use strict';
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
var SPEED = 0.0018;
/* ── Utility ────────────────────────────────────────────── */
function wave(t, off) {
return Math.max(0, Math.min(1,
0.55 + Math.sin(t + off) * 0.25 + Math.sin(t * 1.8 + off * 1.3) * 0.15
));
}
function lerp(a, b, t) { return a + (b - a) * t; }
function isDark() {
return document.documentElement.getAttribute('data-theme') === 'dark';
}
/* ── Hospitality ────────────────────────────────────────── */
function initHospitality(svg, st) {
st.menuPrices = svg.querySelectorAll('.ind-menu-price');
st.menuBars = svg.querySelectorAll('.ind-menu-bar');
st.promoBanner = svg.querySelector('.ind-promo-text');
st.promoPhase = 0;
st.promos = ['HAPPY HOUR 5-7PM', 'NEW: SUMMER MENU', '20% OFF DESSERTS', 'LIVE MUSIC FRI'];
}
function tickHospitality(st) {
// Animate menu prices (shimmer)
for (var i = 0; i < st.menuPrices.length; i++) {
var v = wave(st.phase, i * 1.4);
var price = (5 + Math.round(v * 20)).toFixed(2);
st.menuPrices[i].textContent = '$' + price;
}
// Animate popularity bars
for (var j = 0; j < st.menuBars.length; j++) {
var bv = wave(st.phase * 0.6, j * 1.8);
st.menuBars[j].setAttribute('width', Math.round(bv * 50 + 10));
}
// Cycle promo banner
st.promoPhase += SPEED * 0.3;
if (st.promoPhase > 1) {
st.promoPhase = 0;
if (st.promoBanner) {
var idx = Math.floor(Math.random() * st.promos.length);
st.promoBanner.textContent = st.promos[idx];
}
}
}
/* ── Retail ─────────────────────────────────────────────── */
function initRetail(svg, st) {
st.saleTags = svg.querySelectorAll('.ind-sale-tag');
st.footfall = svg.querySelector('.ind-footfall-val');
st.revBars = svg.querySelectorAll('.ind-rev-bar');
st.productSlots = svg.querySelectorAll('.ind-product-slot');
st.productPhase = 0;
}
function tickRetail(st) {
// Sale tags pulse opacity
for (var i = 0; i < st.saleTags.length; i++) {
var op = 0.5 + wave(st.phase * 1.2, i * 2.0) * 0.5;
st.saleTags[i].setAttribute('opacity', op.toFixed(2));
}
// Footfall counter
if (st.footfall) {
var count = Math.round(wave(st.phase * 0.3, 0) * 450 + 50);
st.footfall.textContent = count;
}
// Revenue bars
for (var j = 0; j < st.revBars.length; j++) {
var rv = wave(st.phase * 0.5, j * 1.5);
var h = Math.round(rv * 35 + 5);
st.revBars[j].setAttribute('height', h);
st.revBars[j].setAttribute('y', 40 - h);
}
// Product slots cycle highlight
st.productPhase += SPEED * 0.15;
if (st.productPhase > 1) {
st.productPhase = 0;
var active = Math.floor(Math.random() * st.productSlots.length);
for (var k = 0; k < st.productSlots.length; k++) {
st.productSlots[k].setAttribute('stroke-width', k === active ? '2' : '0');
}
}
}
/* ── Corporate ──────────────────────────────────────────── */
function initCorporate(svg, st) {
st.kpiVals = svg.querySelectorAll('.ind-kpi-val');
st.kpiArrows = svg.querySelectorAll('.ind-kpi-arrow');
st.meetStatus = svg.querySelector('.ind-meet-status');
st.meetDot = svg.querySelector('.ind-meet-dot');
st.meetPhase = 0;
st.statuses = ['Available', 'In Meeting', 'Reserved 2:30', 'Available'];
st.statusIdx = 0;
st.linePath = svg.querySelector('.ind-corp-line');
st.lineW = 140;
st.linePts = 6;
}
function tickCorporate(st) {
// KPI counters
for (var i = 0; i < st.kpiVals.length; i++) {
var kv = wave(st.phase * 0.6, i * 2.2);
var vals = [
Math.round(kv * 98) + '%',
Math.round(kv * 340 + 60),
Math.round(kv * 50 + 10) + 'ms'
];
if (vals[i]) st.kpiVals[i].textContent = vals[i];
}
// KPI arrows
for (var j = 0; j < st.kpiArrows.length; j++) {
var up = wave(st.phase * 0.6, j * 2.2) > 0.5;
st.kpiArrows[j].setAttribute('transform',
up ? 'rotate(0)' : 'rotate(180)');
st.kpiArrows[j].setAttribute('fill', up ? '#4CAF50' : '#ef4444');
}
// Meeting room status cycle
st.meetPhase += SPEED * 0.15;
if (st.meetPhase > 1) {
st.meetPhase = 0;
st.statusIdx = (st.statusIdx + 1) % st.statuses.length;
if (st.meetStatus) st.meetStatus.textContent = st.statuses[st.statusIdx];
if (st.meetDot) {
st.meetDot.setAttribute('fill',
st.statusIdx === 1 ? '#ef4444' : st.statusIdx === 2 ? '#f59e0b' : '#4CAF50');
}
}
// Line chart
if (st.linePath) {
var d = 'M';
for (var p = 0; p < st.linePts; p++) {
var x = (p / (st.linePts - 1)) * st.lineW;
var y = 10 + (1 - wave(st.phase * 0.8, p * 0.9)) * 30;
d += (p ? ' L' : '') + x.toFixed(1) + ',' + y.toFixed(1);
}
st.linePath.setAttribute('d', d);
}
}
/* ── Education ──────────────────────────────────────────── */
function initEducation(svg, st) {
st.schedSlots = svg.querySelectorAll('.ind-sched-slot');
st.alertBar = svg.querySelector('.ind-alert-bar');
st.alertText = svg.querySelector('.ind-alert-text');
st.alertPhase = 0;
st.alerts = ['Fire Drill 2:00 PM', 'Early Dismissal Fri', 'Gym Closed Today', 'Bus 12 Delayed'];
st.alertIdx = 0;
st.wayfindDots = svg.querySelectorAll('.ind-wf-dot');
st.wfActive = 0;
st.wfPhase = 0;
}
function tickEducation(st) {
// Schedule slots pulse (current class highlight)
for (var i = 0; i < st.schedSlots.length; i++) {
var isCurrent = (Math.floor(st.phase * 0.3) % st.schedSlots.length) === i;
st.schedSlots[i].setAttribute('opacity', isCurrent ? '1' : '0.5');
st.schedSlots[i].setAttribute('stroke', isCurrent ? '#D83302' : 'none');
st.schedSlots[i].setAttribute('stroke-width', isCurrent ? '1.5' : '0');
}
// Alert banner cycle
st.alertPhase += SPEED * 0.12;
if (st.alertPhase > 1) {
st.alertPhase = 0;
st.alertIdx = (st.alertIdx + 1) % st.alerts.length;
if (st.alertText) st.alertText.textContent = st.alerts[st.alertIdx];
}
// Alert bar pulse
if (st.alertBar) {
var ap = 0.6 + Math.sin(st.phase * 2) * 0.4;
st.alertBar.setAttribute('opacity', ap.toFixed(2));
}
// Wayfinding dot sequence
st.wfPhase += SPEED * 0.2;
if (st.wfPhase > 1) {
st.wfPhase = 0;
st.wfActive = (st.wfActive + 1) % Math.max(st.wayfindDots.length, 1);
}
for (var w = 0; w < st.wayfindDots.length; w++) {
st.wayfindDots[w].setAttribute('opacity', w === st.wfActive ? '1' : '0.2');
}
}
/* ── Outdoor Marketplace ────────────────────────────────── */
function initOutdoor(svg, st) {
st.weatherIcon = svg.querySelector('.ind-weather-icon');
st.weatherTemp = svg.querySelector('.ind-weather-temp');
st.weatherPhase = 0;
st.weathers = [
{ icon: '\u2600', temp: '24°C' },
{ icon: '\u26C5', temp: '19°C' },
{ icon: '\u2601', temp: '16°C' },
{ icon: '\u2600', temp: '22°C' }
];
st.weatherIdx = 0;
st.vendorBars = svg.querySelectorAll('.ind-vendor-bar');
st.busyDot = svg.querySelector('.ind-busy-dot');
st.busyLabel = svg.querySelector('.ind-busy-label');
}
function tickOutdoor(st) {
// Weather cycle
st.weatherPhase += SPEED * 0.1;
if (st.weatherPhase > 1) {
st.weatherPhase = 0;
st.weatherIdx = (st.weatherIdx + 1) % st.weathers.length;
var w = st.weathers[st.weatherIdx];
if (st.weatherIcon) st.weatherIcon.textContent = w.icon;
if (st.weatherTemp) st.weatherTemp.textContent = w.temp;
}
// Vendor activity bars
for (var i = 0; i < st.vendorBars.length; i++) {
var bv = wave(st.phase * 0.5, i * 1.6);
var h = Math.round(bv * 25 + 3);
st.vendorBars[i].setAttribute('height', h);
st.vendorBars[i].setAttribute('y', 28 - h);
}
// Busy indicator pulse
if (st.busyDot) {
var busy = wave(st.phase * 0.3, 0) > 0.6;
st.busyDot.setAttribute('fill', busy ? '#ef4444' : '#4CAF50');
if (st.busyLabel) st.busyLabel.textContent = busy ? 'Busy' : 'Quiet';
}
}
/* ── Live Data Displays ─────────────────────────────────── */
function initLiveData(svg, st) {
st.ldBars = svg.querySelectorAll('.ind-ld-bar');
st.ldVals = svg.querySelectorAll('.ind-ld-val');
st.ldLine = svg.querySelector('.ind-ld-line');
st.ldLineW = 110;
st.ldLinePts = 8;
st.ldPieSegs = svg.querySelectorAll('.ind-ld-pie');
st.ldPieR = 22;
st.ldAlertDot = svg.querySelector('.ind-ld-alert');
st.ldAlertText = svg.querySelector('.ind-ld-alert-text');
st.ldAlertPhase = 0;
st.ldAlerts = ['All Systems OK', 'CPU: 72%', 'Latency: 12ms', 'Queue: 340'];
st.ldAlertIdx = 0;
}
function tickLiveData(st) {
// Bars animate
for (var i = 0; i < st.ldBars.length; i++) {
var bv = wave(st.phase, i * 1.1);
var h = Math.round(bv * 30 + 5);
st.ldBars[i].setAttribute('height', h);
st.ldBars[i].setAttribute('y', 35 - h);
}
// Values update
for (var v = 0; v < st.ldVals.length; v++) {
var val = wave(st.phase, v * 1.1);
st.ldVals[v].textContent = Math.round(val * 5000);
}
// Line chart
if (st.ldLine) {
var d = 'M';
for (var p = 0; p < st.ldLinePts; p++) {
var x = (p / (st.ldLinePts - 1)) * st.ldLineW;
var y = 5 + (1 - wave(st.phase * 0.8, p * 0.9)) * 30;
d += (p ? ' L' : '') + x.toFixed(1) + ',' + y.toFixed(1);
}
st.ldLine.setAttribute('d', d);
}
// Pie chart
if (st.ldPieSegs.length) {
var n = st.ldPieSegs.length;
var weights = [], total = 0;
for (var pi = 0; pi < n; pi++) {
var pw = 0.5 + wave(st.phase * 0.4, pi * 2.0) * 0.5;
weights.push(pw);
total += pw;
}
var angle = 0;
for (var pj = 0; pj < n; pj++) {
var sweep = (weights[pj] / total) * 360;
var startA = angle * Math.PI / 180;
var endA = (angle + sweep) * Math.PI / 180;
var large = sweep > 180 ? 1 : 0;
var r = st.ldPieR;
var x1 = Math.sin(startA) * r, y1 = -Math.cos(startA) * r;
var x2 = Math.sin(endA) * r, y2 = -Math.cos(endA) * r;
var path = st.ldPieSegs[pj];
if (path) {
path.setAttribute('d',
'M0,0 L' + x1.toFixed(2) + ',' + y1.toFixed(2) +
' A' + r + ',' + r + ' 0 ' + large + ',1 ' +
x2.toFixed(2) + ',' + y2.toFixed(2) + ' Z');
}
angle += sweep;
}
}
// Alert text cycle
st.ldAlertPhase += SPEED * 0.12;
if (st.ldAlertPhase > 1) {
st.ldAlertPhase = 0;
st.ldAlertIdx = (st.ldAlertIdx + 1) % st.ldAlerts.length;
if (st.ldAlertText) st.ldAlertText.textContent = st.ldAlerts[st.ldAlertIdx];
if (st.ldAlertDot) {
st.ldAlertDot.setAttribute('fill', st.ldAlertIdx === 0 ? '#4CAF50' : '#f59e0b');
}
}
}
/* ── Registry ───────────────────────────────────────────── */
var INDUSTRIES = {
hospitality: { init: initHospitality, tick: tickHospitality },
retail: { init: initRetail, tick: tickRetail },
corporate: { init: initCorporate, tick: tickCorporate },
education: { init: initEducation, tick: tickEducation },
outdoor: { init: initOutdoor, tick: tickOutdoor },
livedata: { init: initLiveData, tick: tickLiveData }
};
/* ── Main loop ──────────────────────────────────────────── */
var instances = [];
function tickAll() {
for (var i = 0; i < instances.length; i++) {
var st = instances[i];
if (st.paused) continue;
st.phase += SPEED * 16;
st.handler.tick(st);
}
requestAnimationFrame(tickAll);
}
function observe(st) {
if (!('IntersectionObserver' in window)) return;
new IntersectionObserver(function (entries) {
for (var i = 0; i < entries.length; i++) {
st.paused = !entries[i].isIntersecting;
}
}, { rootMargin: '200px', threshold: 0.05 }).observe(st.el);
}
var INTERVAL = 4000; // ms between slides
function boot() {
var els = document.querySelectorAll('[data-industry-anim]');
if (!els.length) return;
var stages = document.querySelectorAll('[data-gtv-slideshow]');
if (!stages.length) return;
for (var i = 0; i < els.length; i++) {
var el = els[i];
var type = el.getAttribute('data-industry-anim');
var handler = INDUSTRIES[type];
if (!handler) continue;
if (el._indAnim) continue;
/* Honour reduced-motion show first slide only */
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
var svg = el.querySelector('svg');
if (!svg) continue;
for (var i = 0; i < stages.length; i++) {
initSlideshow(stages[i]);
}
}
var st = {
el: el,
svg: svg,
handler: handler,
phase: Math.random() * Math.PI * 2,
paused: false
};
function initSlideshow(stage) {
var slides = stage.querySelectorAll('.gtv-slide');
if (slides.length < 2) return;
handler.init(svg, st);
observe(st);
el._indAnim = st;
instances.push(st);
var state = { current: 0, paused: false, timer: null };
/* IntersectionObserver pause when off-screen */
if ('IntersectionObserver' in window) {
new IntersectionObserver(function (entries) {
for (var j = 0; j < entries.length; j++) {
state.paused = !entries[j].isIntersecting;
}
if (!state.paused && !state.timer) {
state.timer = setInterval(function () { advance(slides, state); }, INTERVAL);
} else if (state.paused && state.timer) {
clearInterval(state.timer);
state.timer = null;
}
}, { rootMargin: '200px', threshold: 0.05 }).observe(stage);
}
if (instances.length) requestAnimationFrame(tickAll);
/* Start cycling */
state.timer = setInterval(function () { advance(slides, state); }, INTERVAL);
}
function advance(slides, state) {
if (state.paused) return;
slides[state.current].classList.remove('is-active');
state.current = (state.current + 1) % slides.length;
slides[state.current].classList.add('is-active');
}
if (document.readyState === 'loading') {