UNPKG

xd-tools

Version:

封装常用工具类

586 lines (536 loc) 16.2 kB
// import qs from "qs" /** * 数据类型判断 */ const checkConentType = type => { return content => { return Object.prototype.toString.call(content) === `[object ${type}]` } } /** * 判断是否是数组 */ const isArray = checkConentType('Array') /** * 判断是否是对象 */ const isObject = checkConentType('Object') /** * 判断是否是数字 */ const isNumber = checkConentType('Number') /** * 判断是否是字符串 */ const isString = checkConentType('String') /** * 判断是否是布尔值 */ const isBoolean = checkConentType('Boolean') /** * 生成随机混合数 * @param {Number} * @return {String} */ const generateRandomMixed = (n = 10) => { const charts = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'] let res = '' for (let i = 0; i < n; i++) { let id = Math.ceil(Math.random() * 35) res += charts[id] } return res } /** * 获取两个值的中间的随机数 * @param {Number} min 最小值 * @param {Number} max 最大值 * @returns */ const generateRandomInteger = (min, max = 0) => { if (min > max) { let temp = min; min = max; max = temp; } var Range = max - min; var Rand = Math.random(); return (Min + Math.round(Rand * Range)); } /** * 设置 localStorage * @param {String} name key * @param {Any} content value * @returns */ const setState = (name,content) => { if(!name) return if(!isString(content)) { content = JSON.stringify(content) } window.localStorage.setItem(name,content) } /** * 获取localStorage */ const getStore = name => { if (!name) return; return window.localStorage.getItem(name); } /** * 删除localStorage */ const removeStore = name => { if (!name) return; window.localStorage.removeItem(name); } /** * 将一个数组几个一组切割成一个新的数组 * @param {Array} arr 数据 * @param {Number} subLength 几个一组 * @returns */ const splitArray = (arr, subLength = 0) => { if(!isArray(arr)) { return } let index = 0 let newArray = [] while(index < arr.length) { newArray.push(arr.slice(index, (index += subLength))) return newArray } } /** * 跳转URL拼接参数 * @param {String} url * @param {Object} params */ const params2url = (url = '', params = {}) => { if(!params) { return } return url + '?' + qs.stringify(params, { arrayFormat: 'repeat' }) } /** * 获取 URL 链接 中所有的参数 * @param {String} url * @returns */ const getQueryObject = url => { url = !url ? window.location.href : url let search = url.substring(url.lastIndexOf('?') + 1) return qs.parse(search) } /** * 去除字符串中的空格, 默认去除前后空格 * @param {String} str * @param {Number} type 1-所有空格 2-前后空格 3-前空格 4-后空格 * @returns */ const trim = (str, type = 2) => { if(!isString(str)) { return console.log('参数【str】不是字符串') } switch (type) { case 1: return str.replace(/\s+/g, '') case 2: return str.replace(/(^\s*)|(\s*$)/g, '') case 3: return str.replace(/(^\s*)/g, '') case 4: return str.replace(/(\s*$)/g, '') default: return str } } /** * 清除Object中无用的参数 * @param {Object} data * @param {Number} type 1:没用的参数改成undefined 2:删除没用的参数 */ const clearObject = (data, type = 1) => { if(!isObject(data)) { return console.error('参数【data】必须是对象') } let newData = JSON.parse(JSON.stringify(data)) for (const i in newData) { if (Object.hasOwnProperty.call(newData, i)) { const item = newData[i]; if(item === '' || item === null || item === undefined || item === 'undefined' || item === []) { if(type === 1) { newData[i] = undefined } else if(type === 2) { delete newData[i] } } else if (isString(item)) { newData[i] = trim(item) } else if (isNumber(item)) { continue } } return newData } } /** * 清除Array中无用参数 * @param {Array} actual * @returns */ function cleanArray(actual) { if(!isArray(actual)) { return console.error('参数【actual】必须是数组') } const newArray = []; for (let i = 0; i < actual.length; i++) { if(isNumber(actual[i])) { newArray.push(actual[i]); }else if(isString(actual[i])) { newArray.push(actual[i]); }else if (actual[i]) { newArray.push(actual[i]); } } return newArray; } /** * 判断是否是 IOS 系统 * @returns */ const isIOS = () => { const u = navigator.userAgent return isIOS = !u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X)/) } /** * * @param {*} dateStr * @returns */ const getDateDiff = (dateStr) =>{ const dateTimeStamp = Date.parse(dateStr.replace(/-/gi, "/")); const minute = 1000 * 60; const hour = minute * 60; const day = hour * 24; const halfamonth = day * 15; const month = day * 30; const now = new Date().getTime(); const diffValue = now - dateTimeStamp; if (diffValue < 0) { return; } const monthC = diffValue / month; const weekC = diffValue / (7 * day); const dayC = diffValue / day; const hourC = diffValue / hour; const minC = diffValue / minute; let result=""; if (monthC >= 1) { result = "" + parseInt(monthC) + "月前"; } else if (weekC >= 1) { result = "" + parseInt(weekC) + "周前"; } else if (dayC >= 1) { result = "" + parseInt(dayC) + "天前"; } else if (hourC >= 1) { result = "" + parseInt(hourC) + "小时前"; } else if (minC >= 1) { result = "" + parseInt(minC) + "分钟前"; } else result = "刚刚"; return result; } /** * 获取样式 * @param {HTMLElement} element * @param {String} attr 样式名例如 paddingTop * @param {String} NumberMode 返回float 还是 int * @returns */ const getStyle = (element, attr, NumberMode = 'int') => { let target; // scrollTop 获取方式不同,没有它不属于style,而且只有document.body才能用 if (attr === 'scrollTop') { target = element.scrollTop; }else if(element.currentStyle){ target = element.currentStyle[attr]; }else{ target = document.defaultView.getComputedStyle(element,null)[attr]; } //在获取 opactiy 时需要获取小数 parseFloat return NumberMode == 'float'? parseFloat(target) : parseInt(target); } /** * 页面到达底部,加载更多 * @param {HTMLElement} element * @param {Function} callback */ const loadMore = (element, callback) => { let windowHeight = window.screen.height; let height; let setTop; let paddingBottom; let marginBottom; let requestFram; let oldScrollTop; document.body.addEventListener('scroll',() => { loadMore(); }, false) //运动开始时获取元素 高度 和 offseTop, pading, margin element.addEventListener('touchstart',() => { height = element.offsetHeight; setTop = element.offsetTop; paddingBottom = getStyle(element,'paddingBottom'); marginBottom = getStyle(element,'marginBottom'); },{passive: true}) //运动过程中保持监听 scrollTop 的值判断是否到达底部 element.addEventListener('touchmove',() => { loadMore(); },{passive: true}) //运动结束时判断是否有惯性运动,惯性运动结束判断是非到达底部 element.addEventListener('touchend',() => { oldScrollTop = document.body.scrollTop; moveEnd(); },{passive: true}) const moveEnd = () => { requestFram = requestAnimationFrame(() => { if (document.body.scrollTop != oldScrollTop) { oldScrollTop = document.body.scrollTop; loadMore(); moveEnd(); }else{ cancelAnimationFrame(requestFram); //为了防止鼠标抬起时已经渲染好数据从而导致重获取数据,应该重新获取dom高度 height = element.offsetHeight; loadMore(); } }) } const loadMore = () => { if (document.body.scrollTop + windowHeight >= height + setTop + paddingBottom + marginBottom) { callback(); } } } /** * 运动效果 * @param {HTMLElement} element 运动对象,必选 * @param {Object} target 属性:目标值,必选 * @param {Number} duration 运动时间,可选 * @param {String} mode 运动模式,可选 * @param {Function} callback 可选,回调函数,链式动画 */ const animate = (element, target, duration = 400, mode = 'ease-out', callback) => { clearInterval(element.timer); //判断不同参数的情况 if (duration instanceof Function) { callback = duration; duration = 400; }else if(duration instanceof String){ mode = duration; duration = 400; } //判断不同参数的情况 if (mode instanceof Function) { callback = mode; mode = 'ease-out'; } //获取dom样式 const attrStyle = attr => { if (attr === "opacity") { return Math.round(getStyle(element, attr, 'float') * 100); } else { return getStyle(element, attr); } } //根字体大小,需要从此将 rem 改成 px 进行运算 const rootSize = parseFloat(document.documentElement.style.fontSize); const unit = {}; const initState = {}; //获取目标属性单位和初始样式值 Object.keys(target).forEach(attr => { if (/[^\d^\.]+/gi.test(target[attr])) { unit[attr] = target[attr].match(/[^\d^\.]+/gi)[0] || 'px'; }else{ unit[attr] = 'px'; } initState[attr] = attrStyle(attr); }); //去掉传入的后缀单位 Object.keys(target).forEach(attr => { if (unit[attr] == 'rem') { target[attr] = Math.ceil(parseInt(target[attr])*rootSize); }else{ target[attr] = parseInt(target[attr]); } }); let flag = true; //假设所有运动到达终点 const remberSpeed = {};//记录上一个速度值,在ease-in模式下需要用到 element.timer = setInterval(() => { Object.keys(target).forEach(attr => { let iSpeed = 0; //步长 let status = false; //是否仍需运动 let iCurrent = attrStyle(attr) || 0; //当前元素属性址 let speedBase = 0; //目标点需要减去的基础值,三种运动状态的值都不同 let intervalTime; //将目标值分为多少步执行,数值越大,步长越小,运动时间越长 switch(mode){ case 'ease-out': speedBase = iCurrent; intervalTime = duration*5/400; break; case 'linear': speedBase = initState[attr]; intervalTime = duration*20/400; break; case 'ease-in': let oldspeed = remberSpeed[attr] || 0; iSpeed = oldspeed + (target[attr] - initState[attr])/duration; remberSpeed[attr] = iSpeed break; default: speedBase = iCurrent; intervalTime = duration*5/400; } if (mode !== 'ease-in') { iSpeed = (target[attr] - speedBase) / intervalTime; iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); } //判断是否达步长之内的误差距离,如果到达说明到达目标点 switch(mode){ case 'ease-out': status = iCurrent != target[attr]; break; case 'linear': status = Math.abs(Math.abs(iCurrent) - Math.abs(target[attr])) > Math.abs(iSpeed); break; case 'ease-in': status = Math.abs(Math.abs(iCurrent) - Math.abs(target[attr])) > Math.abs(iSpeed); break; default: status = iCurrent != target[attr]; } if (status) { flag = false; //opacity 和 scrollTop 需要特殊处理 if (attr === "opacity") { element.style.filter = "alpha(opacity:" + (iCurrent + iSpeed) + ")"; element.style.opacity = (iCurrent + iSpeed) / 100; } else if (attr === 'scrollTop') { element.scrollTop = iCurrent + iSpeed; }else{ element.style[attr] = iCurrent + iSpeed + 'px'; } } else { flag = true; } if (flag) { clearInterval(element.timer); if (callback) { callback(); } } }) }, 20); } /** * 显示返回顶部按钮,开始、结束、运动 三个过程中调用函数判断是否达到目标点 */ const showBack = callback => { let requestFram; let oldScrollTop; document.addEventListener('scroll',() => { showBackFun(); }, false) document.addEventListener('touchstart',() => { showBackFun(); },{passive: true}) document.addEventListener('touchmove',() => { showBackFun(); },{passive: true}) document.addEventListener('touchend',() => { oldScrollTop = document.body.scrollTop; moveEnd(); },{passive: true}) const moveEnd = () => { requestFram = requestAnimationFrame(() => { if (document.body.scrollTop != oldScrollTop) { oldScrollTop = document.body.scrollTop; moveEnd(); }else{ cancelAnimationFrame(requestFram); } showBackFun(); }) } //判断是否达到目标点 const showBackFun = () => { if (document.body.scrollTop > 500) { callback(true); }else{ callback(false); } } } /** * 获取字符串字节数 * @param {Sting} val * @returns {Number} */ const getByteLen = (val) => { let len = 0; for (let i = 0; i < val.length; i++) { if (val[i].match(/[^\x00-\xff]/ig) != null) { len += 1; } else { len += 0.5; } } return Math.floor(len); } /** * 将第二个Object 合并到 第一个 Object 中 * @param {*} target * @param {*} source * @returns */ const objectMerge = (target = {}, source) => { if (!isObject(target)) { return console.error(`target必须是对象`) } if (!isObject(source)) { return console.error(`source必须是对象`) } for (const property in source) { if (source.hasOwnProperty(property)) { const sourceProperty = source[property]; if (isObject(sourceProperty)) { target[property] = objectMerge(target[property], sourceProperty); continue; } target[property] = sourceProperty; } } return target; } export { checkConentType, isArray, isObject, isNumber, isString, isBoolean, generateRandomMixed, generateRandomInteger, setState, getStore, removeStore, splitArray, params2url, getQueryObject, trim, clearObject, cleanArray, isIOS, getDateDiff, getStyle, loadMore, animate, showBack, getByteLen, objectMerge, }