今天看啥  ›  专栏  ›  前端王睿

教你使用Canvas合成图片

前端王睿  · 简书  ·  · 2019-08-03 20:08

之前写过一篇有关 Canvas图片处理 的文章,主要说的是用HTML5中的Canvas实现类似PS中的滤镜功能,而今天我们继续说说这强大的Canvas,看看如何用它来实现PS中的图片合成功能。

一、使用场景

图片合成的使用场景是非常广的,虽说强大的PS能将任意图片组合在一起,但它也只能实现固定的几张图片的合成,而Canvas则能动态地将各种不同的图片合为一张图片。

那么,何为动态合成呢?

比如,我们要将用户的头像合到另一张图片上,这时候因为用户是不确定的,不同用户的头像不同,所以我们肯定不能用PS来做这件事,这时候就是Canvas大显身手的时候了。

二、基本原理

HTML5给我们提供了一个很好的图像处理神器Canvas,想要合成图片,我们首先得将图片按照一定顺序、大小和位置一张张绘制到Canvas画布中去,最后再将整个Canvas导出为图片。

Canvas图片合成

三、编码实践

1、创建Canvas画布

let canvas = document.createElement('canvas'),
    ctx = canvas.getContext('2d');
canvas.width = 800;
canvas.height = 800;

2、绘制图片至Canvas

let img1 = new Image(),
    img2 = new Image();
img1.src = './images/1.jpg';
img2.src = './images/2.jpg';

// 加载img1
let pm1 = new Promise((res,rej)=>{
  img1.onload = ()=>{
    res();
  }
});
// 加载img2
let pm2 = new Promise((res,rej)=>{
  img2.onload = ()=>{
    res();
  }
});

// 两张图片都加载完成后绘制于Canva中
let drawAllImg = Promise.all([pm1, pm2]).then((res)=>{
  ctx.drawImage(img2, 0, 0, 400, 533);
  ctx.drawImage(img1, 0, 0, 100, 100);
});

这里我分别绘制了 1.jpg 2.jpg 两张图片于Canvas画布中,由于图片需要先从本地加载才能进行绘制,所以这里我使用了 onload() 方法和 Promise 对象(详见 ES6异步编程之Promise(一) ES6异步编程之Promise(二)
),其中 Promise 对象中的 all() 方法是为了等两张图片全部加载完后在执行后续操作。

由于我们现在创建的Canvas还未添加到DOM中,所以若是想要看现在的Canvas情况得先加上下面这句:

document.body.appendChild(canvas);

当然,实际若是只想要合成图片的话就没必要加上这句了,在离屏Canvas(即手动创建的Canvas但并未添加至DOM中)上绘制即可。

3、将Canvas导出为图片

drawAllImg.then(()=>{
  let outputImg = new Image();
  outputImg.src = ctx.canvas.toDataURL();
  document.body.appendChild(outputImg);
});

4、整合代码,最终形成可复用的JS插件

// 图片合成插件
class ImgMerge {

    constructor(imgs = [], options){
        // 图片数组默认配置项
        let defaultImgsItem = {
          url: '',
          x: 0,
          y: 0
        };
        // 导出图片的格式与压缩程度默认配置项
        let defaultOpts = {
          type: 'image/jpeg',
          compress: 1
        };

        try {
          imgs.forEach((item,i,arr) => {
            arr[i] = Object.assign({},defaultImgsItem,item)
          });
        }catch (e) {
            throw '请传入一个正确的对象数组作为参数';
        }

        this.imgs = imgs;   // 图片数组配置项
        this.opts = Object.assign({},defaultOpts,options);   // 其他配置项
        this.imgObjs = [];   // 图片对象数组

        this.createCanvas();  // 创建画布
        return this.outputImg();  // 导出图片

    }

    // 创建画布
    createCanvas(){

        let canvas = document.createElement('canvas'),
            ctx = canvas.getContext('2d');

        let w = this.imgs[0].width, h = this.imgs[0].height;

        if(!w){
            throw '第一张图片宽度未设置';
        }
        if(!h){
            throw '第一张图片高度未设置';
        }

        canvas.width = w;
        canvas.height = h;

        this.ctx = ctx;

    }

    // 绘制图片
    drawImg(i){

        let img = new Image();
        img.src = this.imgs[i].url;
        this.imgObjs.push(img);

        return new Promise((resolve)=>{
            img.onload = resolve;
        });

    }

    // 导出图片
    outputImg(){

        let imgArr = [];
        // 将单张图片的Promise对象存入数组
        this.imgs.forEach((item,i) => {
            imgArr.push(this.drawImg(i));
        });

        // 所有图片加载成功后将图片绘制于Canvas中,后将Canvas导出为图片
        return Promise.all(imgArr).then(()=>{
            this.imgs.forEach((item,i) => {
              let drawPara = [this.imgObjs[i], this.imgs[i].x, this.imgs[i].y];
              // 此处判断参数中图片是否设置了宽高,若宽高均设置,则绘制已设置的宽高,否则按照图片默认宽高绘制
              if(this.imgs[i].width && this.imgs[i].height){
                drawPara.push(this.imgs[i].width, this.imgs[i].height);
              }
              this.ctx.drawImage(...drawPara);
            });
            // 以base64格式导出图片
            return Promise.resolve(this.ctx.canvas.toDataURL(this.opts.type),this.opts.compress);
        });

    }

}

window.ImgMerge = ImgMerge;   //  可用于全局引用
export default ImgMerge;   //  可用于模块化引用

然后就可以轻松地使用该插件了。

import ImgMerge from './imgMerge.js';

window.onload = function () {

    let imgMerge = new ImgMerge([
        {
            url: require('../images/bg.jpg'),
            width: 640,
            height: 1169
        },
        {
            url: require('../images/1.jpg'),
            width: 200,
            height: 200
        }
    ]);

    imgMerge.then(img => {
      let mergeImg = new Image();
      mergeImg.src = img;
      mergeImg.onload = () => {
        document.body.appendChild(mergeImg);
      };
    });

};

相关推荐

ES6异步编程之Promise(一)
ES6异步编程之Promise(二)


结束语

本文通过实际案例主要讲述了如何通过Canvas将多张图片合成为单张图片,同时也展示了由ES6编写的面向对象的JS插件代码,大家如果有兴趣可尝试将文字合成功能也添加进去,如果理解了本文方法之后,其实文字合成到图片上也是非常简单的,仅仅添加个方法和修改下配置项就可以了。




原文地址:访问原文地址
快照地址: 访问文章快照