资讯专栏INFORMATION COLUMN

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

Chaz / 451人阅读

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

背景

最近接触到的需求,前端生成一个带企业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;
  toImage(): Promise;
  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

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);,可拓展性不强。

一种是canvas叠加,使用 ctx.createPattern(canvasImage, "no-repeat"); 可以实现更多复杂的效果

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() 的值,赋给

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);
  });
});

当然你也可以

That is all.

更多推荐

前端进阶小书(advanced_front_end)

前端每日一题(daily-question)

webpack4 搭建 Vue 应用(createVue)

Canvas 进阶(一)二维码的生成与扫码识别

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/105208.html

相关文章

  • Canvas 进阶(三)ts + canvas”辨色“小游戏

    摘要:话不多说,先上和项目源码有趣的是,在我写完这篇文章之后,发现爱编程的李先森也写了一篇手写辨色力小游戏实现方式有所不同,可以对比下。 1. 背景 之前写过一篇文章 ES6 手写一个辨色小游戏, 感觉好玩挺不错。岂料评论区大神频出,其中有人指出,打开控制台,输入以下代码: setInterval( ()=>document.querySelector(#special-block).cli...

    Aceyclee 评论0 收藏0
  • canvas绘制海报+维码,满足绝大部分场景

    摘要:无需任何第三方依赖,轻型工具库。绘制海报,生成带的二维码。默认重新编辑最终图片压缩比,默认基础类型参数表示一个图片部分。之间表示一个二维码部分参数类型描述指定为二维码类型要绘制的内容。通过控制边框颜色,默认为默认为容错等级。 canvas_x 无需任何第三方依赖,轻型工具库。canvas绘制海报,生成带logo的二维码。也可生成编辑界面,用户自定义输入,一键生成等等 默认开启图片跨域,...

    bluesky 评论0 收藏0
  • 关于Hexo6.0搭建个人博客(进阶篇)

    摘要:本篇博文将带大家发现新大陆教你打造炫酷的个人博客站点阅读本文前建议先行阅读本人另外一遍基础博文关于搭建个人博客基础篇目录配置博客基本信息配置主题优化主题上传头像并设置头像旋转效果设置个人社交图标链接设置设置酷炫动态背景设置主题语言设置网站设 本篇博文将带大家发现新大陆,教你打造炫酷的个人博客站点. 阅读本文前建议先行阅读本人另外一遍基础博文关于Hexo搭建个人博客(基础篇) 目录 配...

    jiekechoo 评论0 收藏0
  • 个人分享--web前端学习资源分享

    摘要:前言月份开始出没社区,现在差不多月了,按照工作的说法,就是差不多过了三个月的试用期,准备转正了一般来说,差不多到了转正的时候,会进行总结或者分享会议那么今天我就把看过的一些学习资源主要是博客,博文推荐分享给大家。 1.前言 6月份开始出没社区,现在差不多9月了,按照工作的说法,就是差不多过了三个月的试用期,准备转正了!一般来说,差不多到了转正的时候,会进行总结或者分享会议!那么今天我就...

    sherlock221 评论0 收藏0
  • 个人分享--web前端学习资源分享

    摘要:前言月份开始出没社区,现在差不多月了,按照工作的说法,就是差不多过了三个月的试用期,准备转正了一般来说,差不多到了转正的时候,会进行总结或者分享会议那么今天我就把看过的一些学习资源主要是博客,博文推荐分享给大家。 1.前言 6月份开始出没社区,现在差不多9月了,按照工作的说法,就是差不多过了三个月的试用期,准备转正了!一般来说,差不多到了转正的时候,会进行总结或者分享会议!那么今天我就...

    Ethan815 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<