/** * Throttle function */ export const throttle = (func: Function, timeout: number) => { let ready: boolean = true return (...args: any) => { if (!ready) return ready = false func(...args) setTimeout(() => ready = true, timeout) } } /** * Debounce function */ export const debounce = (func: Function, timeout: number) => { let timer: NodeJS.Timeout return (...args: any) => { clearTimeout(timer) timer = setTimeout(() => func(...args), timeout) } } /** * Split text * @description Split a string into words or characters * @returns string[] */ export const splitText = (text: string, mode: string = 'words'): string[] => { // Split by words if (mode === 'words') { const words = text .replace(/\\n/g, '\n') .replace(/\s+/g, m => m.includes('\n') ? '\n ' : ' ') .trim() .split(' ') return words } // Split by chars else if (mode === 'chars') { const chars = Array.from(text).map(char => char === ' ' ? '\xa0' : char) return chars } } /** * Capitalize first letter */ export const capitalizeFirstLetter = (string: string) => { return string[0].toUpperCase() + string.slice(1) } /** * Linear Interpolation */ export const lerp = (start: number, end: number, amount: number): number => { return (1 - amount) * start + amount * end } /** * Re-maps a number from one range to another * @param value the incoming value to be converted * @param start1 lower bound of the value's current range * @param stop1 upper bound of the value's current range * @param start2 lower bound of the value's target range * @param stop2 upper bound of the value's target range * @param [withinBounds] constrain the value to the newly mapped range * @return remapped number */ export const map = (n: number, start1: number, stop1: number, start2: number, stop2: number, withinBounds: boolean): number => { const value = (n - start1) / (stop1 - start1) * (stop2 - start2) + start2 if (!withinBounds) return value if (start2 < stop2) { return clamp(value, start2, stop2) } else { return clamp(value, stop2, start2) } } /** * Clamp a number */ export const clamp = (num: number, a: number, b: number) => { return Math.max(Math.min(num, Math.max(a, b)), Math.min(a, b)) } /** * Return a random element from an array */ export const getRandomElement = (array: any[]): any => { return ~~(array.length * Math.random()) } /** * Get a DOM element's position */ export const getPosition = (node, scope?: HTMLElement) => { const root = scope || document let offsetTop = node.offsetTop let offsetLeft = node.offsetLeft while (node && node.offsetParent && node.offsetParent != document && node !== root && root !== node.offsetParent) { offsetTop += node.offsetParent.offsetTop offsetLeft += node.offsetParent.offsetLeft node = node.offsetParent } return { top: offsetTop, left: offsetLeft } }