forgeplus-react/node_modules/scroll-into-view/scrollIntoView.js

277 lines
8.0 KiB
JavaScript
Executable File

var COMPLETE = 'complete',
CANCELED = 'canceled';
function raf(task){
if('requestAnimationFrame' in window){
return window.requestAnimationFrame(task);
}
setTimeout(task, 16);
}
function setElementScroll(element, x, y){
Math.max(0, x);
Math.max(0, y);
if(element.self === element){
element.scrollTo(x, y);
}else{
element.scrollLeft = x;
element.scrollTop = y;
}
}
function getTargetScrollLocation(scrollSettings, parent){
var align = scrollSettings.align,
target = scrollSettings.target,
targetPosition = target.getBoundingClientRect(),
parentPosition,
x,
y,
differenceX,
differenceY,
targetWidth,
targetHeight,
leftAlign = align && align.left != null ? align.left : 0.5,
topAlign = align && align.top != null ? align.top : 0.5,
leftOffset = align && align.leftOffset != null ? align.leftOffset : 0,
topOffset = align && align.topOffset != null ? align.topOffset : 0,
leftScalar = leftAlign,
topScalar = topAlign;
if(scrollSettings.isWindow(parent)){
targetWidth = Math.min(targetPosition.width, parent.innerWidth);
targetHeight = Math.min(targetPosition.height, parent.innerHeight);
x = targetPosition.left + parent.pageXOffset - parent.innerWidth * leftScalar + targetWidth * leftScalar;
y = targetPosition.top + parent.pageYOffset - parent.innerHeight * topScalar + targetHeight * topScalar;
x -= leftOffset;
y -= topOffset;
x = scrollSettings.align.lockX ? parent.pageXOffset : x;
y = scrollSettings.align.lockY ? parent.pageYOffset : y;
differenceX = x - parent.pageXOffset;
differenceY = y - parent.pageYOffset;
}else{
targetWidth = targetPosition.width;
targetHeight = targetPosition.height;
parentPosition = parent.getBoundingClientRect();
var offsetLeft = targetPosition.left - (parentPosition.left - parent.scrollLeft);
var offsetTop = targetPosition.top - (parentPosition.top - parent.scrollTop);
x = offsetLeft + (targetWidth * leftScalar) - parent.clientWidth * leftScalar;
y = offsetTop + (targetHeight * topScalar) - parent.clientHeight * topScalar;
x -= leftOffset;
y -= topOffset;
x = Math.max(Math.min(x, parent.scrollWidth - parent.clientWidth), 0);
y = Math.max(Math.min(y, parent.scrollHeight - parent.clientHeight), 0);
x = scrollSettings.align.lockX ? parent.scrollLeft : x;
y = scrollSettings.align.lockY ? parent.scrollTop : y;
differenceX = x - parent.scrollLeft;
differenceY = y - parent.scrollTop;
}
return {
x: x,
y: y,
differenceX: differenceX,
differenceY: differenceY
};
}
function animate(parent){
var scrollSettings = parent._scrollSettings;
if(!scrollSettings){
return;
}
var maxSynchronousAlignments = scrollSettings.maxSynchronousAlignments;
var location = getTargetScrollLocation(scrollSettings, parent),
time = Date.now() - scrollSettings.startTime,
timeValue = Math.min(1 / scrollSettings.time * time, 1);
if(scrollSettings.endIterations >= maxSynchronousAlignments){
setElementScroll(parent, location.x, location.y);
parent._scrollSettings = null;
return scrollSettings.end(COMPLETE);
}
var easeValue = 1 - scrollSettings.ease(timeValue);
setElementScroll(parent,
location.x - location.differenceX * easeValue,
location.y - location.differenceY * easeValue
);
if(time >= scrollSettings.time){
scrollSettings.endIterations++;
// Align ancestor synchronously
scrollSettings.scrollAncestor && animate(scrollSettings.scrollAncestor);
animate(parent);
return;
}
raf(animate.bind(null, parent));
}
function defaultIsWindow(target){
return target.self === target
}
function transitionScrollTo(target, parent, settings, scrollAncestor, callback){
var idle = !parent._scrollSettings,
lastSettings = parent._scrollSettings,
now = Date.now(),
cancelHandler,
passiveOptions = { passive: true };
if(lastSettings){
lastSettings.end(CANCELED);
}
function end(endType){
parent._scrollSettings = null;
if(parent.parentElement && parent.parentElement._scrollSettings){
parent.parentElement._scrollSettings.end(endType);
}
if(settings.debug){
console.log('Scrolling ended with type', endType, 'for', parent)
}
callback(endType);
if(cancelHandler){
parent.removeEventListener('touchstart', cancelHandler, passiveOptions);
parent.removeEventListener('wheel', cancelHandler, passiveOptions);
}
}
var maxSynchronousAlignments = settings.maxSynchronousAlignments;
if(maxSynchronousAlignments == null){
maxSynchronousAlignments = 3;
}
parent._scrollSettings = {
startTime: now,
endIterations: 0,
target: target,
time: settings.time,
ease: settings.ease,
align: settings.align,
isWindow: settings.isWindow || defaultIsWindow,
maxSynchronousAlignments: maxSynchronousAlignments,
end: end,
scrollAncestor
};
if(!('cancellable' in settings) || settings.cancellable){
cancelHandler = end.bind(null, CANCELED);
parent.addEventListener('touchstart', cancelHandler, passiveOptions);
parent.addEventListener('wheel', cancelHandler, passiveOptions);
}
if(idle){
animate(parent);
}
return cancelHandler
}
function defaultIsScrollable(element){
return (
'pageXOffset' in element ||
(
element.scrollHeight !== element.clientHeight ||
element.scrollWidth !== element.clientWidth
) &&
getComputedStyle(element).overflow !== 'hidden'
);
}
function defaultValidTarget(){
return true;
}
function findParentElement(el){
if (el.assignedSlot) {
return findParentElement(el.assignedSlot);
}
if (el.parentElement) {
if(el.parentElement.tagName.toLowerCase() === 'body'){
return el.parentElement.ownerDocument.defaultView || el.parentElement.ownerDocument.ownerWindow;
}
return el.parentElement;
}
if (el.getRootNode){
var parent = el.getRootNode()
if(parent.nodeType === 11) {
return parent.host;
}
}
}
module.exports = function(target, settings, callback){
if(!target){
return;
}
if(typeof settings === 'function'){
callback = settings;
settings = null;
}
if(!settings){
settings = {};
}
settings.time = isNaN(settings.time) ? 1000 : settings.time;
settings.ease = settings.ease || function(v){return 1 - Math.pow(1 - v, v / 2);};
settings.align = settings.align || {};
var parent = findParentElement(target),
parents = 1;
function done(endType){
parents--;
if(!parents){
callback && callback(endType);
}
}
var validTarget = settings.validTarget || defaultValidTarget;
var isScrollable = settings.isScrollable;
if(settings.debug){
console.log('About to scroll to', target)
if(!parent){
console.error('Target did not have a parent, is it mounted in the DOM?')
}
}
var scrollingElements = [];
while(parent){
if(settings.debug){
console.log('Scrolling parent node', parent)
}
if(validTarget(parent, parents) && (isScrollable ? isScrollable(parent, defaultIsScrollable) : defaultIsScrollable(parent))){
parents++;
scrollingElements.push(parent);
}
parent = findParentElement(parent);
if(!parent){
done(COMPLETE)
break;
}
}
return scrollingElements.reduce((cancel, parent, index) => transitionScrollTo(target, parent, settings, scrollingElements[index + 1], done), null);
};