4
浏览器为我们提供了许多有用的方法,为了使用这些方法,开发者通常需要创建对应的对象,往往使得业务代码显得冗长,缺乏经验的开发者对所建对象的处理方式也显得不够“优雅”。
如果对 IIFE 返回的函数可以保存其既有作用域链这一特性善加利用,就可以将这些对象的创建过程“隐藏”起来,使得业务代码更加清晰简洁。

最近在写一个图片处理应用的时候总结了一些微不足道却也方便的方法,随手给思否的同学们分享一下。

1. 文件转 Base64

在大规模使用 FileSystem接口之前,前端读取文件主要靠 input[type='file'] 元素。开发者通常会随手创建一个 FileReader 实例来读取文件,用完之后随即抛弃之,不得不说真是无情。实际上这个实例是可以复用起来的:

/**
 * @method blob2Base64 - 将 blob 文件转为 Base64 URL
 * @returns {Promise<String>}
 * */
 const blob2Base64 = (() => {
    const fileReader = new FileReader();
    let resolver = null, errHandler = null;
    fileReader.onload = () => resolver(fileReader.result);
    fileReader.onerror = err => errHandler(err);
    
    return file => {
        fileReader.readAsDataURL(file);
        return new Promise((resolve, reject) => {
            resolver = resolve;
            errHandler = reject;
        })
    }
})();

这种代码最常用于读取用户选取的图片,使用的时候:

const readFileFromInput = async event => {
    const { target: { files: [file] }} = event;
    const base64 = await blob2Base64(file);
    console.log('read data as base64:', base64);
}
document.querySelector('input[type="file"]').addEventListener('change', readFileFromInput);

2. 从URL加载图片

前端处理图片通常依赖于 canvas ,而 canvas 绘图上下文不支持直接从 URL 读取图片,所以我们需要创建一个图片对象,并在其加载完成后再绘制到 canvas 上,鉴于 canvas 会将图片数据复制一份,这个图片对象是可以复用的:

/**
 * @const loadImageFromURL - 从 URL 加载图片
 * */
const loadImageFromURL = (() => {
    const image = new Image();
    image.setAttribute('crossOrigin', 'Anonymous');
    let resolver = null, errHandler = null;
    image.onload = () => {
        resolver(image);
    };
    image.onerror = err => {
        errHandler(err);
    };
    return URL => {
        return new Promise((resolve, reject) => {
            resolver = resolve;
            errHandler = reject;
            image.src = URL;
        });
    }
})();

当然,如果是使用 Knova 这样的绘图库的话,每添加一张图片都需要创建新的 Image 对象,这段代码稍加修改就可以胜任了。

3. 获取图片BitMap数据

借助 canvas 我们可以将浏览器支持的所有格式的图片解码得到其位图数据,不过,这个需求并不常见,除非你想亲手实现一下二维码扫描功能。这里需要复用的,是一个 canvas 元素对象及其绘图上下文:

/**
 * @method imageFileToImageData - 读取图片文件的为位图数据
 * @param {File} blob - 需要转换的文件对象
 * @return {Promise<ImageData>}
 */const imageFileToImageData = (() => {
    const canvas = document.createElement('canvas');
    canvas.setAttribute('crossOrigin', 'Anonymous');
    const context = canvas.getContext('2d');
    return async blob => {
        const image = await loadImageFromURL(await blob2Base64(blob));
        const { width, height } = image;
        canvas.setAttribute('width', width);
        canvas.setAttribute('height', height);
        context.drawImage(image, 0, 0, width, height);
        return context.getImageData(0, 0, width, height);
    };
})();

上面的两个方法在这里都已经用上了,可以看出简直是简洁、方便、清晰到家了!

4. 文件下载

文件下载的原理是借助一个 a[download] 元素触发点击事件,同样地,这个元素也是可以复用的:

/**
 * 传入 URL 下载文件
 * */
const downLoadPicByURL = (() => {
    const anchor = document.createElement('a');
    return (URL, filename = 'download') => {
        anchor.setAttribute('href', URL);
        anchor.setAttribute('download', filename);
        anchor.click();
    }
})();

当然,由于下载的过程没法监听更没法控制,这个方法不返回任何有效信息。


君迹我心
5.1k 声望44 粉丝

WEB前端,懂点皮毛。