roseworx
Version:
Front end css and js framework
307 lines (281 loc) • 7.36 kB
JavaScript
import {rwxAnimation} from './rwxAnimation';
import rwxDOM from '../helpers/rwxDOMHelpers';
import rwxSwipeTracking from '../common/rwxSwipeTracking';
require('../../scss/modules/rwxSkrollHighjack.scss');
class rwxSkrollHighjack {
constructor(navigation=true, ignore, scrollCallback)
{
this.resourceName = "rwxSkrollHighjack";
this.fn = scrollCallback;
this.ignore = ignore;
this.hasNavigation = navigation;
this.index = 0;
this.counters = [];
this.highjacked = false;
this.stopAnimation = false;
this.m = [...document.querySelectorAll('[data-rwx-skroll-highjack-section]')];
if(this.m.length===0)return;
this.unloadEvent();
this.createAnimation();
document.body.style.overflow = "hidden";
this.clonedEvents = [...window.rwx.scrollTracking.events];
window.rwx.scrollTracking.events = [];
this.animate = this.animate.bind(this);
this.hasNavigation && this.htmlDefinition();
this.immediate();
this.scrolling = this.scrolling.bind(this);
document.addEventListener('wheel', this.scrolling);
window.addEventListener('beforeunload', this.unloadEvent);
this.swipeTrack = new rwxSwipeTracking(window, this.swiped.bind(this));
this.closeNavEvent = this.closeNavEvent.bind(this);
this.hasNavigation && document.addEventListener('click', this.closeNavEvent);
this.catchTabs = this.catchTabs.bind(this);
window.addEventListener('keyup', this.catchTabs);
}
catchTabs(e)
{
if(e.keyCode===9)
{
const anc = rwxDOM.hasAncestor(e.target, '[data-rwx-skroll-highjack-section]')
if(anc)
{
const newIndex = this.m.findIndex((m)=>m===anc);
this.index = newIndex+1;
this.activeCounter();
}
}
}
closeNavEvent()
{
this.navigation.classList.remove('active');
}
unloadEvent()
{
window.scrollTo(0,0);
}
unhook(replaceEvents=true)
{
window.removeEventListener('beforeunload', this.unloadEvent);
if(replaceEvents)window.rwx.scrollTracking.events = this.clonedEvents;
document.body.style.overflow = "";
document.removeEventListener('wheel', this.scrolling);
if(this.hasNavigation)
{
document.body.removeChild(this.navigation);
document.removeEventListener('click', this.closeNavEvent);
}
}
htmlDefinition()
{
this.navigation = document.createElement('div');
this.navigation.setAttribute('tabIndex', 0);
this.navigation.classList.add('skroll-highjack-navigation');
this.m.map((m, i)=>{
let title = m.hasAttribute('data-rwx-skroll-highjack-section-title') ? m.getAttribute('data-rwx-skroll-highjack-section-title') : "";
let container = document.createElement('div');
container.classList.add('skroll-highjack-navigation-counter-container');
container.addEventListener('click', ()=>{
this.navigation.offsetWidth > 32 && this.goTo(i+1);
});
let counter = document.createElement('div');
counter.classList.add('skroll-highjack-navigation-counter');
this.addCounterAttributes(counter, title);
this.addCounterEvents(counter, i, this.m.length);
let text = document.createElement('span');
text.innerText = title;
container.appendChild(counter);
container.appendChild(text);
this.navigation.appendChild(container);
this.counters.push(counter);
});
document.body.insertBefore(this.navigation, document.body.children[0]);
this.navigation.focus();
}
addCounterAttributes(counter, text)
{
counter.setAttribute('tabIndex', 0);
counter.setAttribute('role', 'button');
counter.setAttribute('aria-label', text);
counter.setAttribute('title', text);
}
addCounterEvents(counter, i, length)
{
counter.addEventListener('keydown', (ev)=>{
if(ev.keyCode == 13 || ev.keyCode == 32)
{
ev.preventDefault();
this.goTo(i+1)
}
});
counter.addEventListener('focus', ()=>{
this.nextFocus = i;
this.navigation.classList.add('active');
});
counter.addEventListener('blur', ()=>{
setTimeout(()=>{
if((i===0 && this.nextFocus !==1) || (i===length-1 && this.nextFocus !== length-2))this.navigation.classList.remove('active');
}, 100);
});
}
animate()
{
if(!this.highjacked)return;
this.animation.animate((y)=>{
window.scrollTo(0, y);
this.fn && this.fn(y);
this.clonedEvents.map((ev)=>ev.ev());
})
window.requestAnimationFrame(this.animate);
}
goTo(t)
{
let newIndex = false;
if(typeof t === "number" && t<=this.m.length && t>0)
{
newIndex = t;
}
else if(typeof t === "string")
{
let found = this.m.findIndex((m)=>m.id===t);
if(found!==-1)newIndex=found+1;
}
if(newIndex===false || newIndex===this.index)return;
this.animation.reset();
this.index = newIndex;
this.highjacked = true;
this.activeCounter();
this.isAnimating = true;
this.animate();
}
activeCounter()
{
if(this.hasNavigation)
{
this.counters.map((c, i)=> i===this.index-1 ? c.classList.add('active') : c.classList.remove('active'));
}
}
immediate()
{
if(window.location.hash)
{
const touse = window.location.hash.replace('#', '');
const filtered = this.m.filter((m)=>m.id===touse);
if(filtered.length > 0)
{
const newIndex = this.m.findIndex((m)=>m.id===touse);
const newTop = filtered[0].getBoundingClientRect().top;
this.index = newIndex+1;
this.activeCounter();
window.requestAnimationFrame(()=>{
window.scrollTo(0, newTop);
this.fn && this.fn(newTop);
this.clonedEvents.map((ev)=>ev.ev());
})
}
}
}
getTop()
{
if(this.index===0)return 0;
let val = (this.m[this.index-1].getBoundingClientRect().top + window.pageYOffset)
if(val > (document.body.scrollHeight-window.innerHeight))
{
val = (document.body.scrollHeight-window.innerHeight);
}
return val;
}
createAnimation()
{
this.animation = new rwxAnimation({
from:()=>window.pageYOffset,
to: ()=>this.getTop(),
easing: 'easeOutQuad',
duration: 1000,
complete: ()=>{
this.isAnimating=false;
if(!this.timeout)
{
this.highjacked=false;
}
}
});
}
setTimeout() {
this.timeout = setTimeout(()=>{
this.timeout = false;
if(!this.isAnimating)
{
this.highjacked=false;
}
}, 100);
}
swiped(direction, e)
{
if(this.highjacked)return;
if(this.ignore)
{
if(this.determineIgnore(e.target))return;
}
if(direction==="up")
{
this.index+=1;
}
if(direction==="down")
{
this.index-=1;
}
this.highjacking();
}
scrolling(e)
{
if(this.highjacked && e.type ==="wheel")
{
if(!this.timeout)
{
this.setTimeout();
}
else
{
clearTimeout(this.timeout);
this.setTimeout();
}
}
if(this.highjacked || e.type!=="wheel" || this.timeout){return}
if(this.ignore)
{
if(this.determineIgnore(e.target))return;
}
if(event.deltaY < 0)
{
if(this.index===0){return;}
this.index -=1;
}
else if(event.deltaY > 0)
{
if(this.index===this.m.length){return;}
this.index +=1;
}
this.highjacking();
}
determineIgnore(target)
{
let toreturn = false;
this.ignore.filter((sc)=>(sc.scrollHeight > sc.offsetHeight || sc.scrollWidth > sc.offsetWidth)).map((i)=>{
if(target === i || i.contains(target))
{
toreturn=true;
}
return false;
});
return toreturn;
}
highjacking()
{
this.animation.reset();
this.highjacked = true;
this.activeCounter();
this.isAnimating = true;
this.animate();
}
}
export default rwxSkrollHighjack;