smooth-scrollbar
Version:
Customize scrollbar in modern browsers with smooth scrolling experience.
142 lines (110 loc) • 3.87 kB
text/typescript
import clamp from 'lodash.clamp';
import * as I from '../interfaces/';
import {
isOneOf,
getPosition,
eventScope,
setStyle,
} from '../utils/';
enum Direction { X, Y }
export function mouseHandler(scrollbar: I.Scrollbar) {
const addEvent = eventScope(scrollbar);
const container = scrollbar.containerEl;
const { xAxis, yAxis } = scrollbar.track;
function calcMomentum(
direction: Direction,
clickPosition: number,
): number {
const {
size,
limit,
offset,
} = scrollbar;
if (direction === Direction.X) {
const totalWidth = size.container.width + (xAxis.thumb.realSize - xAxis.thumb.displaySize);
return clamp(clickPosition / totalWidth * size.content.width, 0, limit.x) - offset.x;
}
if (direction === Direction.Y) {
const totalHeight = size.container.height + (yAxis.thumb.realSize - yAxis.thumb.displaySize);
return clamp(clickPosition / totalHeight * size.content.height, 0, limit.y) - offset.y;
}
return 0;
}
function getTrackDirection(
elem: HTMLElement,
): Direction | undefined {
if (isOneOf(elem, [xAxis.element, xAxis.thumb.element])) {
return Direction.X;
}
if (isOneOf(elem, [yAxis.element, yAxis.thumb.element])) {
return Direction.Y;
}
return void 0;
}
let isMouseDown: boolean;
let isMouseMoving: boolean;
let startOffsetToThumb: { x: number, y: number };
let trackDirection: Direction | undefined;
let containerRect: ClientRect;
addEvent(container, 'click', (evt: MouseEvent) => {
if (isMouseMoving || !isOneOf(evt.target, [xAxis.element, yAxis.element])) {
return;
}
const track = evt.target as HTMLElement;
const direction = getTrackDirection(track);
const rect = track.getBoundingClientRect();
const clickPos = getPosition(evt);
if (direction === Direction.X) {
const offsetOnTrack = clickPos.x - rect.left - xAxis.thumb.displaySize / 2;
scrollbar.setMomentum(calcMomentum(direction, offsetOnTrack), 0);
}
if (direction === Direction.Y) {
const offsetOnTrack = clickPos.y - rect.top - yAxis.thumb.displaySize / 2;
scrollbar.setMomentum(0, calcMomentum(direction, offsetOnTrack));
}
});
addEvent(container, 'mousedown', (evt: MouseEvent) => {
if (!isOneOf(evt.target, [xAxis.thumb.element, yAxis.thumb.element])) {
return;
}
isMouseDown = true;
const thumb = evt.target as HTMLElement;
const cursorPos = getPosition(evt);
const thumbRect = thumb.getBoundingClientRect();
trackDirection = getTrackDirection(thumb);
// pointer offset to thumb
startOffsetToThumb = {
x: cursorPos.x - thumbRect.left,
y: cursorPos.y - thumbRect.top,
};
// container bounding rectangle
containerRect = container.getBoundingClientRect();
// prevent selection, see:
// https://github.com/idiotWu/smooth-scrollbar/issues/48
setStyle(scrollbar.containerEl, {
'-user-select': 'none',
});
});
addEvent(window, 'mousemove', (evt) => {
if (!isMouseDown) return;
isMouseMoving = true;
const cursorPos = getPosition(evt);
if (trackDirection === Direction.X) {
// get percentage of pointer position in track
// then tranform to px
// don't need easing
const offsetOnTrack = cursorPos.x - startOffsetToThumb.x - containerRect.left;
scrollbar.setMomentum(calcMomentum(trackDirection, offsetOnTrack), 0);
}
if (trackDirection === Direction.Y) {
const offsetOnTrack = cursorPos.y - startOffsetToThumb.y - containerRect.top;
scrollbar.setMomentum(0, calcMomentum(trackDirection, offsetOnTrack));
}
});
addEvent(window, 'mouseup blur', () => {
isMouseDown = isMouseMoving = false;
setStyle(scrollbar.containerEl, {
'-user-select': '',
});
});
}