Taro3+小程序Canvas动态生成海报和二维码分享到朋友圈

一、海报生成核心技术流程

1、Canvas初始化准备

  • import { Canvas, } from "@tarojs/components";使用隐藏的Canvas组件(通过绝对定位移出视口):
ini 复制代码
<Canvas
    canvasId="posterCanvas"
    style={{ position: "absolute", left: "-999px" }}
/>
  • 通过 Taro.createCanvasContext 获取绘图上下文 Canvas实例获取
ini 复制代码
useReady(
    () => { 
    const ctx = Taro.createCanvasContext("posterCanvas");
    canvasRef.current = ctx; }
);

2、动态二维码生成

  • 使用 drawQrcode 库生成二维码图片,指定颜色和尺寸:
    1. NPM 安装(推荐)看自己需要啦 我下载的2D
css 复制代码
npm install weapp-qrcode --save # 基础版(适用于旧版Canvas API)
npm install weapp-qrcode-canvas-2d --save # 新版(Canvas 2D API,性能更佳)
    1. 手动引入 JS 文件 从 GitHub 下载 dist 目录中的文件:
javascript 复制代码
import drawQrcode from '../../utils/weapp.qrcode.min.js';

🔗 二、核心链接

weapp-qrcode(基础版)

weapp-qrcode-canvas-2d(新版,支持 Canvas 2D)

三、生成二维码

less 复制代码
const qrTempPath = await new Promise((resolve) => { 
    drawQrcode({ width: 100,
        height: 100,
        canvasId: "qrCanvas",
        text: url, // 分享链接
        foreground: "#1EAFB3",
        callback: () => { Taro.nextTick(
            () => { Taro.canvasToTempFilePath({ 
                canvasId: "qrCanvas",
                success: (res) => resolve(res.tempFilePath) },
                Taro.getCurrentInstance().page); 
                }); 
       } 
    }); 
 });
  • 使用weapp-qrcode库生成二维码
  • 转为临时文件路径
  • Taro.nextTick确保Canvas渲染完成

四、 绘制海报

scss 复制代码
const ctx = Taro.createCanvasContext("posterCanvas", 
    Taro.getCurrentInstance().page); // 分层绘制 
    ctx.drawImage(bgImg.path, 0, 0, 343, 576); // 背景
    ctx.drawImage(avatarImg.path, 32, 460, 48, 48); // 头像 
    ctx.setFontSize(16); 
    ctx.setFillStyle("#2F3134");
    ctx.fillText(userInfo?.nickName, 80, 480); // 用户名
    ctx.setFontSize(14); 
    ctx.setFillStyle("#8391A1"); 
    ctx.fillText("邀您免费体验", 80, 504); // 描述 
    ctx.drawImage(qrImg.path, 211, 434, 100, 100); // 二维码  
// 这个二维码是我上面动态生成的二维码 因为我们的需里面要求有动态的埋点二维码所以就动态生成啦 参数都在生成二维码的url上面 看你们的需求啦

五、 生成海报图片并直接掉起分享

javascript 复制代码
     // 4. 生成分享图
      ctx.draw(false, async () => {
        const { tempFilePath } = await Taro.canvasToTempFilePath({
          canvasId: "posterCanvas",
          quality: 1,
        });

     // 5. 直接调起分享
        Taro.showShareImageMenu({
          path: tempFilePath,
          needShowEntrance: false,
          success: () => {
            Taro.hideLoading();
            setPosterVisible(false);
          },
          fail: (err) => {
            Taro.hideLoading();
            setPosterVisible(false);
            // Taro.showToast({
            //   title: `分享失败: ${err.errMsg}`,
            //   icon: "none",
            // });
          },
        });
      });
    

六、 整体代码示例(仅供参考)

javascript 复制代码
import Taro, {
  useReady,
  useShareTimeline,
  useShareAppMessage,
  usePullDownRefresh,
} from "@tarojs/taro";
import React, { useState, useEffect, useRef } from "react";
import {
  View,
  Canvas,
} from "@tarojs/components";
import {
  AtButton,
} from "taro-ui";
import drawQrcode from "weapp-qrcode";


import "./index.less";

export default function Mycanvas() {
  const userInfo = useUserInfoStore().use.userInfo();
  const canvasRef = useRef(null); //海报动态生成
  const qrCanvasRef = useRef(null); // 二维码Canvas引用

  const [posterVisible, setPosterVisible] = useState(false);


  //Canvas实例获取
  useReady(() => {
    const ctx = Taro.createCanvasContext("posterCanvas", this);
    canvasRef.current = ctx;

    qrCanvasRef.current = Taro.createCanvasContext("qrCanvas", this);
  });

  useShareAppMessage(() => ({
    title: "xxx",
    path: `xxx&ts=${Date.now()}`, // 携带分享来源参数
    imageUrl:
      "xxx", // 必须为网络图片
  }));



  // 生成二维码
  // 这里的url参数可能要加上用户的id 方便裂变
  // 生成二维码
  const url = `xxx&ts=${Date.now()}`;

  const handleGeneratePoster = async () => {
    try {
      setPosterVisible(true);
      Taro.showLoading({ title: "生成中..." });
      // 1. 生成二维码图片
      const qrTempPath = await new Promise((resolve, reject) => {
        // 2. 绘制二维码(#1EAFB3)
        drawQrcode({
          width: 100,
          height: 100,
          canvasId: "qrCanvas",
          text: url,
          foreground: "#1EAFB3", // 二维码颜色
          callback: () => {
            Taro.nextTick(() => {
              Taro.canvasToTempFilePath(
                {
                  canvasId: "qrCanvas",
                  success: (res) => {
                    resolve(res.tempFilePath);
                  },
                },
                Taro.getCurrentInstance().page
              );
            });
          },
        });
      });

      // 并行预加载所有图片
      const [bgImg, avatarImg, qrImg] = await Promise.all([
        Taro.getImageInfo({
          src:
            "xxx",
        }),
        Taro.getImageInfo({
          src:
            "xxx",
        }),
        Taro.getImageInfo({
          src: qrTempPath,
          success: (res) => {
            // console.log("res", res);
          },
        }),
      ]);

      // 2. 获取Canvas上下文
      const ctx = Taro.createCanvasContext(
        "posterCanvas",
        Taro.getCurrentInstance().page
      );

      // 3. 绘制元素(使用本地路径)
      ctx.drawImage(bgImg.path, 0, 0, 343, 576);
      ctx.drawImage(avatarImg.path, 32, 460, 48, 48);

      ctx.setFontSize(16);
      ctx.setFillStyle("#2F3134");
      ctx.fillText(xxx, 80, 480); // y: 460+20

      ctx.setFontSize(14);
      ctx.setFillStyle("#8391A1");
      ctx.fillText("xxx", 80, 504); // y: 480+24
      ctx.drawImage(qrImg.path, 211, 434, 100, 100);

      // 4. 生成分享图
      ctx.draw(false, async () => {
        const { tempFilePath } = await Taro.canvasToTempFilePath({
          canvasId: "posterCanvas",
          quality: 1,
        });

        // 5. 直接调起分享
        Taro.showShareImageMenu({
          path: tempFilePath,
          needShowEntrance: false,
          success: () => {
            Taro.hideLoading();
            setPosterVisible(false);
          },
          fail: (err) => {
            Taro.hideLoading();
            setPosterVisible(false);
            // Taro.showToast({
            //   title: `分享失败: ${err.errMsg}`,
            //   icon: "none",
            // });
          },
        });
      });
    } catch (error) {
      setPosterVisible(false);
      Taro.hideLoading();
      Taro.showToast({
        title: `生成失败: ${error.errMsg}`,
        icon: "none",
        duration: 3000,
      });
    }
  };

  return (
    <View className="pageContainer">
         <AtButton className="shareBtn" openType="share" onClick={handleShare}>
            直接转发
          </AtButton> 

          <AtButton
            className="posterBtn"
            disabled={posterVisible}
            onClick={handleGeneratePoster}
          >
            生成海报
          </AtButton>

      <Canvas
        style={{
          position: "absolute",
          left: "-999px",
          width: "343px",
          height: "577px",
        }}
        canvasId="posterCanvas"
      />
      {/* 二维码的 */}
      <Canvas
        canvasId="qrCanvas"
        style={{
          position: "absolute",
          left: "-9999px",
          width: "100px",
          height: "100px",
        }}
      />

    </View>
  );
}

七、技术要点总结

关键技术 实现方式
二维码生成 weapp-qrcode + Canvas隐藏渲染
多图合成 drawImage分层绘制 + Promise.all预加载
分享路径带参 path:xxx
微信分享菜单 showShareImageMenu直接调起
跨屏兼容处理 绝对定位移出视口(left: -999px)
高清质量保障 quality: 1参数 + 固定尺寸Canvas(343x577)

八、遇到的问题

  • Taro.showShareImageMenu分享朋友圈报错: {"errMsg": "showShareImageMenu:fail forbidden"} 或者 {"errMsg": "showShareImageMenu:fail upload timeout"} 因为不支持分享朋友圈带有二维码,这是俺踩的坑呜呜呜,微信基础库从3.8.2开始支持分享朋友圈,但是目前没有办法动态配置这个,也没办法隐藏这个,除非改成小程序二维码,看自己需求了
相关推荐
像风一样自由20202 小时前
HTML与JavaScript:构建动态交互式Web页面的基石
前端·javascript·html
aiprtem2 小时前
基于Flutter的web登录设计
前端·flutter
浪裡遊2 小时前
React Hooks全面解析:从基础到高级的实用指南
开发语言·前端·javascript·react.js·node.js·ecmascript·php
why技术2 小时前
Stack Overflow,轰然倒下!
前端·人工智能·后端
GISer_Jing2 小时前
0704-0706上海,又聚上了
前端·新浪微博
止观止3 小时前
深入探索 pnpm:高效磁盘利用与灵活的包管理解决方案
前端·pnpm·前端工程化·包管理器
whale fall3 小时前
npm install安装的node_modules是什么
前端·npm·node.js
烛阴3 小时前
简单入门Python装饰器
前端·python
袁煦丞4 小时前
数据库设计神器DrawDB:cpolar内网穿透实验室第595个成功挑战
前端·程序员·远程工作
天天扭码4 小时前
从图片到语音:我是如何用两大模型API打造沉浸式英语学习工具的
前端·人工智能·github