| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 |
- <template>
- <view
- ref="qrcodeRef"
- :style="{ width: getPx(props.width) + 'px', height: getPx(props.height) + 'px' }"
- >
- <canvas
- ref="canvasRef"
- :canvas-id="qrcodeId"
- type="2d"
- :id="qrcodeId"
- :style="{ width: getPx(props.width) + 'px', height: getPx(props.height) + 'px' }"
- ></canvas>
- </view>
- </template>
- <script lang="ts" setup>
- import {
- ref,
- watch,
- onMounted,
- getCurrentInstance,
- nextTick,
- computed,
- type PropType,
- onUnmounted
- } from "vue";
- import { drawQrcode, type QrcodeOptions } from "./draw";
- import { getPx, isHarmony, uuid } from "@/cool";
- import type { ClQrcodeMode } from "../../types";
- import { base64ToBlob } from "./utils";
- defineOptions({
- name: "cl-qrcode"
- });
- const props = defineProps({
- // 二维码宽度,支持 px/rpx 单位
- width: {
- type: String,
- default: "200px"
- },
- // 二维码高度,支持 px/rpx 单位
- height: {
- type: String,
- default: "200px"
- },
- // 二维码前景色
- foreground: {
- type: String,
- default: "#131313"
- },
- // 二维码背景色
- background: {
- type: String,
- default: "#FFFFFF"
- },
- // 定位点颜色,不填写时与前景色一致
- pdColor: {
- type: String as PropType<string | null>,
- default: null
- },
- // 定位图案圆角半径,为0时绘制直角矩形
- pdRadius: {
- type: Number,
- default: 10
- },
- // 二维码内容
- text: {
- type: String,
- default: "https://cool-js.com/"
- },
- // logo 图片地址,支持网络、本地路径
- logo: {
- type: String,
- default: ""
- },
- // logo 大小,支持 px/rpx 单位
- logoSize: {
- type: String,
- default: "50px"
- },
- // 二维码边距,单位 px
- padding: {
- type: Number,
- default: 5
- },
- // 二维码样式:rect 普通矩形、circular 小圆点、line 线条、rectSmall 小方格
- mode: {
- type: String as PropType<ClQrcodeMode>,
- default: "circular"
- }
- });
- const { proxy } = getCurrentInstance()!;
- // 二维码组件id
- const qrcodeId = ref<string>("cl-qrcode-" + uuid());
- // 二维码组件元素
- const qrcodeRef = ref<UniElement | null>(null);
- // 二维码组件画布
- const canvasRef = ref(null);
- /**
- * 主绘制方法,根据当前 props 生成二维码并绘制到 canvas。
- * 支持多平台(APP、H5、微信小程序),自动适配高分屏。
- * 内部调用 drawQrcode 进行二维码点阵绘制。
- */
- function drawer() {
- const data = {
- text: props.text,
- size: getPx(props.width),
- foreground: props.foreground,
- background: props.background,
- padding: props.padding,
- logo: props.logo,
- logoSize: getPx(props.logoSize),
- ecc: "H", // 使用最高纠错级别
- mode: props.mode,
- pdColor: props.pdColor,
- pdRadius: props.pdRadius
- } as QrcodeOptions;
- nextTick(() => {
- // #ifdef APP || MP-WEIXIN
- uni.createCanvasContextAsync({
- id: qrcodeId.value,
- component: proxy,
- success(context) {
- drawQrcode(context, data);
- },
- fail(err) {
- console.error(err);
- }
- });
- // #endif
- // #ifdef H5
- // @ts-ignore
- drawQrcode(canvasRef.value, data);
- // #endif
- });
- }
- /**
- * 获取当前二维码图片的临时文件地址
- * @param call 回调函数,返回图片路径,失败返回空字符串
- */
- function toPng(): Promise<string> {
- return new Promise((resolve) => {
- // #ifdef APP
- qrcodeRef.value!.takeSnapshot({
- success(res) {
- resolve(res.tempFilePath);
- },
- fail(err) {
- console.error(err);
- resolve("");
- }
- });
- // #endif
- // #ifdef H5
- const url = URL.createObjectURL(
- base64ToBlob(
- (canvasRef.value as unknown as HTMLCanvasElement)
- .querySelector("canvas")
- ?.toDataURL("image/png", 1) ?? ""
- )
- );
- resolve(url);
- // #endif
- // #ifdef MP-WEIXIN
- uni.createCanvasContextAsync({
- id: qrcodeId.value,
- component: proxy,
- success(context) {
- // 获取2D绘图上下文
- const ctx = context.getContext("2d")!;
- const canvas = ctx.canvas;
- // 将canvas转换为base64格式的PNG图片数据
- const data = canvas.toDataURL("image/png", 1);
- // 获取base64数据部分(去掉data:image/png;base64,前缀)
- const bdataBase64 = data.split(",")[1];
- // 获取文件系统管理器
- const fileMg = uni.getFileSystemManager();
- // 生成临时文件路径
- // @ts-ignore
- const filepath = `${wx.env.USER_DATA_PATH}/${uuid()}.png`;
- // 将base64数据写入文件
- fileMg.writeFile({
- filePath: filepath,
- data: bdataBase64,
- encoding: "base64",
- success() {
- // 写入成功返回文件路径
- resolve(filepath);
- },
- fail() {
- // 写入失败返回空字符串
- resolve("");
- }
- });
- },
- fail(err) {
- console.error(err);
- resolve("");
- }
- });
- // #endif
- });
- }
- // 自动重绘
- const stopWatch = watch(
- computed(() => [
- props.pdColor,
- props.pdRadius,
- props.foreground,
- props.background,
- props.text,
- props.logo,
- props.logoSize,
- props.mode,
- props.padding
- ]),
- () => {
- drawer();
- }
- );
- onMounted(() => {
- setTimeout(
- () => {
- drawer();
- },
- isHarmony() ? 50 : 0
- );
- });
- onUnmounted(() => {
- stopWatch();
- });
- defineExpose({
- toPng
- });
- </script>
|