Add feature animations and update styles for kiosks and about pages
- Introduced `featuresAnim` option in editor and index files to enable feature ticker animations. - Updated rendering logic to include feature animations in the about section. - Added new JavaScript files for handling animations on the about page and kiosks page. - Adjusted CSS classes to accommodate new animations and ensure proper visual representation. - Changed primary color in theme defaults for a more cohesive design.
This commit is contained in:
191
theme/assets/js/kiosks-animator.js
Normal file
191
theme/assets/js/kiosks-animator.js
Normal file
@@ -0,0 +1,191 @@
|
||||
/**
|
||||
* Kiosks Page Animators
|
||||
* 1. Self Check-In — cycles through three visitor check-in scenarios
|
||||
* 2. Self Order — cycles active category + highlighted product in a catalogue kiosk
|
||||
*
|
||||
* Both respect prefers-reduced-motion and pause via IntersectionObserver.
|
||||
* Mirrors the patterns and conventions of solutions-animator.js.
|
||||
*/
|
||||
|
||||
/* ── 1. Self Check-In Kiosk Animator ───────────────────────────────────── */
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
|
||||
|
||||
var CYCLE_MS = 3500; /* ms per scene */
|
||||
|
||||
function initCheckin(stage) {
|
||||
var scenes = stage.querySelectorAll('.checkin-scene');
|
||||
if (!scenes.length) return;
|
||||
|
||||
var state = { idx: 0, timer: null, paused: false };
|
||||
|
||||
/* Show the first scene immediately */
|
||||
scenes[0].classList.add('is-active');
|
||||
|
||||
function advance() {
|
||||
if (state.paused) return;
|
||||
scenes[state.idx].classList.remove('is-active');
|
||||
state.idx = (state.idx + 1) % scenes.length;
|
||||
scenes[state.idx].classList.add('is-active');
|
||||
}
|
||||
|
||||
function startTimer() {
|
||||
if (state.timer) return;
|
||||
state.timer = setInterval(advance, CYCLE_MS);
|
||||
}
|
||||
|
||||
function stopTimer() {
|
||||
clearInterval(state.timer);
|
||||
state.timer = null;
|
||||
}
|
||||
|
||||
if ('IntersectionObserver' in window) {
|
||||
new IntersectionObserver(function (entries) {
|
||||
entries.forEach(function (e) {
|
||||
state.paused = !e.isIntersecting;
|
||||
e.isIntersecting ? startTimer() : stopTimer();
|
||||
});
|
||||
}, { rootMargin: '200px', threshold: 0.05 }).observe(stage);
|
||||
}
|
||||
|
||||
startTimer();
|
||||
}
|
||||
|
||||
function boot() {
|
||||
var stages = document.querySelectorAll('.checkin-stage');
|
||||
if (!stages.length) return;
|
||||
for (var i = 0; i < stages.length; i++) {
|
||||
if (stages[i]._checkinAnim) continue;
|
||||
stages[i]._checkinAnim = true;
|
||||
initCheckin(stages[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', boot);
|
||||
} else {
|
||||
boot();
|
||||
}
|
||||
}());
|
||||
|
||||
/* ── 2. Self-Order / Product Lookup Kiosk Animator ─────────────────────── */
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
|
||||
/* In reduced-motion: just show all products for the first category */
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
document.querySelectorAll('.selforder-stage').forEach(function (stage) {
|
||||
var first = stage.querySelector('.selforder-cat');
|
||||
if (first) first.classList.add('is-active');
|
||||
var cat = first ? first.getAttribute('data-so-cat') : null;
|
||||
if (cat) {
|
||||
stage.querySelectorAll('.selforder-product[data-so-cat="' + cat + '"]').forEach(function (p) {
|
||||
p.classList.add('is-visible');
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
/* Category rotation order */
|
||||
var CATS = ['electronics', 'clothing', 'food', 'home'];
|
||||
var CYCLE_MS = 4000; /* ms per category */
|
||||
/* Which product index within the visible set to highlight each cycle */
|
||||
var HIGHLIGHT_IDX = [0, 1, 0, 1];
|
||||
|
||||
function initSelfOrder(stage) {
|
||||
var catEls = stage.querySelectorAll('.selforder-cat');
|
||||
var productEls = stage.querySelectorAll('.selforder-product');
|
||||
var addBtn = stage.querySelector('.selforder-add');
|
||||
if (!catEls.length || !productEls.length) return;
|
||||
|
||||
var state = { idx: 0, timer: null, paused: false };
|
||||
|
||||
function showCategory(catIdx) {
|
||||
var cat = CATS[catIdx % CATS.length];
|
||||
|
||||
/* Update category tabs */
|
||||
for (var i = 0; i < catEls.length; i++) {
|
||||
catEls[i].classList.toggle('is-active', catEls[i].getAttribute('data-so-cat') === cat);
|
||||
}
|
||||
|
||||
/* Show matching products, hide others */
|
||||
var visible = [];
|
||||
for (var j = 0; j < productEls.length; j++) {
|
||||
var matches = productEls[j].getAttribute('data-so-cat') === cat;
|
||||
productEls[j].classList.toggle('is-visible', matches);
|
||||
productEls[j].classList.remove('is-highlighted');
|
||||
if (matches) visible.push(productEls[j]);
|
||||
}
|
||||
|
||||
/* Highlight one product after a short delay (simulates tap) */
|
||||
var hlIdx = HIGHLIGHT_IDX[catIdx % HIGHLIGHT_IDX.length];
|
||||
var toHighlight = visible[hlIdx % (visible.length || 1)];
|
||||
if (toHighlight) {
|
||||
setTimeout(function () {
|
||||
toHighlight.classList.add('is-highlighted');
|
||||
if (addBtn) {
|
||||
addBtn.removeAttribute('disabled');
|
||||
addBtn.classList.add('is-enabled');
|
||||
}
|
||||
}, 900);
|
||||
}
|
||||
|
||||
/* Reset button when next cycle starts */
|
||||
if (addBtn) {
|
||||
addBtn.setAttribute('disabled', '');
|
||||
addBtn.classList.remove('is-enabled');
|
||||
}
|
||||
}
|
||||
|
||||
/* Show first category immediately */
|
||||
showCategory(0);
|
||||
|
||||
function advance() {
|
||||
if (state.paused) return;
|
||||
state.idx++;
|
||||
showCategory(state.idx);
|
||||
}
|
||||
|
||||
function startTimer() {
|
||||
if (state.timer) return;
|
||||
state.timer = setInterval(advance, CYCLE_MS);
|
||||
}
|
||||
|
||||
function stopTimer() {
|
||||
clearInterval(state.timer);
|
||||
state.timer = null;
|
||||
}
|
||||
|
||||
if ('IntersectionObserver' in window) {
|
||||
new IntersectionObserver(function (entries) {
|
||||
entries.forEach(function (e) {
|
||||
state.paused = !e.isIntersecting;
|
||||
e.isIntersecting ? startTimer() : stopTimer();
|
||||
});
|
||||
}, { rootMargin: '200px', threshold: 0.05 }).observe(stage);
|
||||
}
|
||||
|
||||
startTimer();
|
||||
}
|
||||
|
||||
function boot() {
|
||||
var stages = document.querySelectorAll('.selforder-stage');
|
||||
if (!stages.length) return;
|
||||
for (var i = 0; i < stages.length; i++) {
|
||||
if (stages[i]._selforderAnim) continue;
|
||||
stages[i]._selforderAnim = true;
|
||||
initSelfOrder(stages[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', boot);
|
||||
} else {
|
||||
boot();
|
||||
}
|
||||
}());
|
||||
Reference in New Issue
Block a user