自定义工具函数
1.函数柯里化
function carry(func) { return function carried(...args) { if (args.length >= func.length) { return func.apply(this, args); } else { return function(...args2) { return carried.apply(this, args.concat(args2)); } } } } function sum(a, b) { return a + b; } const _sum = carry(sum); console.log(_sum(1, 2), _sum(1)(2));
2.函数嵌套调用
// 书写函数 compose 的实现,使得 compose(f,g,h)() 的输出结果是 1,3,5,6,4,2 function compose(...funcs) { // f(g(h(xx))) const _funcs = funcs.filter(f => typeof f === 'function'); function next(funcs2) { if (funcs2.length === 1) { return funcs2[0].bind(this, (()=>{})); } else { const f = funcs2.shift(); return f.bind(this, next(funcs2)); } } return next(_funcs); } function f(next) { console.log(1); next(); console.log(2) } function g(next) { console.log(3); next(); console.log(4) } function h(next) { console.log(5); next(); console.log(6) } compose(f,g,h)()
3.深拷贝实现
const copied = new WeakMap(); function deepClone(obj) { if (obj === null || typeof obj !== 'object') { return obj; } if (copied.has(obj)) { // 如果已经拷贝过,则返回之前拷贝的对象引用 return copied.get(obj); } const cloneObj = {}; cloneObj.__proto__ = obj.__proto__; copied.set(obj, cloneObj); // 遍历keys const keys = Reflect.ownKeys(obj); for(let i = 0; i < keys.length; i++) { const value = obj[keys[i]]; if (value instanceof Object) { if (typeof value === 'function') { cloneObj[keys[i]] = value.bind(cloneObj); } else if (value instanceof Date) { cloneObj[keys[i]] = new Date(value.valueOf()); } else if (value instanceof RegExp) { cloneObj[keys[i]] = new RegExp(value.valueOf()); } else if (value instanceof String) { cloneObj[keys[i]] = new String(value.valueOf()); } else if (value instanceof Boolean) { cloneObj[keys[i]] = new Boolean(value.valueOf()); } else if (value instanceof Number) { cloneObj[keys[i]] = new Number(value.valueOf()); } else { cloneObj[keys[i]] = deepClone(value); } } else { cloneObj[keys[i]] = value; } } return cloneObj; }
4.其他工具函数
/** * @pram element 必传,要获取样式的元素 * @pram attribute 必传,要获取的样式属性 * @pram pseudo_elements 可选,默认值null,要获取样式的元素的伪元素,ie8及以下浏览器不生效 * */ function getStyle(element, attribute, pseudo_elements = null){ return window.getComputedStyle ? getComputedStyle(element,pseudo_elements)[attribute] : //ie9及以上的浏览器 element.currentStyle[attribute]; //ie支持 } /** * @pram element 要绑定拖拽事件的元素 * */ function bindDrag(element){ element.onmousedown = (e)=>{ //如果支持setCapture则调用setCapture,来让element拦截并执行浏览器中所有的事件 ie8及以下浏览器拦截默认事件的解决方案 element.setCapture && element.setCapture() e = e || window.event //鼠标相对于元素的位置 let left = e.clientX - e.target.offsetLeft let top = e.clientY - e.target.offsetTop //为元素开启绝对定位方便移动 element.style.position = 'absolute' //当鼠标按下时,给页面绑定onmousemove事件 document.onmousemove = (event)=>{ //获取鼠标移动事件对象 event = event || window.event //当鼠标移动时,被拖拽元素跟着鼠标移动 //设置元素的水平偏移量 element.style.left = event.clientX - left + (document.scrollLeft||0) + 'px' //设置元素的垂直偏移量 element.style.top = event.clientY - top + (document.scrollTop||0) + 'px' }; //当鼠标按下时,给页面绑定onmouseup事件 document.onmouseup = (event)=>{ //获取鼠标移动事件对象 event = event || window.event //固定元素位置 //设置元素的水平偏移量 element.style.left = event.clientX - left + (document.scrollLeft||0) + 'px' //设置元素的垂直偏移量 element.style.top = event.clientY - top +( document.scrollTop||0) + 'px' //当鼠标抬起时,解绑页面的onmousemove事件和onmouseup事件 document.onmouseup = document.onmousemove = null //如果支持releaseCapture则调用releaseCapture,来让element取消拦截 element.releaseCapture && element.releaseCapture() }; //拦截浏览器的默认行为,对ie8及以下不生效 return false; }; } /** * @pram element 要绑定拖拽事件的元素 * @pram downCallback 点击之后的回调函数 * @pram moveCallback 移动过程中的回调函数 * @pram upCallback 释放鼠标之后的回调函数 * */ function bindDragExpand(element,downCallback,moveCallback,upCallback){ element.onmousedown = (e)=>{ //如果支持setCapture则调用setCapture,来让element拦截并执行浏览器中所有的事件 ie8及以下浏览器拦截默认事件的解决方案 element.setCapture && element.setCapture(); e = e || window.event; //是否有指定按下时的回调,若有则将e作为参数传递过去并执行,并接收返回值 let downResult = null if(downCallback) downResult = downCallback(e) let moveResult = null //当鼠标按下时,给页面绑定onmousemove事件 document.onmousemove = (event)=>{ //获取鼠标移动事件对象 event = event || window.event; //将event、moveResult、downResult作为参数传递给moveCallback,并接收返回值 if(moveCallback) moveResult = moveCallback(e,event,moveResult,downResult) }; //当鼠标按下时,给页面绑定onmouseup事件 document.onmouseup = (event)=>{ //获取鼠标移动事件对象 event = event || window.event; //将event、moveResult、downResult作为参数传递给upCallback,并接收返回值 upCallback && upCallback(e,event,moveResult,downResult) //当鼠标抬起时,解绑页面的onmousemove事件,并固定元素位置 document.onmouseup = document.onmousemove = null; //如果支持releaseCapture则调用releaseCapture,来让element取消拦截 element.releaseCapture && element.releaseCapture(); }; //拦截浏览器的默认行为,对ie8及以下不生效 return false; }; } /** * @pram element 必传,要绑定事件的对象 * @pram eventName 必传,要绑定的事件名,如click、keyup、mousemove... * @pram callback 必传,响应函数 * @pram isCapture 可选,是否在捕获阶段触发事件,默认值为false,ie8及以下浏览器不生效 * */ function bind(element,eventName,callback,isCapture = false){ if(element.addEventListener){ element.addEventListener(eventName, callback, isCapture); if(eventName === 'mousewheel') element.addEventListener('DOMMouseScroll', callback, isCapture); } else element.attachEvent('on'+eventName, () => callback.call(element)); } /** * @pram element 要执行动画的元素 * @pram attribute 要变化的属性 * @pram target 要变化属性的目标值,属性值从当前值变化到目标值 * @pram speed 属性值变化的速度 * @pram callback 回调函数,动画执行完毕后执行 * 只有值的单位是px的属性可用 * */ function animate(element,attribute = 'left',target,speed = 10,callback = null){ clearInterval(element.timer); let current = parseInt(getStyle(element,attribute)); if(current>target) speed *= -1; //在目标右侧 //开启定时器 element.timer = setInterval(()=>{ let value = parseInt(getStyle(element,attribute)) + speed; if(value < target && speed<0 || value>target && speed>0) value = target; element.style[attribute] = value + 'px'; if(value === target){ clearInterval(element.timer); callback && callback.call(element); } },16); } /** * 格式化类:删除多余的空格 * @pram element 要格式化类的元素 * */ function formatClass(element){ //去掉多余的空格 element.className = element.className.trim().split(' ').filter((item)=>{ return item.length > 0; }).join(' '); } /** * 查找元素是否含有指定的一些类,返回返回含有的和含有的类 * @pram element 要查找类的元素 * @pram classArr 要查找的类 * */ function queryClass(element,...classArr){ formatClass(element); //定义返回值 const result = { include:[], not:[] } //遍历 classArr.forEach((item)=>{ let reg = new RegExp('\\b'+item+'\\b'); if(reg.test(element.className)) result.include.push(item); else result.not.push(item); }); return result; } /** * 为元素添加指定的一些类,返回添加成功和添加失败的类 * @pram element 要添加类的元素 * @pram classArr 要添加的类 * */ function addClass(element,...classArr){ const query = queryClass(element,...classArr); //定义返回值 const result = { failed:[...query.include], succeeded:[...query.not] } //添加可以添加的类 element.className += ' ' + result.succeeded.join(' '); return result; } /** * 删除元素指定的一些类,返回删除成功和删除失败的类 * @pram element 要删除类的元素 * @pram classArr 要删除的类 * */ function removeClass(element,...classArr){ //定义返回值 const result = { failed:[], succeeded:[] } //遍历 classArr.forEach((item)=>{ let reg = new RegExp('\\b'+item+'\\b'); if(reg.test(element.className)) { result.succeeded.push(item); element.className = element.className.replace(reg, ''); } else result.failed.push(item); }); formatClass(element); return result; } /** * 切换元素指定的一些类,返回切换成功和切换失败的类 * 切换:如果含有该类,则删除;如果不含该类则添加 * @pram element 要切换类的元素 * @pram classArr 要切换的类 * */ function toggleClass(element,...classArr){ //定义返回值 const result = { add:[], remove:[] } classArr.forEach((item)=>{ let reg = new RegExp('\\b'+item+'\\b'); if(reg.test(element.className)) { result.remove.push(item); element.className = element.className.replace(reg, ''); } else result.add.push(item); }); formatClass(element); element.className += ' ' + result.add.join(' '); return result; } /** * 传入一些数据,返回这些数据的数据类型数组,不传则返回空数组 * @pram data :需要判断数据类型的数据,可以传多个 * */ function getDataType(...data){ const result = [] data.forEach((item)=>{ if(typeof item === 'object'){ if( item === null) result.push('null') else if(item instanceof Array) result.push('array') else result.push('object') } else result.push(typeof item) }) return result } /** * 获取传入的画笔对象的画布区域或ImageData区域的点(x,y)的颜色信息 * @pram object 画笔对象或ImageData对象 * @pram x 要获取的像素点的横轴坐标 * @pram y 要获取的像素点的纵轴坐标 * */ function getPxInfo(object,x,y){ let imageData if(object.getImageData){//传入的是画笔 imageData = object.getImageData(0,0,object.canvas.width,object.canvas.height) } else if(object.width && object.height && object.data){//传入的是ImageData对象 imageData = object } else {//传入的是非法对象 console.error('传入的object既不是画笔对象也不是ImageData对象!') return [] } let {width,height,data} = imageData if(height<y){ console.error('要获取的像素点不在指定区域!') return [] } return [ data[4*(y*width+x)], data[4*(y*width+x)+1], data[4*(y*width+x)+2], data[4*(y*width+x)+3] ] } /** * 设置画布区域的ImageData对象的点(x,y)的颜色为color,设置成功返回true,设置失败返回false * @pram ImageData 要设置像素点的区域,默认为null,如不传则默认该区域为整个画笔区域 * @pram x 要设置的像素点的横轴坐标 * @pram y 要设置的像素点的纵轴坐标 * @pram color 要设置的像素点的颜色,默认为黑色不透明 * */ function setImageDataPxInfo(imageData,x,y,color = [0,0,0,255]){ if(!(imageData.width && imageData.height && imageData.data)){//传入的不是ImageData对象 console.error('传入的imageData对象不是ImageData对象!') return false } let {width,height} = imageData if(height<=y||width<=x){ console.error('要设置的像素点不在指定区域!') return false } for (let i = 0; i < 4; i++) imageData.data[4*(y*width+x)+i] = color[i] return true } /** * 设置传入的ctx或ImageData区域的点(x,y)的颜色信息,设置成功返回true,设置失败返回false * @pram ctx 画笔对象 * @pram x 要设置的像素点的横轴坐标,默认为0 * @pram y 要设置的像素点的纵轴坐标,默认为0 * @pram color 要设置的像素点的颜色,默认为黑色不透明 * @pram ImageData 要设置像素点的区域,默认为null,如不传则默认该区域为整个画笔区域 * @pram x0 设置后区域的放置起始点的横轴坐标,默认为0 * @pram y0 设置后区域的放置起始点的纵轴坐标,默认为0 * */ function setPxInfo(ctx,x = 0,y = 0,color = [0,0,0,255],imageData = null,x0 = 0,y0 = 0){ if(!ctx.canvas) { console.error('传入的ctx不是画笔对象!') return false } if(!imageData){ imageData = ctx.getImageData(0,0,ctx.canvas.width,ctx.canvas.height) } else if(!(imageData.width && imageData.height && imageData.data)){//传入的不是ImageData对象 console.warn('传入的imageData对象不是ImageData对象!') } else if(!(x0<ctx.canvas.width&&y0<ctx.canvas.height)){ console.warn('传入的起始位置错误!') } let {width,height} = imageData if(height<=y){ console.error('要设置的像素点不在指定区域!') return false } //检查color格式是否正确 if(!(color instanceof Array)){//不是数组 console.error('传入的color不是数组!') return false } else{ for (let i = 0; i < 4; i++) { if(!(typeof color[i] === 'number')||color[i]>255||color[i]<0){ console.warn(`color[${i}]的数据格式不对,已被重置为0!`) color[i] = 0 } imageData.data[4*(y*width+x)+i] = color[i] } if(color.length>4){ console.warn(`‘color’数组的数据只有前面四个有效!`) } } ctx.putImageData(imageData,x0,y0) return true } /** * 给指定的画布区域打上马赛克 * @pram ctx 画笔对象 * @pram x 打码起始点的横轴坐标 * @pram y 打码起始点的纵轴坐标 * @pram width 打码区域的宽度 * @pram height 打码区域的高度 * @pram grade 模糊程度,默认为10 * */ function addMosaic(ctx,x,y,width,height,grade = 10){ let imageData = ctx.getImageData(x,y,width,height) for (let i = 0; i < Math.ceil(width / grade); i++) { for (let j = 0; j < Math.ceil(height / grade); j++) { let limit = [ grade*i + grade > width ? width - grade*i : grade, grade*j + grade > height ? height - grade*j : grade ] let random1 = Math.floor(Math.random()*limit[0]) let random2 = Math.floor(Math.random()*limit[1]) let color = getPxInfo(imageData,i*grade+random1,j*grade+random2) for (let k = 0; k < limit[0] ; k++) { for (let l = 0; l < limit[1]; l++) { setImageDataPxInfo(imageData,i*grade+k,j*grade+l,color) } } } } ctx.putImageData(imageData,0,0) } //打码动画函数,time为每个码的时间 function addMosaicAnimation(ctx,x,y,width,height,grade = 10,time){ let j = 0,i = 0,flag = 1 setInterval(()=>{ if(!(i < Math.ceil(height / grade))||i<0) { flag*=-1 j++ } if(!(j < Math.ceil(width / grade))) return let limit = [ grade*i + grade > width ? width - grade*i : grade, grade*j + grade > height ? height - grade*j : grade ] let random1 = Math.floor(Math.random()*limit[0]) let random2 = Math.floor(Math.random()*limit[1]) let imageData = ctx.getImageData(i*limit[0],j*limit[1],limit[0],limit[1]) let color = getPxInfo(imageData,random1,random2) for (let k = 0; k < limit[0] ; k++) { for (let l = 0; l < limit[1]; l++) { setPxInfo(ctx,k,l,color,imageData,i*limit[0],j*limit[1]) } } i+=flag },time) } /** * 计算两个日期之间的天数 * @param dateString1 开始日期 yyyy-MM-dd * @param dateString2 结束日期 yyyy-MM-dd * @returns {number} 如果日期相同 返回一天 开始日期大于结束日期,返回0 */ function getDaysBetween(dateString1,dateString2){ var startDate = Date.parse(dateString1); var endDate = Date.parse(dateString2); if (startDate>endDate){ return 0; } if (startDate==endDate){ return 1; } var days=(endDate - startDate)/(1*24*60*60*1000); return days; }
本文作者:何以之
本文链接:https://www.cnblogs.com/serendipity-echo/articles/18668611
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步