工作的时候,大都会写些工具函数,分享下那些年写过的有趣函数。(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);
};
});
}