64 lines
4.3 KiB
JavaScript
64 lines
4.3 KiB
JavaScript
(() => {
|
|
const {createTimeline, stagger} = anime;
|
|
const reduced = matchMedia('(prefers-reduced-motion: reduce)');
|
|
const printMode = location.search.includes('print-pdf');
|
|
const seen = new Set(), timelines = new Map(); let motion = !reduced.matches;
|
|
const motionButton = document.querySelector('#motion');
|
|
// Explicit paused instances: Reveal owns slide navigation; one clock owns motion.
|
|
// Targets are dedicated motion wrappers, not positioned/rotated diagram parts.
|
|
document.querySelectorAll('.slides>section').forEach(slide=>{
|
|
const targets=[...slide.querySelectorAll('[data-motion]')];
|
|
if(!targets.length)return;
|
|
const tl=createTimeline({autoplay:false,defaults:{duration:300,ease:'outCubic'}});
|
|
if(slide.id==='sequence') {
|
|
['observe','interpret','act'].forEach((label,i)=>{
|
|
tl.label(label,i*180).add(targets[i],{opacity:[0,1],translateY:[12,0]},label);
|
|
});
|
|
} else tl.add(targets,{opacity:[0,1],translateY:[12,0],delay:stagger(70),duration:400},0);
|
|
tl.seek(tl.duration);tl.pause();timelines.set(slide,tl);
|
|
});
|
|
const allowed=()=>motion&&!reduced.matches&&!printMode&&!Reveal.isOverview();
|
|
function settle() {
|
|
timelines.forEach(tl=>{tl.pause();tl.seek(tl.duration);});
|
|
}
|
|
function replay() {
|
|
settle();const tl=timelines.get(Reveal.getCurrentSlide());
|
|
if(!tl||!allowed())return false;
|
|
tl.seek(0);tl.play();return true;
|
|
}
|
|
function seek(timeMs) {
|
|
if(!Number.isFinite(timeMs))throw new TypeError('seek requires finite milliseconds');
|
|
const tl=timelines.get(Reveal.getCurrentSlide());
|
|
if(!tl)return false;
|
|
tl.pause();tl.seek(allowed()?Math.max(0,Math.min(timeMs,tl.duration)):tl.duration);
|
|
return true;
|
|
}
|
|
function run() {
|
|
settle();const slide=Reveal.getCurrentSlide();
|
|
if(!slide||seen.has(slide.id)||!allowed())return;
|
|
seen.add(slide.id);replay();
|
|
}
|
|
function motionLabel(){motionButton.textContent=motion&&!reduced.matches?'Motion on':'Motion off';motionButton.setAttribute('aria-pressed',String(motion&&!reduced.matches));}
|
|
motionButton.onclick=()=>{motion=!motion;settle();motionLabel();};
|
|
reduced.addEventListener('change',()=>{if(reduced.matches)settle();motionLabel();});motionLabel();
|
|
addEventListener('beforeprint',settle);
|
|
const fs=document.querySelector('#fullscreen');
|
|
async function fullscreen(){try{if(document.fullscreenElement)await document.exitFullscreen();else await document.documentElement.requestFullscreen();}catch{fs.textContent='Fullscreen unavailable';}}
|
|
fs.onclick=fullscreen;
|
|
document.addEventListener('fullscreenchange',()=>{fs.textContent=document.fullscreenElement?'Exit fullscreen':'Enter fullscreen';fs.setAttribute('aria-pressed',String(!!document.fullscreenElement));});
|
|
document.addEventListener('keydown',e=>{if(e.key.toLowerCase()==='f'&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.target.matches('input,textarea,[contenteditable]')){e.preventDefault();e.stopPropagation();fullscreen();}},true);
|
|
document.querySelectorAll('[data-node]').forEach(node=>{
|
|
function select(){const was=node.getAttribute('aria-pressed')==='true';document.querySelectorAll('[data-node]').forEach(n=>n.setAttribute('aria-pressed','false'));node.setAttribute('aria-pressed',String(!was));document.querySelector('#detail').textContent=was?'Select a source to reveal its role. Tab + Enter or Space also works.':node.dataset.detail;}
|
|
node.addEventListener('click',select);
|
|
node.addEventListener('keydown',e=>{if(e.key==='Enter'||e.key===' '){e.preventDefault();e.stopPropagation();select();}});
|
|
});
|
|
Reveal.initialize({width:1240,height:700,margin:.08,center:false,hash:true,transition:'none',backgroundTransition:'none',slideNumber:'c/t',view:'slide',keyboard:{70:null},autoAnimate:false}).then(()=>{
|
|
if(!location.hash){try{const i=Number(localStorage.getItem('presentation-html-demo-slide'));if(Number.isInteger(i)&&i>=0&&i<Reveal.getTotalSlides())Reveal.slide(i);}catch{}}
|
|
run(); window.deckReady=true;
|
|
});
|
|
Reveal.on('slidechanged',()=>{try{localStorage.setItem('presentation-html-demo-slide',String(Reveal.getIndices().h));}catch{}run();});
|
|
Reveal.on('overviewshown',settle);
|
|
document.addEventListener('visibilitychange',()=>{if(document.hidden)settle();});
|
|
window.presentationQA={settle,seek,replay,reduced:()=>reduced.matches,active:()=>[...timelines.values()].filter(tl=>!tl.paused&&!tl.completed).length};
|
|
})();
|