xd-tools
Version:
封装常用工具类
586 lines (536 loc) • 16.2 kB
JavaScript
// 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,
}