纯前端合并图片与下载图片实现

一、需求场景

1、前端动态二维码组合

对于很多的营销活动,需要提供给用户一张可下载二维码,但是二维码要组合一些特定的业务信息,然后便于用户分享、传播,达到营销效果。这一类的图片下载有一个特点,二维码里蕴含了用户信息,所以是每个人的二维码是不一样的,而业务背景图都是一样的,而且用户下载的最终图片样式是可以确定的,这时候前端则需要自己的能力组合两张图,下载给用户。

纯前端合并图片与下载图片实现_第1张图片

2、水印技术

有的网站信息内容专利、防窃取的要求,为了让用户下载分享的内容不被随意对外分享,则需要将用户下载的图片上盖上一层署名信息,从而防止有人下载图片后冒充作者,侵犯作者权益。

纯前端合并图片与下载图片实现_第2张图片

3、还有很多场景...

纯前端合并图片与下载图片实现_第3张图片

纯前端合并图片与下载图片实现_第4张图片




二、解决方案

解决方案的前提是利用客户端的能力,纯前端来解决。作为前端开发者,对于画图来说,Canvas能力是完全足够的,2D的、3D的都是可以完成,我这里的解决方案就是利用Canvas来做的。首先要利用canvas画图能力,前提两张图要合并的相对的位置,大小需要先知道,这需要你先跟视觉同学沟通好,实际涉及到的Canvas的api有如下这三个getContextdrawImagetoDataURL,下面来认识一下这三个API。

1、getContext方法

var ctx = canvas.getContext(contextType, contextAttributes);

HTMLCanvasElement.getContext() 方法会返回当前canvas的上下文,注意是HTMLCanvasElement元素的方法,可以是页面上的canvas标签,也可以是javascript的createElement方法创建的元素。

看一下getContext方法的参数,contextType是想要在画布上绘制的类型,可以选的值有2d,webgl,webgl2等。

参数值
含义
2d 创建 CanvasRenderingContext2D 二维渲染上下文,像图片这种二维空间的选这个类型。
webgl/experimental-webgl 创建 WebGLRenderingContext 三维渲染上下文对象,适用于三维动画制作开发。
webgl2/experimental-webgl2 创建一个 WebGL2RenderingContext 三维渲染上下文对象,webgl的升级版本。
bitmaprenderer 将创建将canvas内容替换为指定ImageBitmap功能的ImageBitmapRenderingContext,canvas与位图的生成,笔者也没接触过。

2、drawImage方法

drawImage方法是上面getContext获取的上下文里的方法,但是它只在contextType为2d的时候才可以被调用,也就是只能在二维图片的时候使用,可以根据其调用入参控制进行画图、组合图、剪切图。

void ctx.drawImage(image, x, y);

在画布上将image图像定位到x坐标长度与y坐标长度,画上去的图片大小就是图片本身的大小,不能控制。

void ctx.drawImage(image, x, y, width, height);

在画布上将image图像定位到x坐标长度与y坐标长度,并且指定image图像宽与高。

void ctx.drawImage(image, x, y, width, height, dx, dy, dWidth, dHeight);

在画布上将image图像定位到x坐标长度与y坐标长度,并且指定image图像宽与高,然后截取出来,放在(dx,dy)的坐标处,并且指定宽高,感觉可以很快做个文字放大镜。

3、toDataURL方法

canvas.toDataURL(type, encoderOptions);

toDataURL方法也是HTMLCanvasElement元素提供的方法,当调用HTMLCanvasElement.toDataURL() 方法时,会返回一个包含图片展示的data URL,像这样的格式data:[][;base64],

该方法可以接受两个参数。

type: 生成dataURL类型,默认是image/png的的类型,如果需要生成jpg或者其他类型传入即可。

encoderOptions:可选参数,生成的dataURL质量参数,默认是0.92,可以传入0-1之间的小数,越大质量越高,生成的串信息也越多,越长。


三、实现流程

1、生成画布

先生成一块canvas画布,并且把第一张图,一般是背景图画进画布里。这里要注意,drawImage方法的第一个参数是HTMLImageElement元素,你可以通过document.querySelector获取页面上img标签元素,也可以通过createElement('img')去生成,取决于你的图是否在页面上展示着。

// html



// javascript
class Canvas {
  // 创建canvas
  constructor (config = {}) {
    this.canvas = document.createElement('canvas');
    this.canvas.width = config.bgWidth;
    this.canvas.height = config.bgHeight;
    this.ctx = this.canvas.getContext('2d');
  }
  // 画图到画布上
  async run(config) {
    // images是HTMLImageElement元素,而非链接地址
    const image = await getImage(config.src);
    this.ctx.drawImage(image, config.x, config.y, config.width, config.height);
  }
  // 验证使用,将生产的图呈现到页面上
  print() {
    document.querySelector('#img').setAttribute('src', this.canvas.toDataURL());
  }
}
// 调用
const mycanvas = new Canvas({
   bgWidth: 500,
   bgHeight: 600
})
mycanvas.run({
  src: 'https://n.sinaimg.cn/ent/transform/460/w630h630/20180824/Zaob-hicsiaw3749625.jpg',
  x: 0,
  y: 0,
  width: 250,
  height: 600
}).then(() => {
  // 画到页面上
  mycanvas.print()
})

2、组装

在同一个ctx上进行两次将两张图进行合并drawImage,进行合并完成。

// html



// javascript
// canvas合成操作
class Canvas {
  ...
  async run2(img1, img2) {
    const images = [img1, img2];
    const imgSrcs = images.map(({ src }) => getImage(src));
    const imgEles = await Promise.all(imgSrcs);
    imgEles.map((ele, i) => {
      if (ele) {
        const { x = 0, y = 0, width = 0, height = 0 } = images[i];
        this.ctx.drawImage(ele, x, y, width, height);
      }
    });
  }
  ...
}


// 调用
const mycanvas = new Canvas({
  bgWidth: 500,
  bgHeight: 600
})
mycanvas.run({
  src: 'https://n.sinaimg.cn/ent/transform/460/w630h630/20180824/Zaob-hicsiaw3749625.jpg',
  x: 0,
  y: 0,
  width: 250,
  height: 600
}, {
  src: 'https://n.sinaimg.cn/ent/transform/460/w630h630/20180824/Zaob-hicsiaw3749625.jpg',
  x: 250,
  y: 0,
  width: 250,
  height: 600
}).then(() => {
  // 画到页面上
  mycanvas.print()
})

3、调用结果如下

纯前端合并图片与下载图片实现_第5张图片


4、实现下载方法

class Canvas {
  ...
  download() {
    // 文件流式下载
    this.canvas.toBlob(blob => {
      const a = document.createElement('a');
      a.download = 'image.png';
      a.style.display = 'none';
      a.href = URL.createObjectURL(blob);
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
    });
  }
  ...
}


四、发布至NPM,开源共享

目前我已经将上述组合图片的功能做成了开源package,上传至npm源上,如果有有需要的可以使用以下命令直接使用。

npm包地址

https://www.npmjs.com/package/composite-image

issue地址

https://github.com/FantasyGao/composite-image/issues

// 直接安装使用
npm/cnpm i --save composite-image
// 也可以在html文件直接引入

纯前端合并图片与下载图片实现_第6张图片

    

  欢迎各位使用与提一些建议,感谢。

你可能感兴趣的:(纯前端合并图片与下载图片实现)