mirror of
https://github.com/tenrok/OverlayScrollbars.git
synced 2026-06-22 11:20:36 +03:00
add clickScrollPlguin for click scrolling support
This commit is contained in:
@@ -0,0 +1,63 @@
|
|||||||
|
import { animateNumber, noop } from 'support';
|
||||||
|
import type { Plugin } from 'plugins';
|
||||||
|
|
||||||
|
export type ClickScrollPluginInstance = {
|
||||||
|
_: (
|
||||||
|
moveHandleRelative: (deltaMovement: number) => void,
|
||||||
|
getHandleOffset: (handleRect?: DOMRect, trackRect?: DOMRect) => number,
|
||||||
|
startOffset: number,
|
||||||
|
handleLength: number,
|
||||||
|
relativeTrackPointerOffset: number
|
||||||
|
) => () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const clickScrollPluginName = '__osClickScrollPlugin';
|
||||||
|
|
||||||
|
export const clickScrollPlugin: Plugin<ClickScrollPluginInstance> = /* @__PURE__ */ (() => ({
|
||||||
|
[clickScrollPluginName]: {
|
||||||
|
_: (
|
||||||
|
moveHandleRelative,
|
||||||
|
getHandleOffset,
|
||||||
|
startOffset,
|
||||||
|
handleLength,
|
||||||
|
relativeTrackPointerOffset
|
||||||
|
) => {
|
||||||
|
// click scroll animation
|
||||||
|
let iteration = 0;
|
||||||
|
let clear = noop;
|
||||||
|
const animateClickScroll = (clickScrollProgress: number) => {
|
||||||
|
clear = animateNumber(
|
||||||
|
clickScrollProgress,
|
||||||
|
clickScrollProgress + handleLength * Math.sign(startOffset),
|
||||||
|
133,
|
||||||
|
(animationProgress, _, animationCompleted) => {
|
||||||
|
moveHandleRelative(animationProgress);
|
||||||
|
const handleStartBound = getHandleOffset();
|
||||||
|
const handleEndBound = handleStartBound + handleLength;
|
||||||
|
const mouseBetweenHandleBounds =
|
||||||
|
relativeTrackPointerOffset >= handleStartBound &&
|
||||||
|
relativeTrackPointerOffset <= handleEndBound;
|
||||||
|
|
||||||
|
if (animationCompleted && !mouseBetweenHandleBounds) {
|
||||||
|
if (iteration) {
|
||||||
|
animateClickScroll(animationProgress);
|
||||||
|
} else {
|
||||||
|
const firstIterationPauseTimeout = setTimeout(() => {
|
||||||
|
animateClickScroll(animationProgress);
|
||||||
|
}, 222);
|
||||||
|
clear = () => {
|
||||||
|
clearTimeout(firstIterationPauseTimeout);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
iteration++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
animateClickScroll(0);
|
||||||
|
|
||||||
|
return () => clear();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}))();
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from 'plugins/clickScrollPlugin/clickScrollPlugin';
|
||||||
@@ -2,3 +2,4 @@ export * from './plugins';
|
|||||||
export * from './optionsValidationPlugin';
|
export * from './optionsValidationPlugin';
|
||||||
export * from './sizeObserverPlugin';
|
export * from './sizeObserverPlugin';
|
||||||
export * from './scrollbarsHidingPlugin';
|
export * from './scrollbarsHidingPlugin';
|
||||||
|
export * from './clickScrollPlugin';
|
||||||
|
|||||||
@@ -10,17 +10,16 @@ import {
|
|||||||
selfClearTimeout,
|
selfClearTimeout,
|
||||||
parent,
|
parent,
|
||||||
closest,
|
closest,
|
||||||
rAF,
|
|
||||||
cAF,
|
|
||||||
push,
|
push,
|
||||||
noop,
|
|
||||||
} from 'support';
|
} from 'support';
|
||||||
|
import { getPlugins, clickScrollPluginName } from 'plugins';
|
||||||
import { getEnvironment } from 'environment';
|
import { getEnvironment } from 'environment';
|
||||||
import {
|
import {
|
||||||
classNameScrollbarHandle,
|
classNameScrollbarHandle,
|
||||||
classNamesScrollbarInteraction,
|
classNamesScrollbarInteraction,
|
||||||
classNamesScrollbarWheel,
|
classNamesScrollbarWheel,
|
||||||
} from 'classnames';
|
} from 'classnames';
|
||||||
|
import type { ClickScrollPluginInstance } from 'plugins';
|
||||||
import type { ReadonlyOptions } from 'options';
|
import type { ReadonlyOptions } from 'options';
|
||||||
import type { StructureSetupState } from 'setups';
|
import type { StructureSetupState } from 'setups';
|
||||||
import type {
|
import type {
|
||||||
@@ -37,31 +36,7 @@ export type ScrollbarsSetupEvents = (
|
|||||||
isHorizontal?: boolean
|
isHorizontal?: boolean
|
||||||
) => () => void;
|
) => () => void;
|
||||||
|
|
||||||
const { round, max, sign } = Math;
|
const { round } = Math;
|
||||||
const animationCurrentTime = () => performance.now();
|
|
||||||
const animateNumber = (
|
|
||||||
from: number,
|
|
||||||
to: number,
|
|
||||||
duration: number,
|
|
||||||
onFrame: (progress: number, completed: boolean) => any
|
|
||||||
) => {
|
|
||||||
let animationFrameId = 0;
|
|
||||||
const timeStart = animationCurrentTime();
|
|
||||||
const frame = () => {
|
|
||||||
const timeNow = animationCurrentTime();
|
|
||||||
const timeElapsed = timeNow - timeStart;
|
|
||||||
const stopAnimation = timeElapsed >= duration;
|
|
||||||
const percent = 1 - (max(0, timeStart + duration - timeNow) / duration || 0);
|
|
||||||
const progress = (to - from) * percent + from;
|
|
||||||
const animationCompleted = stopAnimation || percent === 1;
|
|
||||||
|
|
||||||
onFrame(progress, animationCompleted);
|
|
||||||
|
|
||||||
animationFrameId = animationCompleted ? 0 : rAF!(frame);
|
|
||||||
};
|
|
||||||
frame();
|
|
||||||
return () => cAF!(animationFrameId);
|
|
||||||
};
|
|
||||||
const getScale = (element: HTMLElement): XY<number> => {
|
const getScale = (element: HTMLElement): XY<number> => {
|
||||||
const { width, height } = getBoundingClientRect(element);
|
const { width, height } = getBoundingClientRect(element);
|
||||||
const { w, h } = offsetSize(element);
|
const { w, h } = offsetSize(element);
|
||||||
@@ -108,8 +83,7 @@ const createInteractiveScrollEvents = (
|
|||||||
const leftTopKey = isHorizontal ? 'left' : 'top'; // for BCR (can't use xy because of IE11)
|
const leftTopKey = isHorizontal ? 'left' : 'top'; // for BCR (can't use xy because of IE11)
|
||||||
const whKey = isHorizontal ? 'w' : 'h';
|
const whKey = isHorizontal ? 'w' : 'h';
|
||||||
const xyKey = isHorizontal ? 'x' : 'y';
|
const xyKey = isHorizontal ? 'x' : 'y';
|
||||||
const getHandleOffset = (handleRect: DOMRect, trackRect: DOMRect) =>
|
|
||||||
handleRect[leftTopKey] - trackRect[leftTopKey];
|
|
||||||
const createRelativeHandleMove =
|
const createRelativeHandleMove =
|
||||||
(mouseDownScroll: number, invertedScale: number) => (deltaMovement: number) => {
|
(mouseDownScroll: number, invertedScale: number) => (deltaMovement: number) => {
|
||||||
const { _overflowAmount } = structureSetupState();
|
const { _overflowAmount } = structureSetupState();
|
||||||
@@ -129,13 +103,17 @@ const createInteractiveScrollEvents = (
|
|||||||
|
|
||||||
if (continuePointerDown(pointerDownEvent, options, isDragScroll)) {
|
if (continuePointerDown(pointerDownEvent, options, isDragScroll)) {
|
||||||
const instantClickScroll = !isDragScroll && pointerDownEvent.shiftKey;
|
const instantClickScroll = !isDragScroll && pointerDownEvent.shiftKey;
|
||||||
|
const getHandleRect = () => getBoundingClientRect(_handle);
|
||||||
|
const getTrackRect = () => getBoundingClientRect(_track);
|
||||||
|
const getHandleOffset = (handleRect?: DOMRect, trackRect?: DOMRect) =>
|
||||||
|
(handleRect || getHandleRect())[leftTopKey] - (trackRect || getTrackRect())[leftTopKey];
|
||||||
const moveHandleRelative = createRelativeHandleMove(
|
const moveHandleRelative = createRelativeHandleMove(
|
||||||
scrollOffsetElement[scrollLeftTopKey] || 0,
|
scrollOffsetElement[scrollLeftTopKey] || 0,
|
||||||
1 / getScale(scrollOffsetElement)[xyKey]
|
1 / getScale(scrollOffsetElement)[xyKey]
|
||||||
);
|
);
|
||||||
const pointerDownOffset = pointerDownEvent[clientXYKey];
|
const pointerDownOffset = pointerDownEvent[clientXYKey];
|
||||||
const handleRect = getBoundingClientRect(_handle);
|
const handleRect = getHandleRect();
|
||||||
const trackRect = getBoundingClientRect(_track);
|
const trackRect = getTrackRect();
|
||||||
const handleLength = handleRect[widthHeightKey];
|
const handleLength = handleRect[widthHeightKey];
|
||||||
const handleCenter = getHandleOffset(handleRect, trackRect) + handleLength / 2;
|
const handleCenter = getHandleOffset(handleRect, trackRect) + handleLength / 2;
|
||||||
const relativeTrackPointerOffset = pointerDownOffset - trackRect[leftTopKey];
|
const relativeTrackPointerOffset = pointerDownOffset - trackRect[leftTopKey];
|
||||||
@@ -157,42 +135,22 @@ const createInteractiveScrollEvents = (
|
|||||||
if (instantClickScroll) {
|
if (instantClickScroll) {
|
||||||
moveHandleRelative(startOffset);
|
moveHandleRelative(startOffset);
|
||||||
} else if (!isDragScroll) {
|
} else if (!isDragScroll) {
|
||||||
// click scroll animation
|
const sizeObserverPlugin = getPlugins()[clickScrollPluginName] as
|
||||||
let iteration = 0;
|
| ClickScrollPluginInstance
|
||||||
let clear = noop;
|
| undefined;
|
||||||
const animateClickScroll = (clickScrollProgress: number) => {
|
|
||||||
clear = animateNumber(
|
|
||||||
clickScrollProgress,
|
|
||||||
clickScrollProgress + handleLength * sign(startOffset),
|
|
||||||
133,
|
|
||||||
(animationProgress, animationCompleted) => {
|
|
||||||
moveHandleRelative(animationProgress);
|
|
||||||
const handleStartBound = getHandleOffset(getBoundingClientRect(_handle), trackRect);
|
|
||||||
const handleEndBound = handleStartBound + handleLength;
|
|
||||||
const mouseBetweenHandleBounds =
|
|
||||||
relativeTrackPointerOffset >= handleStartBound &&
|
|
||||||
relativeTrackPointerOffset <= handleEndBound;
|
|
||||||
|
|
||||||
if (animationCompleted && !mouseBetweenHandleBounds) {
|
if (sizeObserverPlugin) {
|
||||||
if (iteration) {
|
push(
|
||||||
animateClickScroll(animationProgress);
|
offFns,
|
||||||
} else {
|
sizeObserverPlugin._(
|
||||||
const firstIterationPauseTimeout = setTimeout(() => {
|
moveHandleRelative,
|
||||||
animateClickScroll(animationProgress);
|
getHandleOffset,
|
||||||
}, 222);
|
startOffset,
|
||||||
clear = () => {
|
handleLength,
|
||||||
clearTimeout(firstIterationPauseTimeout);
|
relativeTrackPointerOffset
|
||||||
};
|
)
|
||||||
}
|
|
||||||
iteration++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
animateClickScroll(0);
|
|
||||||
|
|
||||||
push(offFns, () => clear());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
on(
|
on(
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import { rAF, cAF } from 'support/compatibility';
|
||||||
|
import { isFunction } from 'support/utils';
|
||||||
|
|
||||||
|
const { max } = Math;
|
||||||
|
const animationCurrentTime = () => performance.now();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* percent: current percent (0 - 1),
|
||||||
|
* time: current time (duration * percent),
|
||||||
|
* min: start value
|
||||||
|
* max: end value
|
||||||
|
* duration: duration in ms
|
||||||
|
*/
|
||||||
|
export type EasingFn = (
|
||||||
|
percent: number,
|
||||||
|
time: number,
|
||||||
|
min: number,
|
||||||
|
max: number,
|
||||||
|
duration: number
|
||||||
|
) => number;
|
||||||
|
|
||||||
|
export const animateNumber = (
|
||||||
|
from: number,
|
||||||
|
to: number,
|
||||||
|
duration: number,
|
||||||
|
onFrame: (progress: number, percent: number, completed: boolean) => any,
|
||||||
|
easing?: EasingFn | false
|
||||||
|
): ((complete?: boolean) => void) => {
|
||||||
|
let animationFrameId = 0;
|
||||||
|
const timeStart = animationCurrentTime();
|
||||||
|
const finalDuration = Math.max(0, duration);
|
||||||
|
const frame = (complete?: boolean) => {
|
||||||
|
const timeNow = animationCurrentTime();
|
||||||
|
const timeElapsed = timeNow - timeStart;
|
||||||
|
const stopAnimation = timeElapsed >= finalDuration;
|
||||||
|
const percent = complete
|
||||||
|
? 1
|
||||||
|
: 1 - (max(0, timeStart + finalDuration - timeNow) / finalDuration || 0);
|
||||||
|
const progress =
|
||||||
|
(to - from) *
|
||||||
|
(isFunction(easing)
|
||||||
|
? easing(percent, percent * finalDuration, 0, 1, finalDuration)
|
||||||
|
: percent) +
|
||||||
|
from;
|
||||||
|
const animationCompleted = stopAnimation || percent === 1;
|
||||||
|
|
||||||
|
onFrame && onFrame(progress, percent, animationCompleted);
|
||||||
|
|
||||||
|
animationFrameId = animationCompleted ? 0 : rAF!(() => frame());
|
||||||
|
};
|
||||||
|
frame();
|
||||||
|
return (complete) => {
|
||||||
|
cAF!(animationFrameId);
|
||||||
|
complete && frame(complete);
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
export * from 'support/dom/animation';
|
||||||
export * from 'support/dom/attribute';
|
export * from 'support/dom/attribute';
|
||||||
export * from 'support/dom/class';
|
export * from 'support/dom/class';
|
||||||
export * from 'support/dom/create';
|
export * from 'support/dom/create';
|
||||||
|
|||||||
@@ -9,9 +9,11 @@ jest.mock('support/compatibility/apis', () => {
|
|||||||
...originalModule,
|
...originalModule,
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
rAF: jest.fn().mockImplementation((...args) => mockRAF(...args)),
|
rAF: jest.fn().mockImplementation((...args) => mockRAF(...args)),
|
||||||
|
// @ts-ignore
|
||||||
cAF: jest.fn().mockImplementation((...args) => clearTimeout(...args)),
|
cAF: jest.fn().mockImplementation((...args) => clearTimeout(...args)),
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
setT: jest.fn().mockImplementation((...args) => setTimeout(...args)),
|
setT: jest.fn().mockImplementation((...args) => setTimeout(...args)),
|
||||||
|
// @ts-ignore
|
||||||
clearT: jest.fn().mockImplementation((...args) => clearTimeout(...args)),
|
clearT: jest.fn().mockImplementation((...args) => clearTimeout(...args)),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
+1
@@ -26,6 +26,7 @@ jest.mock('support/compatibility/apis', () => {
|
|||||||
...originalModule,
|
...originalModule,
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
setT: jest.fn().mockImplementation((...args) => setTimeout(...args)),
|
setT: jest.fn().mockImplementation((...args) => setTimeout(...args)),
|
||||||
|
// @ts-ignore
|
||||||
clearT: jest.fn().mockImplementation((...args) => clearTimeout(...args)),
|
clearT: jest.fn().mockImplementation((...args) => clearTimeout(...args)),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,187 @@
|
|||||||
|
import { animateNumber } from 'support/dom/animation';
|
||||||
|
|
||||||
|
jest.useFakeTimers();
|
||||||
|
|
||||||
|
jest.mock('support/compatibility/apis', () => {
|
||||||
|
const originalModule = jest.requireActual('support/compatibility/apis');
|
||||||
|
const mockRAF = (arg: any) => setTimeout(arg, 0);
|
||||||
|
return {
|
||||||
|
...originalModule,
|
||||||
|
// @ts-ignore
|
||||||
|
rAF: jest.fn().mockImplementation((...args) => mockRAF(...args)),
|
||||||
|
// @ts-ignore
|
||||||
|
cAF: jest.fn().mockImplementation((...args) => clearTimeout(...args)),
|
||||||
|
// @ts-ignore
|
||||||
|
setT: jest.fn().mockImplementation((...args) => setTimeout(...args)),
|
||||||
|
// @ts-ignore
|
||||||
|
clearT: jest.fn().mockImplementation((...args) => clearTimeout(...args)),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('dom animation', () => {
|
||||||
|
describe('animateNumber', () => {
|
||||||
|
test('animate 0 to 1', () => {
|
||||||
|
const onFrame = jest.fn();
|
||||||
|
expect(onFrame).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
animateNumber(0, 1, 500, onFrame);
|
||||||
|
|
||||||
|
expect(onFrame).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onFrame).toHaveBeenLastCalledWith(0, 0, false);
|
||||||
|
|
||||||
|
jest.advanceTimersByTime(250);
|
||||||
|
|
||||||
|
expect(onFrame).toHaveBeenCalledTimes(252);
|
||||||
|
expect(onFrame).toHaveBeenLastCalledWith(0.5, 0.5, false);
|
||||||
|
|
||||||
|
jest.advanceTimersByTime(250);
|
||||||
|
|
||||||
|
expect(onFrame).toHaveBeenCalledTimes(502);
|
||||||
|
expect(onFrame).toHaveBeenLastCalledWith(1, 1, true);
|
||||||
|
|
||||||
|
jest.runAllTimers();
|
||||||
|
|
||||||
|
expect(onFrame).toHaveBeenCalledTimes(502);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('animate 1 to 0', () => {
|
||||||
|
const onFrame = jest.fn();
|
||||||
|
expect(onFrame).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
animateNumber(1, 0, 1000, onFrame);
|
||||||
|
|
||||||
|
expect(onFrame).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onFrame).toHaveBeenLastCalledWith(1, 0, false);
|
||||||
|
|
||||||
|
jest.advanceTimersByTime(500);
|
||||||
|
|
||||||
|
expect(onFrame).toHaveBeenCalledTimes(502);
|
||||||
|
expect(onFrame).toHaveBeenLastCalledWith(0.5, 0.5, false);
|
||||||
|
|
||||||
|
jest.advanceTimersByTime(500);
|
||||||
|
|
||||||
|
expect(onFrame).toHaveBeenCalledTimes(1002);
|
||||||
|
expect(onFrame).toHaveBeenLastCalledWith(0, 1, true);
|
||||||
|
|
||||||
|
jest.runAllTimers();
|
||||||
|
|
||||||
|
expect(onFrame).toHaveBeenCalledTimes(1002);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('animate duration 0', () => {
|
||||||
|
const onFrame = jest.fn();
|
||||||
|
expect(onFrame).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
animateNumber(0, 100, 0, onFrame);
|
||||||
|
|
||||||
|
expect(onFrame).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onFrame).toHaveBeenLastCalledWith(100, 1, true);
|
||||||
|
|
||||||
|
jest.runAllTimers();
|
||||||
|
|
||||||
|
expect(onFrame).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('animate negative duration', () => {
|
||||||
|
const onFrame = jest.fn();
|
||||||
|
expect(onFrame).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
animateNumber(0, 100, -100, onFrame);
|
||||||
|
|
||||||
|
expect(onFrame).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onFrame).toHaveBeenLastCalledWith(100, 1, true);
|
||||||
|
|
||||||
|
jest.runAllTimers();
|
||||||
|
|
||||||
|
expect(onFrame).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('animate with easing and fractions', () => {
|
||||||
|
const onFrame = jest.fn();
|
||||||
|
const onFrameB = jest.fn();
|
||||||
|
expect(onFrame).not.toHaveBeenCalled();
|
||||||
|
expect(onFrameB).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
animateNumber(25.2, 55.5, 1000, onFrame, (percent) => percent);
|
||||||
|
animateNumber(25.2, 55.5, 1000, onFrameB);
|
||||||
|
|
||||||
|
expect(onFrame).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onFrameB).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onFrame).toHaveBeenLastCalledWith(25.2, 0, false);
|
||||||
|
expect(onFrameB).toHaveBeenLastCalledWith(25.2, 0, false);
|
||||||
|
|
||||||
|
jest.advanceTimersByTime(250);
|
||||||
|
|
||||||
|
expect(onFrame).toHaveBeenCalledTimes(252);
|
||||||
|
expect(onFrameB).toHaveBeenCalledTimes(252);
|
||||||
|
expect(onFrame).toHaveBeenLastCalledWith(32.775, 0.25, false);
|
||||||
|
expect(onFrameB).toHaveBeenLastCalledWith(32.775, 0.25, false);
|
||||||
|
|
||||||
|
jest.advanceTimersByTime(250);
|
||||||
|
|
||||||
|
expect(onFrame).toHaveBeenCalledTimes(502);
|
||||||
|
expect(onFrameB).toHaveBeenCalledTimes(502);
|
||||||
|
expect(onFrame).toHaveBeenLastCalledWith(40.35, 0.5, false);
|
||||||
|
expect(onFrameB).toHaveBeenLastCalledWith(40.35, 0.5, false);
|
||||||
|
|
||||||
|
jest.advanceTimersByTime(100);
|
||||||
|
|
||||||
|
expect(onFrame).toHaveBeenCalledTimes(602);
|
||||||
|
expect(onFrameB).toHaveBeenCalledTimes(602);
|
||||||
|
expect(onFrame).toHaveBeenLastCalledWith(43.379999999999995, 0.6, false);
|
||||||
|
expect(onFrameB).toHaveBeenLastCalledWith(43.379999999999995, 0.6, false);
|
||||||
|
|
||||||
|
jest.runAllTimers();
|
||||||
|
|
||||||
|
expect(onFrame).toHaveBeenCalledTimes(1002);
|
||||||
|
expect(onFrameB).toHaveBeenCalledTimes(1002);
|
||||||
|
expect(onFrame).toHaveBeenLastCalledWith(55.5, 1, true);
|
||||||
|
expect(onFrameB).toHaveBeenLastCalledWith(55.5, 1, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('animate and stop animation', () => {
|
||||||
|
const onFrame = jest.fn();
|
||||||
|
expect(onFrame).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
const stop = animateNumber(1, 0, 1000, onFrame);
|
||||||
|
|
||||||
|
expect(onFrame).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onFrame).toHaveBeenLastCalledWith(1, 0, false);
|
||||||
|
|
||||||
|
stop();
|
||||||
|
|
||||||
|
expect(onFrame).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
jest.advanceTimersByTime(500);
|
||||||
|
|
||||||
|
expect(onFrame).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
jest.runAllTimers();
|
||||||
|
|
||||||
|
expect(onFrame).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('animate and stop animation with complete', () => {
|
||||||
|
const onFrame = jest.fn();
|
||||||
|
expect(onFrame).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
const stop = animateNumber(0, 5555, 1000, onFrame);
|
||||||
|
|
||||||
|
expect(onFrame).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onFrame).toHaveBeenLastCalledWith(0, 0, false);
|
||||||
|
|
||||||
|
stop(true);
|
||||||
|
|
||||||
|
expect(onFrame).toHaveBeenCalledTimes(2);
|
||||||
|
expect(onFrame).toHaveBeenLastCalledWith(5555, 1, true);
|
||||||
|
|
||||||
|
jest.advanceTimersByTime(500);
|
||||||
|
|
||||||
|
expect(onFrame).toHaveBeenCalledTimes(2);
|
||||||
|
|
||||||
|
jest.runAllTimers();
|
||||||
|
|
||||||
|
expect(onFrame).toHaveBeenCalledTimes(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -10,9 +10,11 @@ jest.mock('support/compatibility/apis', () => {
|
|||||||
...originalModule,
|
...originalModule,
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
rAF: jest.fn().mockImplementation((...args) => mockRAF(...args)),
|
rAF: jest.fn().mockImplementation((...args) => mockRAF(...args)),
|
||||||
|
// @ts-ignore
|
||||||
cAF: jest.fn().mockImplementation((...args) => clearTimeout(...args)),
|
cAF: jest.fn().mockImplementation((...args) => clearTimeout(...args)),
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
setT: jest.fn().mockImplementation((...args) => setTimeout(...args)),
|
setT: jest.fn().mockImplementation((...args) => setTimeout(...args)),
|
||||||
|
// @ts-ignore
|
||||||
clearT: jest.fn().mockImplementation((...args) => clearTimeout(...args)),
|
clearT: jest.fn().mockImplementation((...args) => clearTimeout(...args)),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ const normalizePath = (pathName) =>
|
|||||||
|
|
||||||
const fixturesDir = path.join(__dirname, '.fixtures');
|
const fixturesDir = path.join(__dirname, '.fixtures');
|
||||||
const libraryFixturePath = normalizePath(path.join(fixturesDir, 'lib.js'));
|
const libraryFixturePath = normalizePath(path.join(fixturesDir, 'lib.js'));
|
||||||
const unshakedFixturePath = normalizePath(path.join(fixturesDir, 'unshaked.js'));
|
const normalFixturePath = normalizePath(path.join(fixturesDir, 'nromal.js'));
|
||||||
const shakedFixturePath = normalizePath(path.join(fixturesDir, 'shaked.js'));
|
const treeshakedFixturePath = normalizePath(path.join(fixturesDir, 'treeshaked.js'));
|
||||||
|
|
||||||
const unshakedFixtureContent = `
|
const unshakedFixtureContent = `
|
||||||
export * as os from '${libraryFixturePath}';
|
export * as os from '${libraryFixturePath}';
|
||||||
@@ -89,18 +89,24 @@ const bundleFunctions = {
|
|||||||
const testBundler = (bundlerName) => async () => {
|
const testBundler = (bundlerName) => async () => {
|
||||||
const bundleFunction = bundleFunctions[bundlerName];
|
const bundleFunction = bundleFunctions[bundlerName];
|
||||||
const outputDir = path.join(__dirname, `.${bundlerName}`);
|
const outputDir = path.join(__dirname, `.${bundlerName}`);
|
||||||
const unshaked = await bundleFunction(unshakedFixturePath, path.join(outputDir, 'unshaked.js'));
|
const normal = await bundleFunction(
|
||||||
const shaked = await bundleFunction(shakedFixturePath, path.join(outputDir, 'shaked.js'));
|
normalFixturePath,
|
||||||
|
path.join(outputDir, path.basename(normalFixturePath))
|
||||||
|
);
|
||||||
|
const treeshaked = await bundleFunction(
|
||||||
|
treeshakedFixturePath,
|
||||||
|
path.join(outputDir, path.basename(treeshakedFixturePath))
|
||||||
|
);
|
||||||
|
|
||||||
cleanBundle && fs.rmSync(outputDir, { recursive: true });
|
cleanBundle && fs.rmSync(outputDir, { recursive: true });
|
||||||
|
|
||||||
console.info(`${bundlerName} size`, {
|
console.info(`${bundlerName} size`, {
|
||||||
unshaked,
|
normal,
|
||||||
shaked,
|
treeshaked,
|
||||||
diff: unshaked - shaked,
|
diff: normal - treeshaked,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(unshaked - shaked).toBeGreaterThan(expectedBundleDiff);
|
expect(normal - treeshaked).toBeGreaterThan(expectedBundleDiff);
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('tree shaking', () => {
|
describe('tree shaking', () => {
|
||||||
@@ -131,8 +137,8 @@ describe('tree shaking', () => {
|
|||||||
fs.mkdirSync(fixturesDir);
|
fs.mkdirSync(fixturesDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.writeFileSync(unshakedFixturePath, unshakedFixtureContent);
|
fs.writeFileSync(normalFixturePath, unshakedFixtureContent);
|
||||||
fs.writeFileSync(shakedFixturePath, shakedFixtureContent);
|
fs.writeFileSync(treeshakedFixturePath, shakedFixtureContent);
|
||||||
}, 60000 * 2);
|
}, 60000 * 2);
|
||||||
|
|
||||||
// clean the fixture
|
// clean the fixture
|
||||||
|
|||||||
+3
-1
@@ -32,7 +32,9 @@ import {
|
|||||||
} from 'support';
|
} from 'support';
|
||||||
import { Options } from 'options';
|
import { Options } from 'options';
|
||||||
import { DeepPartial } from 'typings';
|
import { DeepPartial } from 'typings';
|
||||||
import { addPlugin, scrollbarsHidingPlugin, sizeObserverPlugin } from 'plugins';
|
import { addPlugin, scrollbarsHidingPlugin, sizeObserverPlugin, clickScrollPlugin } from 'plugins';
|
||||||
|
|
||||||
|
addPlugin(clickScrollPlugin);
|
||||||
|
|
||||||
if (!window.ResizeObserver) {
|
if (!window.ResizeObserver) {
|
||||||
addPlugin(sizeObserverPlugin);
|
addPlugin(sizeObserverPlugin);
|
||||||
|
|||||||
Reference in New Issue
Block a user