自定义工具函数

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;
}

 

posted @ 2025-01-13 14:46  何以之  阅读(20)  评论(0)    收藏  举报