Canvas 进阶(二)写一个生成带logo的二维码npm插件

栏目: Html5 · 发布时间: 6年前

内容简介:最近接触到的需求,前端生成一个带企业logo的二维码,并支持点击下载它。在前面的文章有讲到如何用 canvas 画二维码,在此基础上再画一个公司logo,并提供下载的方法供调用,再封装成 npm 插件模块名称:

背景

最近接触到的需求,前端生成一个带企业logo的二维码,并支持点击下载它。

实现

在前面的文章有讲到如何用 canvas 画二维码,在此基础上再画一个公司logo,并提供下载的方法供调用,再封装成 npm 插件

模块名称: qrcode-with-logos

github地址: https://github.com/zxpsuper/qrcode-with-logos

npm地址: https://www.npmjs.com/package/qrcode-with-logos

核心代码

将整个封装成一个 QrCodeWithLogo 类,并提供三个方法:

interface IQrCodeWithLogo {
  toCanvas(): Promise<any>;
  toImage(): Promise<any>;
  downloadImage(name: string): void;
}

class QrCodeWithLogo implements IQrCodeWithLogo {
  option: BaseOptions;
  constructor(option: BaseOptions) {
    this.option = option;
    return this;
  }
  toCanvas = () => {
    return toCanvas.call(this, this.option);
  };
  toImage = () => {
    return toImage.call(this, this.option);
  };
  downloadImage = (name: string) => {
    saveImage(this.option.image, name);
  };
}

1. toCanvas()

此方法用到了库 qrcodetoCanvas 方法

export const toCanvas = (options: BaseOptions) => {
  return renderQrCode(options)
    .then(() => options)
    .then(drawLogo);
};

这里先用 qrcode 库画出二维码的canvas

Canvas 进阶(二)写一个生成带logo的二维码npm插件

import QRCode = require("qrcode");

const toCanvas = promisify(QRCode.toCanvas);

export const renderQrCode = ({
  canvas,
  content,
  width = 0,
  nodeQrCodeOptions = {}
}: BaseOptions) => {
  // 容错率,默认对内容少的二维码采用高容错率,内容多的二维码采用低容错率
  // according to the content length to choose different errorCorrectionLevel
  nodeQrCodeOptions.errorCorrectionLevel =
    nodeQrCodeOptions.errorCorrectionLevel || getErrorCorrectionLevel(content);

  return getOriginWidth(content, nodeQrCodeOptions).then((_width: number) => {
    // 得到原始比例后还原至设定值,再放大4倍以获取高清图
    // Restore to the set value according to the original ratio, and then zoom in 4 times to get the HD image.
    nodeQrCodeOptions.scale = width === 0 ? undefined : (width / _width) * 4;
    // @ts-ignore
    return toCanvas(canvas, content, nodeQrCodeOptions);
  });
};

promisify()是封装的一个方法,用于减少return promise时的代码,方便书写

export const promisify = (f: Function): Function => {
  return function() {
    const args = Array.prototype.slice.call(arguments);
    return new Promise(function(resolve, reject) {
      args.push(function(err: object, result: object) {
        if (err) reject(err);
        else resolve(result);
      });
      f.apply(null, args);
    });
  };
};

画出canvas,紧接着判断是否有logo, 如果有就画logo,这里有两种模式:

ctx.drawImage(image, logoXY, logoXY, logoWidth, logoWidth);
ctx.createPattern(canvasImage, "no-repeat");

Canvas 进阶(二)写一个生成带logo的二维码npm插件

export const drawLogo = ({ canvas, content, logo }: BaseOptions) => {
  if (!logo) {
    return;
  }
  // @ts-ignore
  const canvasWidth = canvas.width;
  const {
    logoSize = 0.15,
    borderColor = "#ffffff",
    bgColor = borderColor || "#ffffff",
    borderSize = 0.05,
    crossOrigin,
    borderRadius = 8,
    logoRadius = 0
  } = logo;
  let logoSrc = typeof logo === "string" ? logo : logo.src;
  let logoWidth = canvasWidth * logoSize;
  let logoXY = (canvasWidth * (1 - logoSize)) / 2;
  let logoBgWidth = canvasWidth * (logoSize + borderSize);
  let logoBgXY = (canvasWidth * (1 - logoSize - borderSize)) / 2;
  // @ts-ignore
  const ctx = canvas.getContext("2d");

  // logo 底色, draw logo background color
  canvasRoundRect(ctx)(
    logoBgXY,
    logoBgXY,
    logoBgWidth,
    logoBgWidth,
    borderRadius
  );
  ctx.fillStyle = bgColor;
  ctx.fill();

  // logo
  const image = new Image();
  if (crossOrigin || logoRadius) {
    image.setAttribute("crossOrigin", crossOrigin || "anonymous");
  }
  image.src = logoSrc;

  // 使用image绘制可以避免某些跨域情况
  // Use image drawing to avoid some cross-domain situations
  const drawLogoWithImage = (image: any) => {
    ctx.drawImage(image, logoXY, logoXY, logoWidth, logoWidth);
  };

  // 使用canvas绘制以获得更多的功能
  // Use canvas to draw more features, such as borderRadius
  const drawLogoWithCanvas = (image: any) => {
    const canvasImage = document.createElement("canvas");
    canvasImage.width = logoXY + logoWidth;
    canvasImage.height = logoXY + logoWidth;
    canvasImage
      .getContext("2d")
      .drawImage(image, logoXY, logoXY, logoWidth, logoWidth);

    canvasRoundRect(ctx)(logoXY, logoXY, logoWidth, logoWidth, logoRadius);
    ctx.fillStyle = ctx.createPattern(canvasImage, "no-repeat");
    ctx.fill();
  };

  // 将 logo绘制到 canvas上
  // Draw the logo on the canvas
  return new Promise((resolve, reject) => {
    image.onload = () => {
      logoRadius ? drawLogoWithCanvas(image) : drawLogoWithImage(image);
      resolve();
    };
  });
};

2. toImage()

此方法利用之前的 toCanvas() 方法,生成canvas后拿到 canvas.toDataURL() 的值,赋给 <img>src 即可。这里,我们加入了 download downloadName 属性用于下载,因此在 toImage() 方法中判断,代码如下:

export const toImage = (options: BaseOptions) => {
  const canvas = document.createElement("canvas");
  console.log("options", options);
  options.canvas = canvas;
  if (options.logo) {
    if (isString(options.logo)) {
      // @ts-ignore
      options.logo = { src: options.logo };
    }
    // @ts-ignore
    options.logo.crossOrigin = "Anonymous";
  }
  // @ts-ignore
  return toCanvas(options).then(() => {
    const { image = new Image(), downloadName = "qr-code" } = options;
    let { download } = options;
    // @ts-ignore
    image.src = canvas.toDataURL();

    if (download !== true && !isFunction(download)) {
      return;
    }
    download = download === true ? (start: Function) => start() : download;

    const startDownload: Function = () => {
      saveImage(image, downloadName);
    };

    download && download(startDownload);
    return new Promise((resolve, reject) => {
      resolve();
    });
  });
};


export const saveImage = (image: Element, name: string) => {
  // @ts-ignore
  const dataURL = image.src;

  const link = document.createElement("a");
  link.download = name;
  link.href = dataURL;
  link.dispatchEvent(new MouseEvent("click"));
};

3. downloadImage(name)

提供一个主动调用下载图片的方法,传入文件名name, 其中用到 saveImage() 方法,这个在 toImage() 中也有用到。

下载文件的流程是:生成一个 <a> 标签, 设置 href 值为 imagesrc 值, download 属性赋值文件名,然后给 <a> 主动一次点击事件即可。

downloadImage = (name: string) => {
    saveImage(this.option.image, name);
};

npm 发布

初次尝试 typescript , 用的不够优美但无妨使用。 npm run build 构建出支持 umd 的文件,然后 npm login , npm publish 即可。webpack 配置可查看 github 代码。

插件使用

下面是详细代码

<canvas id="canvas"></canvas> <img src="" alt="" id="image" />
<img id="image" alt="">

npm 模块导入:

import QrCodeWithLogo from "qrcode-with-logos";
let qrcode = new QrCodeWithLogo({
  canvas: document.getElementById("canvas"),
  content: "https://github.com/zxpsuper",
  width: 380,
  //   download: true,
  image: document.getElementById("image"),
  logo: {
    src: "https://avatars1.githubusercontent.com/u/28730619?s=460&v=4"
  }
});

qrcode.toCanvas().then(() => {
  qrcode.toImage().then(() => {
    setTimeout(() => {
      qrcode.downloadImage("hello world");
    }, 2000);
  });
});

当然你也可以 <script> 引入使用

<script src="https://zxpsuper.github.io/qrcode-with-logos/dist/QRcode-with-logo.js"></script>
<script>
let qrcode = new QrCodeWithLogo({
  canvas: document.getElementById("canvas"),
  content: "https://github.com/zxpsuper",
  width: 380,
  //   download: true,
  image: document.getElementById("image"),
  logo: {
    src: "https://avatars1.githubusercontent.com/u/28730619?s=460&v=4"
  }
});

qrcode.toCanvas().then(() => {
  qrcode.toImage().then(() => {
    setTimeout(() => {
      qrcode.downloadImage("hello world");
    }, 2000);
  });
});
</script>

That is all.


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Hacking Growth

Hacking Growth

Sean Ellis、Morgan Brown / Crown Business / 2017-4-25 / USD 29.00

The definitive playbook by the pioneers of Growth Hacking, one of the hottest business methodologies in Silicon Valley and beyond. It seems hard to believe today, but there was a time when Airbnb w......一起来看看 《Hacking Growth》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具