那些年写过的工具函数

143 阅读3分钟

工作的时候,大都会写些工具函数,分享下那些年写过的有趣函数。(PS: 这里用了 ts 的写法)

添加动画

实现滚动提示动画时,需要根据 js 变量进行计算修改,有点繁琐,想起开发微信小程序时,用过一个animate函数,尝试仿造了低配版。

/**
 * @description 动画函数
 * @param {String} selectorId 操作Dom的id
 * @param {Object[]} keyframes 动画关键帧,关键帧数offset * 100 ,动画样式data
 * @param {Number} duration 动画时长
 * @param {String} timingFunction 时间函数
 * @param {Function} callback 动画结束后执行的回调函数
 * @param {Boolean} infinite 是否循环动画
 * @example
 * animate(
    'tips',
    [
      { offset: 0.0, data:{transform: `translateX(${contentWidth.value}px)`} },
      { offset: 1.0, data:{transform: `translateX(${-textWidth.value}px)`} },
    ],
    5,
    () => {
      console.log('it is over')
    }
  )
 */
function animate({
    selectorId,
    keyframes,
    duration = 1,
    timingFunction = 'linear',
    callback = () => {},
    infinite = false,
}: {
    selectorId: string;
    keyframes: { offset: number; data: {} }[];
    duration?: number;
    timingFunction?: string;
    callback?: () => void;
    infinite?: boolean;
}) {
    // 获取动画帧
    const keyframe = keyframes
        .map((item, index) => {
            let keys = [];
            for (const i in item.data) {
                keys.push(`${i.toString() + ':' + item.data[i as keyof typeof item.data]}`);
            }
            return Math.ceil(item.offset * 100) + '%' + '{' + keys.join(';') + '}';
        })
        .join('');
    // 样式表添加动画
    document.styleSheets[0].insertRule(`@keyframes animate-${selectorId} {${keyframe}}`, 0);
    const el = document.getElementById(selectorId);
    // 动画执行回调
    if (el) {
        if (infinite) {
            el.style.animation = `animate-${selectorId} ${duration}s ${timingFunction} infinite`;
            el.addEventListener('animationiteration', callback);
        } else {
            el.style.animation = `animate-${selectorId} ${duration}s`;
            el.addEventListener('animationend', () => {
                callback();
                el.style.animation = '';
            });
        }
    }
}

canvas 绘制圆角图片/矩形

canvas 绘制海报时,需要绘制多个圆角矩形和圆形头像,导致绘图函数过长,抽离核心逻辑写成了通用方法。

/**
 * @description 绘制一个圆角图片/矩形
 * @param {CanvasRenderingContext2D} ctx canvas的上下文环境
 * @param {Number} x 左上角x轴坐标
 * @param {Number} y 左上角y轴坐标
 * @param {Number} w 矩形的宽度
 * @param {Number} h 矩形的高度
 * @param {Number} r 圆的半径
 * @param {HTMLImageElement} img Image对象
 * @param {String} color 填充颜色
 **/
function drawRoundRectImage(
    ctx: CanvasRenderingContext2D,
    x: number,
    y: number,
    w: number,
    h: number,
    r: number,
    color?: string,
    img?: HTMLImageElement
) {
    if (2 * r > w || 2 * r > h) {
        return;
    }
    // save()和restore()方法是绘制状态的存储器,例如当前矩阵变换例如:平移translate(),缩放scale(),以及旋转rotate()、剪切区域clip()
    ctx.save();
    ctx.translate(x, y);
    //旋转角度
    //开始画图
    ctx.beginPath();
    //从右下角顺时针绘制,弧度从0到1/2PI
    ctx.arc(w - r, h - r, r, 0, Math.PI / 2);
    //矩形下边线
    ctx.lineTo(r, h);
    //左下角圆弧,弧度从1/2sPI到PI
    ctx.arc(r, h - r, r, Math.PI / 2, Math.PI);
    //矩形左边线
    ctx.lineTo(0, r);
    //左上角圆弧,弧度从PI到3/2PI
    ctx.arc(r, r, r, Math.PI, (Math.PI * 3) / 2);
    //上边线
    ctx.lineTo(w - r, 0);
    //右上角圆弧
    ctx.arc(w - r, r, r, (Math.PI * 3) / 2, Math.PI * 2);
    //右边线
    ctx.lineTo(w, h - r);
    // 结束画图
    ctx.closePath();
    if (img) {
        //剪切形状
        ctx.clip();
        // //填充图片
        ctx.drawImage(img, 0, 0, w, h);
    }
    if (color) {
        ctx.fillStyle = color;
        ctx.fill();
    }
    ctx.restore();
}

获取(包含特殊字符)文本的正则对象

命名检查要求时使用了正则test(),需要排除特殊字符影响。

/**
 * @description 获取(包含特殊字符)文本的正则对象
 * @param {String} text 匹配文本
 * @returns {RegExp} 正则表达式对象
 */
function getRegExp(
    text: string,
    allowedChars = ['\\', '$', '(', ')', '*', '+', '.', '[', '?', '^', '{', '|']
): RegExp {
    // 处理特殊字符
    const specialCharacter = allowedChars;
    specialCharacter.map((item) => {
        const reg = new RegExp('\\' + item, 'gim');
        text = text.replace(reg, '\\' + item);
    });
    return new RegExp(text);
}

获取剪贴板图片

做电商素材管理的时候,客户想把淘宝已有的图片直接粘贴上传,所以实现了这样一个复用函数。

export enum MODULE_ENUM {
    图片 = 'image',
}
/**
 * @description 粘贴复制文件/文件地址,默认允许图片类型
 * @param {ClipboardEvent} e 剪贴板事件对象
 * @param {String[]} format 允许文件类型
 * @returns {File | undefined}  file 粘贴的文件
 */
function pasteFileORpath(
    e: ClipboardEvent,
    format: string[] = [MODULE_ENUM.图片]
): Promise<File | undefined> {
    return new Promise((resolve) => {
        if (e.clipboardData === null) {
            return;
        }
        const file = e.clipboardData.files[0];
        const item = e.clipboardData.items[0];
        // 复制的图片地址
        if (item.kind === 'string') {
            item.getAsString(async (url) => {
                let file;
                if (HTTP_HTTPS_REG.test(url)) {
                    const response = await fetch(url);
                    const blob = await response.blob();
                    if (format.includes(blob.type.split('/')[0])) {
                        const fileName = blob.type.replace('/', '.');
                        file = new File([blob], fileName, { type: blob.type });
                    }
                }
                resolve(file);
            });
        } else if (item.kind === 'file') {
            // 复制的本地/网页图片
            if (format.includes(file.type.split('/')[0])) {
                resolve(file);
            }
        } else {
            return;
        }
    });
}

图片转 Base64 格式

随处可见的图片转 Base64 函数,用来水字数 🤣

/**
 * @description 图片转 base64 格式
 * @param {HTMLImageElement} img image 元素
 * @param {Number} width 自定义宽度
 * @param {Number} height 自定义高度
 * @returns {String} dataUrl 图片展示的数据 url
 */
function getBase64Image(img: HTMLImageElement, width?: number, height?: number): string {
    const canvas = document.createElement('canvas');
    canvas.width = width ? width : img.width;
    canvas.height = height ? height : img.height;
    const ctx = canvas.getContext('2d');
    ctx!.drawImage(img, 0, 0, canvas.width, canvas.height);
    const dataUrl = canvas.toDataURL();
    return dataUrl;
}

/**
 * @description canvas base64 图片
 * @param {String} url 图片 url
 * @returns
 */
function getCanvasBase64(url: string): Promise<string> {
    const image = new Image();
    // 注意跨域问题: '' 等于 'anonymous'
    image.crossOrigin = '';
    image.src = url;
    return new Promise((resolve, reject) => {
        image.onload = function () {
            resolve(getBase64Image(image));
        };
        image.onerror = function (err) {
            reject(err);
        };
    });
}