UNPKG

smooth-scrollbar

Version:

Customize scrollbar in modern browsers with smooth scrolling experience.

119 lines (91 loc) 2.86 kB
import clamp from 'lodash.clamp'; import * as I from '../interfaces/'; import { eventScope, getPosition, } from '../utils/'; export function selectHandler(scrollbar: I.Scrollbar) { const addEvent = eventScope(scrollbar); const { containerEl, contentEl } = scrollbar; let isSelected = false; let isContextMenuOpened = false; // flag to prevent selection when context menu is opened let animationID: number; function scroll({ x, y }) { if (!x && !y) return; const { offset, limit } = scrollbar; // DISALLOW delta transformation scrollbar.setMomentum( clamp(offset.x + x, 0, limit.x) - offset.x, clamp(offset.y + y, 0, limit.y) - offset.y, ); animationID = requestAnimationFrame(() => { scroll({ x, y }); }); } addEvent(window, 'mousemove', (evt: MouseEvent) => { if (!isSelected) return; cancelAnimationFrame(animationID); const dir = calcMomentum(scrollbar, evt); scroll(dir); }); // prevent scrolling when context menu is opened // NOTE: `contextmenu` event may be fired // 1. BEFORE `selectstart`: when user right-clicks on the text content -> prevent future scrolling, // 2. AFTER `selectstart`: when user right-clicks on the blank area -> cancel current scrolling, // so we need to both set the flag and cancel current scrolling addEvent(contentEl, 'contextmenu', () => { // set the flag to prevent future scrolling isContextMenuOpened = true; // stop current scrolling cancelAnimationFrame(animationID); isSelected = false; }); // reset context menu flag on mouse down // to ensure the scrolling is allowed in the next selection addEvent(contentEl, 'mousedown', () => { isContextMenuOpened = false; }); addEvent(contentEl, 'selectstart', () => { if (isContextMenuOpened) { return; } cancelAnimationFrame(animationID); isSelected = true; }); addEvent(window, 'mouseup blur', () => { cancelAnimationFrame(animationID); isSelected = false; isContextMenuOpened = false; }); // patch for touch devices addEvent(containerEl, 'scroll', (evt: Event) => { evt.preventDefault(); containerEl.scrollTop = containerEl.scrollLeft = 0; }); } function calcMomentum( scrollbar: I.Scrollbar, evt: MouseEvent, ) { const { top, right, bottom, left } = scrollbar.bounding; const { x, y } = getPosition(evt); const res = { x: 0, y: 0, }; const padding = 20; if (x === 0 && y === 0) return res; if (x > right - padding) { res.x = (x - right + padding); } else if (x < left + padding) { res.x = (x - left - padding); } if (y > bottom - padding) { res.y = (y - bottom + padding); } else if (y < top + padding) { res.y = (y - top - padding); } res.x *= 2; res.y *= 2; return res; }