如何用TS实现一个全端可用的二维码以及条形码SDK

1,686 阅读10分钟

未标题-1.png 🌻心若安则步步生莲,心清一切明,心浊一切暗,心痴一切迷,心悟一切禅,心有多净就有多静。万物生于一,而一生于万物。

前置知识

  1. 使用Npm发布一个jsSDK包
  2. 使用TS + rollup 搭建SDK基础结构
  3. 自定义自己的TS描述文件.d.ts
  4. canvas的基本操作 条形码参考

github.com/lindell/JsB… 4.2k

二维码参考

blog.csdn.net/lemisi/arti…

github.com/BWmelon/qrc…

github.com/kazuhikoara… 1.7k

github.com/jeromeetien… 4.7k

github.com/soldair/nod… 5.7k

github.com/davidshimjs… 10.7k

更多资源可以在GitHub上搜索

二维码SDK历史背景

当时UniApp并未现身于九州大陆,小程序并为百花齐放,那时候同一个需求需要在微信小程序以及支付宝小程序上实现相同的功能,因此大多数时候写完一端的代码之后在批量转换为另一端的代码,其实就是将wx.xx API转换为my.xx API或者将my.xx API转换为wx.xx API。(当时有别人写好的sdk统一使用wx.xx也可以在支付宝小程序上使用)去年趁着上班摸鱼时间改为UniApp。可跨端使用并增加了部分功能。当然我标题说的是全端,而UniApp并不能跨全端(包括Vue、React、Angular、Svelte......)

初始化项目

在这里我们需要一个带@Npm包。在安装的时候:

npm install @uni-ui/code-plugs

需要登录自己的npm账号添加一个组织Add Organization比如uni-ui为组织名称

npm init --scope=@uni-ui/code-ui //如果不需带@的npm包可以直接npm init

安装TS + Rollup

npm i @babel/core
npm i @babel/preset-env
npm i rollup-plugin-babel
npm i rollup-plugin-typescript2
npm i rollup-plugin-uglify
npm i typescript
npm i @dcloudio/types //UniApp TS 类型描述文件

新建rollup.config.js以及tsconfig.json

rollup.config.js

import babel from 'rollup-plugin-babel';
import path from 'path'
import { uglify } from 'rollup-plugin-uglify';
import typescript from 'rollup-plugin-typescript2';
const getPath = _path => path.resolve(__dirname, _path)
export default {
    input: 'src/index.ts',// 文件入口
    output: [
        {
            file: 'dist/code.umd.min.js',// sdk输出路径
            name: 'CODE',
            format: 'umd'//打包格式amd,cjs,es,iife,umd
        }
    ],
    plugins: [
        babel({
          exclude: 'node_modules/**'
        }),
        uglify(), //代码压缩
        typescript({
            tsconfig: getPath('./tsconfig.json')
        })
    ]
};

tsconfig.json

{
    "compilerOptions": {
        "target": "es5", // 编译目标
        "module": "esnext", // 模块类型
        "removeComments": true,// 删除注释 
        "sourceMap": true,
        "suppressImplicitAnyIndexErrors": true,
        "moduleResolution": "node",
        "allowUnreachableCode": true, // 不报告执行不到的代码错误。
        "allowJs": false, // 是否编辑js文件
        "strict": true, // 严格模式
        "noUnusedLocals": true, // 未使用变量报错
        "experimentalDecorators": true, // 启动装饰器
        "esModuleInterop": true,
        "declarationMap": false, // 生成定义sourceMapss
        "lib": ["es5", "es2015", "es2016", "es2017", "es2018", "dom"],  // 导入库类型定义
        "types": [// 导入指定类型包
          "@dcloudio/types"//UniApp TS类型描述文件
        ]
    },
}

新建文件夹创建目录结构

  • src
    • codeType 编码类型文件目录
    • common 公共方法目录
    • lib 条形码,二维码
    • types TS描述文件.d.ts
    • index.ts 入口文件
  • tsconfig.json
  • package.json
  • .babelrc
  • .npmignore
  • rollup.config.js

index.ts入口文件

/**
* @author wmf❤洛尘
* @version 1.0.0
* @Date 2021-11-14
* @LastEditTime 2021-11-14
* @description UniApp 二维码 条形码
* */
import { OperationCode } from './lib/barcode'
import { WidgetCode } from './lib/qrcode'

// 条形码
export const BarCode = OperationCode;

// 二维码
export const QRCode = WidgetCode;

条形码

为了大家方便使用生成条形码的大小以及保存位图片时的宽高我们统一位rpx,但是canvas不支持rpx只支持px,因此为了偷懒我们需要封装一个rpx转px的公用方法

src/common/support.ts

/**
 * @author wmf❤洛尘
 * @method UNIT_CONVERSION
 * @description UniApp rpx ——> px 默认750
 * @param num 
 * @returns 转换后的像素
 */
 export const UNIT_CONVERSION = function (num: string | number): number{
	return uni.upx2px(Number(num));
 }
 /**
 * @author wmf❤洛尘
 * @method SaveCodeImg
 * @description 保存二维码或者条形码为图片
 * @param k 
 * @returns 
 */
export const SaveCodeImg = function(k: StrongCode.SaveCanvasPars): object{
    const width: number = UNIT_CONVERSION(Number(k.width));
    const height: number = UNIT_CONVERSION(Number(k.height));
    return new Promise((resolve)=>{
        if (Object.prototype.toString.call(k.id) == '[object String]') {
            uni.canvasToTempFilePath({
                canvasId: k.id as string,
                width: width,
                height: height,
                destWidth: width,
                destHeight: height,
                fileType: k.type || 'png',
                quality: k.quality || 1,
                complete: function(res) {
                    resolve(res)
                }
            }, k.ctx)
        } else if (Object.prototype.toString.call(k.id) == '[object Object]') {//兼容nvue
            const ctx = k.id as StrongCode.NvueCanvasConText;
            ctx.toTempFilePath(0, 0, width, height, width, height, k.type || 'png', 1,(res)=> {
                resolve(res)
            })
        }
    })
}

在这里我们将TS的interface统一放在一个文件里面

src/types/code.d.ts

declare namespace StrongCode {
    
    /**
     * @description 保存图片 兼容Nvue
     */
    interface NvueCanvasConText extends UniApp.CanvasContext {// 继承UniApp.CanvasContext
        toTempFilePath(x: number, y: number, w: number, h: number, dw: number, dh:number, type?: string, quality?: number, callback?: (result: any) => void): void;
    }
    /**
     * @description 二维码边框参数
     */
    interface BorderCode {
        color: string[],
        degree: number,
        lineWidth: number,
        opacity: number,
    }
    /**
     * @description 二维码边中间图片参数
     * @param src 图片地址
     * @param size 图片大小
     * @param color 图片边框颜色 默认为 #FFFFFF
     * @param type 图片类型 none circle round 默认none
     * @param width 如果type = circle | round 图片周围的白色边框粗细 默认为5
     */
    interface CodeImg {
       src: string,
       size?: number,
       type?: string,
       color?: string,
       width?: number,
       degree: number,
    }
    /**
     * @description 二维码文字绘制
     * @param placement 文字的位置可选值 top bottom middle 默认 middle
     */
    interface CodeText {
        opacity?: number,
        font?: string,
        placement?: string,
        color?: string[],
        content: string
    }
    /**
     * @description 生成二维码参数
     */
    interface BarCodePars {
        id: string | UniApp.CanvasContext,
        size: string | number,
        code: string,
        level?: number,
        bgColor?: string,
        text?: CodeText,
        color?: string[],
        img?: CodeImg,
        iconSize?: number,
        border?: BorderCode,
        ctx: object
    }
    /**
     * @description 保存二维码或者条形码为图片
     */
    interface SaveCanvasPars {
        id: string | UniApp.CanvasContext,
        type?: string,
        width: string | number,
        height: string | number,
        quality?: number,
        ctx: object
    }
    /**
     * @description 条形码生成参数
     */
    interface OperationCodePars {
        id: string | UniApp.CanvasContext,
        width: number,
        height: number,
        type?: string,
        code: string,
        bgColor?: string,
        color?: string,
        ctx: object
    }
    /**
     * @description 保存图片 兼容Nvue
     */
    interface areaPars {
        width: number,
        height: number,
        top: number,
        left: number
    }
    /**
     * @description 保存图片 兼容Nvue
     */
    interface  BarcOpt {
        currcs: number,
    }
    /**
     * @description 绘制条形码所需参数
     */
    interface  Provider {
        ANY: number,
        AB: number,
        A: number,
        B: number,
        C: number
    }
    /**
     * @description 绘制条形码所需参数
     */
    interface  PCodeOpt {
        CHAR_TILDE: number
    }
}

然后在tsconfig.json里面添加

"types": [// 导入指定类型包
    "@dcloudio/types",//UniApp的描述文件
     "./src/types/code"// 自定义的描述文件
]

初始化条形码

src/lib/barcode.ts

/**
 * @method GraphicContentInit
 * @description 初始化条形码
 */
class GraphicContentInit {
    width: number;
    height:number;
    quiet: number;
    borderSize: number = 0;
    paddingWidth: number = 0;
    ctx: UniApp.CanvasContext;
    color: string;
    backGroud: string;
    area: StrongCode.areaPars;

    constructor (ctx: UniApp.CanvasContext,width: number,height: number,color: string,backGroud: string) {
        this.ctx = ctx;
        this.width = width;
        this.height = height;
        this.quiet = Math.round(this.width / 40);
        this.color = color;
        this.backGroud = backGroud;
        this.area = {
            width: width - this.paddingWidth * 2 - this.quiet * 2,
            height: height - this.borderSize * 2,
            top: this.borderSize - 4,
            left: this.borderSize + this.quiet
        };
        this.fillBgRect(0,0, width, height);
        this.fillBgRect(0, this.borderSize, width, height - this.borderSize * 2);
    }
    
    fillFgRect(x: number,y: number, width: number, height: number) {
	this.FILLRECT(x,y,width, height,this.color);
    };
    fillBgRect(x: number,y: number, width: number, height: number) {
	this.FILLRECT(x,y, width, height,this.backGroud);
    };
    FILLRECT(x: number,y: number, width: number, height: number,color: string) {
	this.ctx.setFillStyle(color);
	this.ctx.fillRect(x, y, width, height);
    }
}

生成条形码

import {
    UNIT_CONVERSION, 
    SaveCodeImg
} from '../common/support'

import { BarCode128, BarCode39 } from '../codeType'

import {PATTERNS} from '../common/metadata'

/**
* @author wmf❤洛尘 
* @method OperationCode 创建条形码
* @description 使用UniApp的条形码
*/
export const OperationCode = function (opt: StrongCode.OperationCodePars, callback?: Function) {
    if (!opt.code) {
        console.warn("没有找到条形码code");
        return
    }
    if (!opt.id){
        console.warn("没有找到条形码canvas id或者实列!");
        return
    }
    let CTX: UniApp.CanvasContext;
    // 这里增加一个判断 目的是为了兼容Nvue
    if (Object.prototype.toString.call(opt.id) == '[object String]') {
        CTX = uni.createCanvasContext(<string>opt.id, opt.ctx || null);
        BarCodeCanvas(opt, CTX, callback)
    } else if (Object.prototype.toString.call(opt.id) == '[object Object]') {//在此兼容nvue
        CTX = opt.id as UniApp.CanvasContext;
        BarCodeCanvas(opt, CTX, callback)
    }
}
export const BarCodeCanvas = function (opt: StrongCode.OperationCodePars, ctx: UniApp.CanvasContext, callback?: Function) {
    const width: number = UNIT_CONVERSION(opt.width);
    const height: number = UNIT_CONVERSION(opt.height);

    let gc = new GraphicContentInit(ctx, width, height,opt.color || "#000000",opt.bgColor || "#ffffff");//初始化条形码
    
    SetBarCodeType[opt.type || 'CODE128'](opt.code,gc,height); //条形码的编码类型
    
    ctx.draw(false, async (res) => {
        callback ? callback({// 画布绘制完成的回调
            ...res,
            img: res.errMsg == "drawCanvas:ok"await SaveCodeImg({// 绘制成功成返回条形码图片否则返回 null
                width: opt.width,
                height: opt.height,
                id: opt.id,
                ctx: opt.ctx || null
            }) : null,
            id: Object.prototype.toString.call(opt.id) == '[object String]' ? opt.id : "nvue"
        }) : null;
    });
}
const SetBarCodeType = {
    /**
     * @method CODE128
     * @param code 条形码的值
     * @param gc 
     * @param height 
     * @description 条形码类型 默认CODE128
     */
    "CODE128": function CODE128 (code: string, gc: GraphicContentInit,height: number) {
        const CodeNum: number[] = BarCode128(code);
        let barWeight = gc.area.width / ((CodeNum.length - 3) * 11 + 35);
        let x: number = gc.area.left;
        const y = gc.area.top;
        const barH = height - gc.area.top;
        
        for (let i = 0; i < CodeNum.length; i++) {
            const c = CodeNum[i];
            for (let bar = 0; bar < 8; bar += 2) {
                const barW = PATTERNS[c][bar] * barWeight;
                // const barH = height - y - this.border
                const spcW = PATTERNS[c][bar + 1] * barWeight;
                if (barW > 0) {
                    gc.fillFgRect(x, y, barW, barH); //开始绘画条形码
                }

                x += barW + spcW;
            }
        }
    },
    /**
     * @method CODE39
     * @param code 条形码的值
     * @param gc 
     * @param height 
     * @todo 待实现
     * @description 条形码类型 CODE39
     */
    "CODE39": function CODE39 (code: string, gc: GraphicContentInit, height: number): void {
        console.error("条形码编码类型:CODE39暂未实现");
    },
    /**
     * @method EAN
     * @param code 条形码的值
     * @param gc 
     * @param height 
     * @todo 待实现
     * @description 条形码类型 EAN
     */
    "EAN": function EAN (code: string, gc: GraphicContentInit, height: number): void {
        console.error("条形码编码类型:EAN暂未实现");
    },
    /**
     * @method ITF
     * @param code 条形码的值
     * @param gc 
     * @param height 
     * @todo 待实现
     * @description 条形码类型 ITF
     */
    "ITF": function ITF (code: string, gc: GraphicContentInit, height: number): void {
        console.error("条形码编码类型:ITF暂未实现");
    },
    /**
     * @method MSI
     * @param code 条形码的值
     * @param gc 
     * @param height 
     * @todo 待实现
     * @description 条形码类型 MSI
     */
    "MSI": function MSI (code: string, gc: GraphicContentInit, height: number): void {
        console.error("条形码编码类型:MSI暂未实现");
    },
    /**
     * @method Codabar
     * @param code 条形码的值
     * @param gc 
     * @param height 
     * @todo 待实现
     * @description 条形码类型 Codabar
     */
    "Codabar": function Codabar (code: string, gc: GraphicContentInit, height: number): void {
        console.error("条形码编码类型:Codabar暂未实现");
    },
    /**
     * @method Pharmacode
     * @param code 条形码的值
     * @param gc 
     * @param height 
     * @todo 待实现
     * @description 条形码类型 Pharmacode
     */
    "Pharmacode": function Pharmacode (code: string, gc: GraphicContentInit, height: number): void {
        console.error("条形码编码类型:Pharmacode暂未实现");
    }
}

二维码

src/lib/qrcode.ts

import {
    QRCodeInit,
    UNIT_CONVERSION,
    UtF16TO8,
    SaveCodeImg
} from '../common/support'

/**
 * @author wmf
 * @method WidgetCode 生成二维码
 * @param opt 二维码参数
 * @param callback 回调
 * @description 定义二维码参数
*/
export const WidgetCode = function(opt: StrongCode.BarCodePars,callback?: Function){
    if (!opt.code) {
        console.warn("没有找到二维码code");
        return
    }
    if (!opt.id){
        console.warn("没有找到二维码canvas id或者实列!");
        return
    }
    let CTX: UniApp.CanvasContext;
    //实例化QRCodeInit
    const BARCODE: QRCodeInit = new QRCodeInit(opt.level);
    // 二维码code转码 主要针对纯汉字
    const CODE: string = UtF16TO8(opt.code)
    // 生成二维码所需要的数据
    const FRAME: number[] = BARCODE.Genframe(CODE)
    const width: number = BARCODE.getWidth();

    if (Object.prototype.toString.call(opt.id) == '[object String]') {
        CTX = uni.createCanvasContext(<string>opt.id, opt.ctx || null);
        RepaintCanvas(opt, CTX,FRAME, width, callback)
    } else if (Object.prototype.toString.call(opt.id) == '[object Object]') {//在此兼容nvue
        CTX = opt.id as UniApp.CanvasContext;
        RepaintCanvas(opt, CTX,FRAME, width,callback)
    }

}
const RepaintCanvas = function (opt: StrongCode.BarCodePars, ctx: UniApp.CanvasContext, frame: number[], width: number, callback?: Function) {

    const SIZE: number = UNIT_CONVERSION(opt.size)
    const px: number = Math.round(SIZE / (width + 8));
    const roundedSize: number = px * (width + 8);
    const offset: number = Math.floor((SIZE - roundedSize) / 2);

    ctx.clearRect(0, 0, SIZE, SIZE);
    ctx.setFillStyle(opt.bgColor || '#FFFFFF');//二维码背景色
    ctx.fillRect(0, 0, SIZE, SIZE);//设置画布大小

    //绘制二维码颜色 支持渐变
    opt.color ? SetColorCode(ctx,SIZE,opt.color) : ctx.setFillStyle("#000000");
    // 绘制二维码边框 支持渐变 透明度
    opt.border ? SetBorderCode(ctx,SIZE,opt.border) : false;

    for (let i = 0; i < width; i++) {//开始生成二维码
        for (let j = 0; j < width; j++) {
            if (frame[j * width + i]) {
                // ctx.drawImage('/static/9.png', px * (4 + i) + offset, px * (4 + j) + offset, px, px);//二维码码点可以替换为图片
                ctx.fillRect(px * (4 + i) + offset, px * (4 + j) + offset, px, px);
            }
        }
    }
    // 图片放在下面 防止图片在二维码下面
    opt.img ? SetImageType[opt.img.type || 'none'](ctx,SIZE,opt.img) : false;
    //绘制二维码文字
    opt.text ? SetTextCode(ctx,SIZE,opt.text) : false;
    
    ctx.draw(false, async (res) => {
        callback ? callback({
            ...res,
            img: res.errMsg == "drawCanvas:ok" ? await SaveCodeImg({
                width: opt.size,
                height: opt.size,
                id: opt.id,
                ctx: opt.ctx || null
            }) : null,
            id: Object.prototype.toString.call(opt.id) == '[object String]' ? opt.id : "nvue"
        }) : null;
    });

}
/**
 * @method SetColorCode
 * @author wmf
 * @Date 2021-11-08
 * @LastEditTime 2021-11-08
 * @todo 颜色支持多种渐变
 * @description 设置二维码颜色 支持渐变色
 */
const SetColorCode = function (ctx: UniApp.CanvasContext,size: number,colors: string[]): void {
    let GRD: UniApp.CanvasGradient = ctx.createLinearGradient(0, 0, size, 0);
    if(colors.length === 1){
        GRD.addColorStop(0, colors[0]);
        GRD.addColorStop(1, colors[0]);
    }
    if(colors.length === 2){
        GRD.addColorStop(0, colors[0]);
        GRD.addColorStop(1, colors[1]);
    }
    if(colors.length === 3){
        GRD.addColorStop(0, colors[0]);
        GRD.addColorStop(0.5, colors[1]);
        GRD.addColorStop(1, colors[2]);
    }
    ctx.setFillStyle(GRD)
}
/**
 * @author wmf
 * @method SetImageType
 * @description 二维码中间log绘制
 */
const SetImageType = {//none circle round
    'none': function SetImageCode(ctx: UniApp.CanvasContext,size: number, img: StrongCode.CodeImg){
        const iconSize = img?.size || 30
        const width =  Number(((size - iconSize) / 2).toFixed(2));
        ctx.save();
        ctx.drawImage(img.src, width, width, iconSize, iconSize)
    },
    /**
     * @method SetCircleImg
     * @author wmf
     * @todo 二维码中间log 圆形
     * @description 设置二维码log为圆形
     */
    'circle': function SetCircleImg(ctx: UniApp.CanvasContext,size: number, img: StrongCode.CodeImg){
        const r: number = (img.size || 30);
        const w: number = r * 2;
        const x: number = size/2 - r;
        const y: number = size/2 - r;
	const cx: number = x + r;
	const cy: number = y + r;
        ctx.save();
	ctx.arc(cx, cy, r, 0, 2 * Math.PI);
	ctx.setLineWidth(img.width || 5)
	ctx.setStrokeStyle(img.color || "#FFFFFF"); // 设置绘制圆形边框的颜色
	ctx.stroke(); 
	ctx.clip();
	ctx.drawImage(img.src, x, y, w, w);
    },
    /**
     * @method SetRoundImg
     * @author wmf
     * @todo 二维码中间log 圆角
     * @description 设置二维码log为圆角
     */
    'round': function SetRoundImg(ctx: UniApp.CanvasContext,size: number, img: StrongCode.CodeImg) {
        let r: number = img.degree || 5;
        const iconSize = img.size || 30;
        const w: number = iconSize;
        const h: number = iconSize;
        const x: number = size/2 - iconSize/2;
        const y: number = size/2 - iconSize/2;
        w < 2 * r ? r = w / 2 : false;
        h < 2 * r ? r = h / 2 : false;
        ctx.save();
	ctx.beginPath();
	ctx.moveTo(x + r, y);
	ctx.arcTo(x + w, y, x + w, y + h, r);
	ctx.arcTo(x + w, y + h, x, y + h, r);
	ctx.arcTo(x, y + h, x, y, r);
	ctx.arcTo(x, y, x + w, y, r);
	ctx.closePath();
        ctx.setLineWidth(img.width || 5)
	ctx.setStrokeStyle(img.color || "#FFFFFF"); // 设置绘制圆形边框的颜色
	ctx.stroke();
	ctx.clip();
	ctx.drawImage(img.src, x, y, w , w);
    }
}
/**
 * 
 * @method SetBorderCode
 * @author wmf
 * @todo 二维码边框颜色支持多种渐变 后期不需要判断
 * @description 设置二维码边框
 */

 const SetBorderCode = function(ctx: UniApp.CanvasContext,size: number,border?: StrongCode.BorderCode): void {
    const colors: string[] = border?.color || ['#F27121','#8A2387'];
    const r: number = border?.degree || 5;
    const x: number = 0;
    const y: number = 0;
    const w: number = size;
    const h: number = size;
    let GRD: UniApp.CanvasGradient = ctx.createLinearGradient(0, 0, size, 0);
    if(colors.length === 1){
        GRD.addColorStop(0, colors[0]);
        GRD.addColorStop(1, colors[0]);
    }
    if(colors.length === 2){
        GRD.addColorStop(0, colors[0]);
        GRD.addColorStop(1, colors[1]);
    }
    if(colors.length === 3){
        GRD.addColorStop(0, colors[0]);
        GRD.addColorStop(0.5, colors[1]);
        GRD.addColorStop(1, colors[2]);
    }
    ctx.save();
    ctx.setGlobalAlpha(border?.opacity || 1)
	ctx.beginPath();
	ctx.moveTo(x + r, y);
	ctx.arcTo(x + w, y, x + w, y + h, r);
	ctx.arcTo(x + w, y + h, x, y + h, r);
	ctx.arcTo(x, y + h, x, y, r);
	ctx.arcTo(x, y, x + w, y, r);

    ctx.closePath();
	ctx.setLineWidth(border?.lineWidth || 5)
	ctx.setStrokeStyle(GRD); // 设置绘制圆形边框的颜色
	ctx.stroke();
	ctx.clip();
    ctx.setGlobalAlpha(1)
}

/**
 * @method SetTextCode
 * @author wmf
 * @Date 2021-11-08
 * @LastEditTime 2021-11-08
 * @todo 颜色支持多种渐变
 * @description 在二维码上设置文本
 */
 const SetTextCode = function (ctx: UniApp.CanvasContext,size: number,text: StrongCode.CodeText): void {
    let GRD: UniApp.CanvasGradient = ctx.createLinearGradient(0, 0, size, 0);
    let colors = text.color || ["#FFFFFF"];
    if(colors.length === 1){
        GRD.addColorStop(0, colors[0]);
        GRD.addColorStop(1, colors[0]);
    }
    if(colors.length === 2){
        GRD.addColorStop(0, colors[0]);
        GRD.addColorStop(1, colors[1]);
    }
    if(colors.length === 3){
        GRD.addColorStop(0, colors[0]);
        GRD.addColorStop(0.5, colors[1]);
        GRD.addColorStop(1, colors[2]);
    }
    ctx.restore();
    ctx.setGlobalAlpha(text?.opacity || 1)
    ctx.setTextAlign('center');//'left'、'center'、'right'
    ctx.setTextBaseline('middle');//可选值 'top'、'bottom'、'middle'、'normal'
    ctx.font = text?.font || "normal 20px system-ui",
    ctx.setFillStyle(GRD)
    ctx.fillText(text.content, size/2, size/2);
    ctx.setGlobalAlpha(1)
}

打包发布Npm

npm run build
// or
yarn build

打包完成后会在当前项目根目录下生成一个dist 文件

npm login //登录
npm publish --access public //发布 注意:发布一个带有@的包 需要加一个参数--access public

注意⚠️:.npmignore

我们只需要将打包后的dist文件上传其余不需要

node_modules
src
.babelrc
rollup.config.js
package-lock.json
package.json
tsconfig.json

跃跃一试

打开HbuildX开发工具新建一个空白UniApp项目

组件方式依赖于@uni-ui/code-plugs,在npm i @uni-ui/code-ui -save的时候会自动安装相关依赖。

@uni-ui/code-plugs就是我们刚才发布的Npm包

npm i @uni-ui/code-ui -save

pages.json里面配置如下

"easycom": { 
    "^w-(.*)": "@uni-ui/code-ui/components/w-$1/index.vue"//二维码条形码的配置 
}, 
"pages": [ 
    //... 
 ]

index.vue

<view>
    <!-- 条形码 -->
    <w-barcode :options="config.bar"></w-barcode>
    <view class="barnum" @click="open">{{ code }}</view>
</view>
<view>
    <!-- 二维码 -->
    <w-qrcode :options="config.qrc" ref="qrcode" @generate="hello"></w-qrcode>
</view>
<script>
export default {
    data() {
        return {
            config: {
                bar: {//条形码配置参数
                    code: 'E01181016286106',
                    color: '#ff9472', // 条形码的颜色
                    bgColor: '#FFFFFF', // 背景色
                    width: 670, // 宽度 代表 670rpx
                    height: 100 // 高度 代表 100rpx
		},
                qrc: {// 二维码配置参数
                    code: 'https://qm.qq.com/cgi-bin/qm/qr?k=LKqML292dD2WvwQfAJXBUmvgbiB_TZWF&noverify=0',
                    size: 460, // 二维码大小460rpx
                    level: 4, //纠错级 0~4
                    bgColor: '#FFFFFF', //二维码背景色 不传默认白色
                    type: 'none',
                    border: {// 二维码边框配置
                        opacity: 1,//边框透明度 0~1不传吗,默认不透明
                        degree: 0,//边框角度
                        color: ['#e96443','#904e95'], //边框颜色支持渐变色
                        lineWidth: 5 //边框宽度
                    },
                    img: {//二维码中间log配置
                        src: '/static/logo.png',// 图片路径
                        size: 40,// 图片大小
                        type: 'round',// 图片展示类型 默认 none circle round
                        color: '#ffffff',//边框颜色
                        width: 8 //边框宽度
                     },
                     text:{
                        opacity: 1,//文字透明度默认不透明 0~1
                         //当前字体样式的属性。符合 CSS font 语法 的 DOMString 字符串,至少需要提供字体大小和字体族名。默认值为 normal 20px system-ui。
                        font: "normal 30px system-ui",// 默认normal 20px system-ui
                        color: ['#45B649','#00c3ff', '#ee0979'],//文字颜色 不传默认黑色
                        content: "I Miss You" //文字内容
                      },
                      color:['#45B649','#00c3ff', '#ee0979'] //边框颜色支持渐变色
                }
        }
    };
    methods: {
        hello (res) {//二维码绘制完成后的回调 成功失败都会触发
            console.log(res)
        }
    }
}

运行结果

1636952764(1).png

基于UniApp的二维码的jsSDk就这样完成了,当然标题说的是全端,在Vue、React、Angular、Svelte......里面使用只需要将uni.xx API替换为html5 的canvasApi就可以了。

最后附上UniApp 插件市场地址:ext.dcloud.net.cn/plugin?id=4… 里面有完整的示例