mirror of
https://github.com/tenrok/OverlayScrollbars.git
synced 2026-06-22 13:30:35 +03:00
fix floating point scrollSize browser rounding
This commit is contained in:
@@ -12,7 +12,8 @@ import {
|
|||||||
removeElements,
|
removeElements,
|
||||||
windowSize,
|
windowSize,
|
||||||
runEach,
|
runEach,
|
||||||
equalWH,
|
equalBCRWH,
|
||||||
|
getBoundingClientRect,
|
||||||
} from 'support';
|
} from 'support';
|
||||||
import {
|
import {
|
||||||
classNameEnvironment,
|
classNameEnvironment,
|
||||||
@@ -87,14 +88,14 @@ const getRtlScrollBehavior = (parentElm: HTMLElement, childElm: HTMLElement): {
|
|||||||
|
|
||||||
const getFlexboxGlue = (parentElm: HTMLElement, childElm: HTMLElement): boolean => {
|
const getFlexboxGlue = (parentElm: HTMLElement, childElm: HTMLElement): boolean => {
|
||||||
addClass(parentElm, classNameEnvironmentFlexboxGlue);
|
addClass(parentElm, classNameEnvironmentFlexboxGlue);
|
||||||
const minOffsetsizeParent = offsetSize(parentElm);
|
const minOffsetsizeParent = getBoundingClientRect(parentElm);
|
||||||
const minOffsetsize = offsetSize(childElm);
|
const minOffsetsize = getBoundingClientRect(childElm);
|
||||||
const supportsMin = equalWH(minOffsetsize, minOffsetsizeParent);
|
const supportsMin = equalBCRWH(minOffsetsize, minOffsetsizeParent, true);
|
||||||
|
|
||||||
addClass(parentElm, classNameEnvironmentFlexboxGlueMax);
|
addClass(parentElm, classNameEnvironmentFlexboxGlueMax);
|
||||||
const maxOffsetsizeParent = offsetSize(parentElm);
|
const maxOffsetsizeParent = getBoundingClientRect(parentElm);
|
||||||
const maxOffsetsize = offsetSize(childElm);
|
const maxOffsetsize = getBoundingClientRect(childElm);
|
||||||
const supportsMax = equalWH(maxOffsetsize, maxOffsetsizeParent);
|
const supportsMax = equalBCRWH(maxOffsetsize, maxOffsetsizeParent, true);
|
||||||
|
|
||||||
return supportsMin && supportsMax;
|
return supportsMin && supportsMax;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import {
|
|||||||
addClass,
|
addClass,
|
||||||
removeClass,
|
removeClass,
|
||||||
clientSize,
|
clientSize,
|
||||||
|
offsetSize,
|
||||||
|
getBoundingClientRect,
|
||||||
} from 'support';
|
} from 'support';
|
||||||
import { LifecycleHub, Lifecycle } from 'lifecycles/lifecycleHub';
|
import { LifecycleHub, Lifecycle } from 'lifecycles/lifecycleHub';
|
||||||
import { getEnvironment } from 'environment';
|
import { getEnvironment } from 'environment';
|
||||||
@@ -21,6 +23,10 @@ import { classNameViewportScrollbarStyling } from 'classnames';
|
|||||||
|
|
||||||
const overlaidScrollbarsHideOffset = 42;
|
const overlaidScrollbarsHideOffset = 42;
|
||||||
const overlaidScrollbarsHideBorderStyle = `${overlaidScrollbarsHideOffset}px solid transparent`;
|
const overlaidScrollbarsHideBorderStyle = `${overlaidScrollbarsHideOffset}px solid transparent`;
|
||||||
|
interface ContentScrollSizeCacheContext {
|
||||||
|
_viewportSize: WH<number>;
|
||||||
|
_viewportScrollSize: WH<number>;
|
||||||
|
}
|
||||||
interface OverflowAmountCacheContext {
|
interface OverflowAmountCacheContext {
|
||||||
_contentScrollSize: WH<number>;
|
_contentScrollSize: WH<number>;
|
||||||
_viewportSize: WH<number>;
|
_viewportSize: WH<number>;
|
||||||
@@ -29,26 +35,48 @@ interface OverflowAmountCacheContext {
|
|||||||
export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle => {
|
export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle => {
|
||||||
const { _structureSetup, _getPaddingStyle } = lifecycleHub;
|
const { _structureSetup, _getPaddingStyle } = lifecycleHub;
|
||||||
const { _host, _padding, _viewport, _content, _contentArrange } = _structureSetup._targetObj;
|
const { _host, _padding, _viewport, _content, _contentArrange } = _structureSetup._targetObj;
|
||||||
const { _update: updateContentScrollSizeCache, _current: getCurrentContentScrollSizeCache } = createCache<WH<number>>(
|
const { _update: updateContentScrollSizeCache, _current: getCurrentContentScrollSizeCache } = createCache<
|
||||||
() => scrollSize(_content || _viewport),
|
WH<number>,
|
||||||
|
ContentScrollSizeCacheContext
|
||||||
|
>(
|
||||||
|
(ctx) => {
|
||||||
|
const { _viewportSize, _viewportScrollSize } = ctx;
|
||||||
|
const contentViewportScrollSize = scrollSize(_content || _viewport);
|
||||||
|
|
||||||
|
return _content ? fixScrollSizeRounding(contentViewportScrollSize, _viewportSize, _viewportScrollSize) : contentViewportScrollSize;
|
||||||
|
},
|
||||||
{ _equal: equalWH }
|
{ _equal: equalWH }
|
||||||
);
|
);
|
||||||
const { _update: updateOverflowAmountCache, _current: getCurrentOverflowAmountCache } = createCache<XY<number>, OverflowAmountCacheContext>(
|
const { _update: updateOverflowAmountCache, _current: getCurrentOverflowAmountCache } = createCache<XY<number>, OverflowAmountCacheContext>(
|
||||||
(ctx) => ({
|
(ctx) => ({
|
||||||
x: Math.max(0, Math.round((ctx._contentScrollSize.w - ctx._viewportSize.w) * 100) / 100),
|
x: Math.max(0, ctx._contentScrollSize.w - ctx._viewportSize.w),
|
||||||
y: Math.max(0, Math.round((ctx._contentScrollSize.h - ctx._viewportSize.h) * 100) / 100),
|
y: Math.max(0, ctx._contentScrollSize.h - ctx._viewportSize.h),
|
||||||
}),
|
}),
|
||||||
{ _equal: equalXY }
|
{ _equal: equalXY }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const fixScrollSizeRounding = (contentScrollSize: WH<number>, viewportSize: WH<number>, viewportScrollSize: WH<number>): WH<number> => {
|
||||||
|
const equalViewportSizes = viewportSize.w === viewportScrollSize.w || viewportSize.h === viewportScrollSize.h;
|
||||||
|
const contentViewportScrollSizeOverflow = contentScrollSize.w > viewportScrollSize.w || contentScrollSize.h > viewportScrollSize.h;
|
||||||
|
|
||||||
|
if (equalViewportSizes && contentViewportScrollSizeOverflow) {
|
||||||
|
const viewportRect = getBoundingClientRect(_viewport);
|
||||||
|
const viewportOffsetSize = offsetSize(_viewport);
|
||||||
|
|
||||||
|
return {
|
||||||
|
w: contentScrollSize.w - Math.ceil(Math.max(0, viewportRect.width - viewportOffsetSize.w)),
|
||||||
|
h: contentScrollSize.h - Math.ceil(Math.max(0, viewportRect.height - viewportOffsetSize.h)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return contentScrollSize;
|
||||||
|
};
|
||||||
|
|
||||||
const setViewportOverflowStyle = (horizontal: boolean, amount: number, behavior: OverflowBehavior, styleObj: StyleObject) => {
|
const setViewportOverflowStyle = (horizontal: boolean, amount: number, behavior: OverflowBehavior, styleObj: StyleObject) => {
|
||||||
const overflowKey = horizontal ? 'overflowX' : 'overflowY';
|
const overflowKey = horizontal ? 'overflowX' : 'overflowY';
|
||||||
//const scrollMaxKey = horizontal ? 'scrollLeftMax' : 'scrollTopMax';
|
|
||||||
const behaviorIsScroll = behavior === 'scroll';
|
const behaviorIsScroll = behavior === 'scroll';
|
||||||
const behaviorIsVisibleScroll = behavior === 'visible-scroll';
|
const behaviorIsVisibleScroll = behavior === 'visible-scroll';
|
||||||
const hideOverflow = behaviorIsScroll || behavior === 'hidden';
|
const hideOverflow = behaviorIsScroll || behavior === 'hidden';
|
||||||
//const scrollMax = _viewport[scrollMaxKey];
|
|
||||||
//const scrollMaxOverflow = isNumber(scrollMax) ? scrollMax > 0 : true;
|
|
||||||
const applyStyle = amount > 0 && hideOverflow;
|
const applyStyle = amount > 0 && hideOverflow;
|
||||||
|
|
||||||
if (applyStyle) {
|
if (applyStyle) {
|
||||||
@@ -156,7 +184,11 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
|
|||||||
const contentClientSize = clientSize(_content || _viewport); // needs to be client Size because applied border for content arrange on content
|
const contentClientSize = clientSize(_content || _viewport); // needs to be client Size because applied border for content arrange on content
|
||||||
const contentArrangeOffsetSize = clientSize(_contentArrange); // can be offset size aswell
|
const contentArrangeOffsetSize = clientSize(_contentArrange); // can be offset size aswell
|
||||||
|
|
||||||
contentScrollSizeCache = updateContentScrollSizeCache(force);
|
contentScrollSizeCache = updateContentScrollSizeCache(force, {
|
||||||
|
_viewportSize: viewportSize,
|
||||||
|
_viewportScrollSize: viewportScrollSize,
|
||||||
|
});
|
||||||
|
|
||||||
const { _value: contentScrollSize } = contentScrollSizeCache;
|
const { _value: contentScrollSize } = contentScrollSizeCache;
|
||||||
overflowAmuntCache = updateOverflowAmountCache(force, {
|
overflowAmuntCache = updateOverflowAmountCache(force, {
|
||||||
_contentScrollSize: {
|
_contentScrollSize: {
|
||||||
@@ -230,8 +262,8 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
|
|||||||
// TODO: Test without content
|
// TODO: Test without content
|
||||||
// TODO: Test without padding
|
// TODO: Test without padding
|
||||||
// TODO: hide host || padding overflow if scroll x or y
|
// TODO: hide host || padding overflow if scroll x or y
|
||||||
// TODO: fix false overflow bug (fractal scroll size)
|
|
||||||
// TODO: add trinsic lifecycle
|
// TODO: add trinsic lifecycle
|
||||||
|
// TODO: IE max-width fix not always working
|
||||||
// TODO: remove lifecycleHub get set padding if not needed
|
// TODO: remove lifecycleHub get set padding if not needed
|
||||||
|
|
||||||
style(_viewport, viewportStyle);
|
style(_viewport, viewportStyle);
|
||||||
|
|||||||
@@ -8,11 +8,18 @@ import { PlainObject } from 'typings';
|
|||||||
* @param b Object b.
|
* @param b Object b.
|
||||||
* @param props The props which shall be compared.
|
* @param props The props which shall be compared.
|
||||||
*/
|
*/
|
||||||
export const equal = <T extends PlainObject>(a: T | undefined, b: T | undefined, props: Array<keyof T>): boolean => {
|
export const equal = <T extends PlainObject>(
|
||||||
|
a: T | undefined,
|
||||||
|
b: T | undefined,
|
||||||
|
props: Array<keyof T>,
|
||||||
|
propMutation?: ((value: any) => any) | null | false
|
||||||
|
): boolean => {
|
||||||
if (a && b) {
|
if (a && b) {
|
||||||
let result = true;
|
let result = true;
|
||||||
each(props, (prop) => {
|
each(props, (prop) => {
|
||||||
if (a[prop] !== b[prop]) {
|
const compareA = propMutation ? propMutation(a[prop]) : a[prop];
|
||||||
|
const compareB = propMutation ? propMutation(b[prop]) : b[prop];
|
||||||
|
if (compareA !== compareB) {
|
||||||
result = false;
|
result = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -44,3 +51,13 @@ export const equalXY = (a?: XY, b?: XY) => equal<XY>(a, b, ['x', 'y']);
|
|||||||
* @param b Object b.
|
* @param b Object b.
|
||||||
*/
|
*/
|
||||||
export const equalTRBL = (a?: TRBL, b?: TRBL) => equal<TRBL>(a, b, ['t', 'r', 'b', 'l']);
|
export const equalTRBL = (a?: TRBL, b?: TRBL) => equal<TRBL>(a, b, ['t', 'r', 'b', 'l']);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares two DOM Rects for their equality of their width and height properties
|
||||||
|
* Also returns false if one of the DOM Rects is undefined or null.
|
||||||
|
* @param a DOM Rect a.
|
||||||
|
* @param b DOM Rect b.
|
||||||
|
* @param round Whether the values should be rounded.
|
||||||
|
*/
|
||||||
|
export const equalBCRWH = (a?: DOMRect, b?: DOMRect, round?: boolean) =>
|
||||||
|
equal<DOMRect>(a, b, ['width', 'height'], round && ((value) => Math.round(value)));
|
||||||
|
|||||||
Reference in New Issue
Block a user