/** * throttle 函数节流 * @since 1.0.0 * @example 适用于限制`resize`和`scroll`等函数的调用频率 * @param {Number} delay 0 或者更大的毫秒数。 对于事件回调,大约100或250毫秒(或更高)的延迟是最有用的。 * @param {Boolean} noTrailing 可选,默认为false。 * 如果noTrailing为true,当节流函数被调用,每过`delay`毫秒`callback`也将执行一次。 * 如果noTrailing为false或者未传入,`callback`将在最后一次调用节流函数后再执行一次. * (延迟`delay`毫秒之后,节流函数没有被调用,内部计数器会复位) * @param {Function} callback 延迟毫秒后执行的函数。`this`上下文和所有参数都是按原样传递的, * 执行去节流功能时,调用`callback`。 * @param {Boolean} debounceMode 如果`debounceMode`为true,`clear`在`delay`ms后执行。 * 如果debounceMode是false,`callback`在`delay` ms之后执行。 * @return {Function} 新的节流函数 */ export function throttle(delay, noTrailing, callback, debounceMode) { // After wrapper has stopped being called, this timeout ensures that // `callback` is executed at the proper times in `throttle` and `end` // debounce modes. let timeoutID; // Keep track of the last time `callback` was executed. let lastExec = 0; // `noTrailing` defaults to falsy. if (typeof noTrailing !== 'boolean') { debounceMode = callback; callback = noTrailing; noTrailing = undefined; } // The `wrapper` function encapsulates all of the throttling / debouncing // functionality and when executed will limit the rate at which `callback` // is executed. function wrapper() { let self = this; let elapsed = Number(new Date()) - lastExec; let args = arguments; // Execute `callback` and update the `lastExec` timestamp. function exec() { lastExec = Number(new Date()); callback.apply(self, args); } // If `debounceMode` is true (at begin) this is used to clear the flag // to allow future `callback` executions. function clear() { timeoutID = undefined; } if (debounceMode && !timeoutID) { // Since `wrapper` is being called for the first time and // `debounceMode` is true (at begin), execute `callback`. exec(); } // Clear any existing timeout. if (timeoutID) { clearTimeout(timeoutID); } if (debounceMode === undefined && elapsed > delay) { // In throttle mode, if `delay` time has been exceeded, execute // `callback`. exec(); } else if (noTrailing !== true) { // In trailing throttle mode, since `delay` time has not been // exceeded, schedule `callback` to execute `delay` ms after most // recent execution. // // If `debounceMode` is true (at begin), schedule `clear` to execute // after `delay` ms. // // If `debounceMode` is false (at end), schedule `callback` to // execute after `delay` ms. timeoutID = setTimeout(debounceMode ? clear : exec, debounceMode === undefined ? delay - elapsed : delay); } } // Return the wrapper function. return wrapper; } /** * debounce 函数防抖 * 与throttle不同的是,debounce保证一个函数在多少毫秒内不再被触发,只会执行一次, * 要么在第一次调用return的防抖函数时执行,要么在延迟指定毫秒后调用。 * @since 1.0.0 * @example 适用场景:如在线编辑的自动存储防抖。 * @param {Number} delay 0或者更大的毫秒数。 对于事件回调,大约100或250毫秒(或更高)的延迟是最有用的。 * @param {Boolean} atBegin 可选,默认为false。 * 如果`atBegin`为false或未传入,回调函数则在第一次调用return的防抖函数后延迟指定毫秒调用。 * 如果`atBegin`为true,回调函数则在第一次调用return的防抖函数时直接执行 * @param {Function} callback 延迟毫秒后执行的函数。`this`上下文和所有参数都是按原样传递的, * 执行去抖动功能时,,调用`callback`。 * @return {Function} 新的防抖函数。 */ export function debounce(delay, atBegin, callback) { return callback === undefined ? throttle(delay, atBegin, false) : throttle(delay, callback, atBegin !== false); } /** * formatTime 格式化时间 * @param time * @param format * @returns {string|null} */ export function formatTime(time, format) { time = typeof (time) === "number" ? time : (time instanceof Date ? time.getTime() : parseInt(time)); if (isNaN(time)) return null; if (typeof (format) !== 'string' || !format) format = 'yyyy-MM-dd hh:mm:ss'; let _time = new Date(time); time = _time.toString().split(/[\s\:]/g).slice(0, -2); time[1] = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'][_time.getMonth()]; let _mapping = { MM: 1, dd: 2, yyyy: 3, hh: 4, mm: 5, ss: 6 }; return format.replace(/([Mmdhs]|y{2})\1/g, (key) => time[_mapping[key]]); } /** * isLocalNetwork 判断是否是本机或局域网IP * @param ip * @returns {boolean} */ export function isLocalNetwork(ip) { const localNetworkRanges = [ '192.168.', '10.', '127.', /^172\.((1[6-9]|2[0-9]|3[0-1])\.)/ ]; return localNetworkRanges.some(range => { if (typeof range === 'string') { return ip.startsWith(range); } else { return range.test(ip); } }); } export function on( element, event, handler ) { if (element && event && handler) { return element.addEventListener(event, handler, false); } } export function off( element, event, handler = _=>{} ) { if (element && event) { return element.removeEventListener(event, handler, false); } } export const isMac = /Mac|iPod|iPhone|iPad/.test(navigator.platform) export const normalize = str => isMac ? str.replace(/Ctrl/g, '⌘').replace(/Alt/g, '⌥').replace(/Shift/g, '⇧') : str /** * compareVersion 比较两个版本号 * @param {string} version1 - 版本号1,格式为 x.x.x * @param {string} version2 - 版本号2,格式为 x.x.x * @returns {number} - 如果version1 > version2返回1,如果version1 < version2返回-1,相等返回0 */ export function compareVersion(version1, version2) { const v1 = version1.split('.').map(Number); const v2 = version2.split('.').map(Number); for (let i = 0; i < Math.max(v1.length, v2.length); i++) { const num1 = i < v1.length ? v1[i] : 0; const num2 = i < v2.length ? v2[i] : 0; if (num1 > num2) return 1; if (num1 < num2) return -1; } return 0; }