mirror of
https://github.com/rxliuli/apps.apple.com.git
synced 2025-12-08 09:01:44 +00:00
144 lines
3.9 KiB
TypeScript
144 lines
3.9 KiB
TypeScript
// COPIED FROM
|
|
// https://github.pie.apple.com/amp-ui/ember-ui-media-shelf/blob/580ff07a546771bce8b3d85494c6268860e97215/addon/-private/scroll-by-polyfill.js
|
|
|
|
const SCROLL_TIME = 468;
|
|
const Element =
|
|
typeof window !== 'undefined' ? window.HTMLElement || window.Element : null;
|
|
|
|
let originalScrollBy;
|
|
|
|
/**
|
|
* returns result of applying ease math function to a number
|
|
* @method ease
|
|
* @param {Number} k
|
|
* @returns {Number}
|
|
*/
|
|
function ease(k: number): number {
|
|
return 0.5 * (1 - Math.cos(Math.PI * k));
|
|
}
|
|
|
|
// define timing method
|
|
const now: () => number =
|
|
typeof window !== 'undefined' && window?.performance?.now
|
|
? window.performance.now.bind(window.performance)
|
|
: Date.now;
|
|
|
|
/**
|
|
* changes scroll position inside an element
|
|
* @method scrollElement
|
|
* @param {Number} x
|
|
* @returns {undefined}
|
|
*/
|
|
function scrollElement(x: number): void {
|
|
this.scrollLeft = x;
|
|
}
|
|
|
|
/**
|
|
* self invoked function that, given a context, steps through scrolling
|
|
* @method step
|
|
* @param {Object} context
|
|
* @returns {undefined}
|
|
*/
|
|
type Context = {
|
|
startTime: number;
|
|
startX: number;
|
|
x: number;
|
|
method: (x: number) => void;
|
|
scrollable: HTMLElement;
|
|
};
|
|
function step(context: Context): void {
|
|
const time = now();
|
|
let elapsed = (time - context.startTime) / SCROLL_TIME;
|
|
|
|
// avoid elapsed times higher than one
|
|
elapsed = Math.min(1, elapsed);
|
|
|
|
// apply easing to elapsed time
|
|
const value = ease(elapsed);
|
|
|
|
const currentX = context.startX + (context.x - context.startX) * value;
|
|
|
|
context.method.call(context.scrollable, currentX);
|
|
|
|
// scroll more if we have not reached our destination
|
|
if (currentX !== context.x) {
|
|
window.requestAnimationFrame(step.bind(window, context));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* scrolls window or element with a smooth behavior
|
|
* @method smoothScroll
|
|
* @param {Object|Node} el
|
|
* @param {Number} x
|
|
* @returns {undefined}
|
|
*/
|
|
function smoothScroll(el: HTMLElement, x: number): void {
|
|
const startTime = now();
|
|
// define scroll context
|
|
const startX = el.scrollLeft;
|
|
const method = scrollElement;
|
|
|
|
// scroll looping over a frame
|
|
step({
|
|
scrollable: el,
|
|
method,
|
|
startTime,
|
|
startX,
|
|
x,
|
|
});
|
|
}
|
|
|
|
let polyfillHasRun = false;
|
|
/**
|
|
* ripped partially from https://github.com/iamdustan/smoothscroll/blob/master/src/smoothscroll.js
|
|
* Only polyfill horizontal scroll space to avoid unexpected behaviour in parent apps
|
|
*
|
|
* @method scrollByPolyfill
|
|
*/
|
|
export default function scrollByPolyfill(): void {
|
|
// return if scroll behavior is supported
|
|
if ('scrollBehavior' in document.documentElement.style || polyfillHasRun) {
|
|
return;
|
|
}
|
|
|
|
// if prefers-reduce-motion && need polyfill, navigate shelf immediately without easing
|
|
const motionMediaQuery = window.matchMedia(
|
|
'(prefers-reduced-motion: reduce)',
|
|
);
|
|
function addScrollByToProto() {
|
|
if (motionMediaQuery.matches) {
|
|
if (originalScrollBy) {
|
|
Element.prototype.scrollBy = originalScrollBy;
|
|
}
|
|
return;
|
|
}
|
|
|
|
function scrollByPoly(options: ScrollToOptions): void;
|
|
function scrollByPoly(x: number, _y: number): void;
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
function scrollByPoly(
|
|
paramOne: number | ScrollToOptions,
|
|
_paramTwo?: number,
|
|
): void {
|
|
let xValue = 0;
|
|
if (typeof paramOne === 'number') {
|
|
xValue = paramOne;
|
|
} else if (typeof paramOne === 'object') {
|
|
xValue = paramOne.left || 0;
|
|
}
|
|
|
|
const moveByX = this.scrollLeft + xValue;
|
|
smoothScroll(this, moveByX);
|
|
}
|
|
|
|
originalScrollBy = Element.prototype.scrollBy;
|
|
Element.prototype.scrollBy = scrollByPoly;
|
|
}
|
|
|
|
motionMediaQuery.addListener(addScrollByToProto);
|
|
|
|
addScrollByToProto();
|
|
polyfillHasRun = true;
|
|
}
|