评论

wxml2canvas-2d:简单易用的小程序海报、战绩等分享图片生成方案

基于微信小程序 2D Canvas 的画布组件,根据给定 WXML 结构以及 CSS 样式快速转换成 Canvas 元素,以用于生成海报图片分享等操作。所见即所得(bushi

Github 地址:https://github.com/ChrisChan13/wxml2canvas-2d

介绍

当前,众多小程序的多处场景都需要能够生成分享图便于用户进行二次传播,从而提升小程序的传播率以及加强品牌效应。
比较简单的分享图,如寥寥几行文字和一张小程序码,可以通过微信的 Canvas API 绘制。旧版 Canvas API 绘制过程繁琐,且每次绘制都需要调用 draw 方法,一不小心代码就写了上百行。
新版 Canvas API 基本与 Web Canvas 对齐,使得开发效率提高、性能得到优化。虽然免去了很多繁琐操作,但面对拥有元素众多、结构复杂的分享图片,依然解决不了代码冗长的问题。
目前开源的一些小程序图片生成方案,有的年久失修、有的依然使用旧版 Canvas API、有的使用方式不够简便,于是便有了开发 wxml2canvas-2d 的想法。
wxml2canvas-2d 的图片生成方式简单直观:首先在 wxml 页面上编写元素结构,其次在 wxss 中编写元素样式,最后调用 wxml2canvas-2d 的相关方法即可生成所需的分享图片。
wxml2canvas-2d 会通过 class 类名查询元素节点的 computedStyle 和节点属性,从而将元素节点绘制到画布上。这样做的好处是在编写 wxml 结构以及样式时,可以直观的看见样式的变化,方便调整。当然也有坏处,这个用来生成图片的“wxml 模板”,必须存在于页面上。若需要隐藏这个“模板”,只可用定位将其移至屏幕外,不可以使用 wx:if 或 hidden 隐藏。
wxml2canvas-2d 已经支持大部分常用的 CSS 属性,等你来测~

示例

克隆此 Github 仓库,运行 npm i & npm run dev,将 miniprogram_dev 文件夹导入微信开发者工具

效果预览

小程序内容:

生成的图片:

安装

npm

使用 npm 构建前,请先阅读微信官方的 npm 支持

# 通过 npm 安装
npm i wxml2canvas-2d -S --production

构建 npm 包

打开微信开发者工具,点击 工具 -> 构建 npm,并勾选 使用 npm 模块 选项,构建完成后,即可引入组件。

使用

  1. 在页面配置中引入 wxml2canvas-2d ;
{
  "usingComponents": {
    "wxml2canvas": "wxml2canvas-2d"
  }
}
  1. 在页面中编写 wxml 结构,将要生成画布内容的根节点用名为 wxml2canvas-container 的样式类名称标记,将该根节点内部需要生成画布内容的节点用名为 wxml2canvas-item 的样式类名称标记(文字类节点需在对应节点声明 data-text 属性,并传入文字内容)。上述两个样式类名称可以自定义,只需将对应名称传入 wxml2canvas 组件的对应属性参数即可;
<!-- pages/index/index.wxml -->
<view class="wxml2canvas-container box">
  <view class="wxml2canvas-item title" data-text="测试标题">测试标题</view>
  <image class="wxml2canvas-item image" src="/your-image-path.png" />
  <view class="wxml2canvas-item content" data-text="测试内容,长文本。。">测试内容,长文本。。</view>
</view>
<button catchtap="generateSharingCard">生成画布内容</button>
<wxml2canvas id="wxml2canvas" />
  1. 补充各个节点样式;
/* pages/index/index.wxss */
.box { /* 根节点(容器)的样式 */ }
.title { /* 标题的样式 */ }
.image { /* 图片的样式 */ }
.content { /* 内容的样式 */ }
  1. 依据 wxml 结构以及 css 样式,生成画布内容,并将生成结果导出。
// pages/index/index.js
Page({
  async generateSharingCard() {
    const canvas = this.selectComponent('#wxml2canvas');
    await canvas.draw();
    const filePath = await canvas.toTempFilePath();
    wx.previewImage({
      urls: [filePath],
    });
  },
});

更多内容及文档 点击此处 前往查看!如果有好的建议或者想法,也欢迎提交 Issue 或 PR~

最后一次编辑于  2024-11-21  
点赞 1
收藏
评论

8 个评论

  • 夏味
    夏味
    02-21

    你好,想请教下,canvas 画布大小有限制,无法生成超长图片。这个问题有什么解决方案吗?有没有可能不用 canvas,直接使用 JS 生成图片?

    02-21
    赞同
    回复 5
    • 杰
      02-21
      微信新版 Canvas API 的画布大小有限制,这个没办法突破。在小程序内生成图片基本绕不开 Canvas,个人觉得有以下方案可参考,希望可以帮到你:
      1. 将内容分成片段,不超过大小限制,按顺序逐一绘制到 Canvas 上并导出,最后拼成一张完整图片;

      2. 使用官方 Skyline 渲染引擎,使用 snapshot 组件,https://developers.weixin.qq.com/miniprogram/dev/component/snapshot.html
      3. 由后端提供图片生成、使用无头浏览器渲染并截图等。
      02-21
      1
      回复
    • 夏味
      夏味
      02-24回复
      好的,非常感谢
      02-24
      回复
    • 杰
      02-24回复夏味
      不客气!若有其他方案希望也可以分享分享
      02-24
      回复
    • 夏味
      夏味
      02-24回复
      我试了下拼接图片的方案1,可行,但是速度太慢,不含 canvas 绘制事件,也需要 3 秒以上了。三方依赖库,大约 70 KB

      canvas 绘制出文字 + 图片(图1)

      1. ctx.getImageData 获取到每个像素的颜色数据 Uint8ClampedArray
      2. 拼接图片,将两份数据合并
      3. JS 库生成图片数据,高度 * 2
      UPNG.encode([bitmapData2.buffer], width, height * 2, 0)

      图3 是原始图片尺寸,和每个步骤的结束时间
      02-24
      回复
    • 杰
      02-25回复夏味
      确实,大图片的像素数据合并起来太费时间,在各种老旧设备的用户手中只会更慢,不过可能还有优化空间,比如:
      - 第三方库的合并方法可能有优化空间,删除非必要逻辑
      - 运用小程序的多线程 Worker 进行合并,减少主线程阻塞
      - 涉及画布的内容使用 OffscreenCanvas,减少主线程阻塞


      但做这些优化和测试,有点费时费力
      02-25
      1
      回复
  • 🍀111
    🍀111
    02-17

    您好,我在Taro框架上使用,文字渲染不出来是什么原因?代码就是示例代码,图片也可以展示,但是文字渲染不出来

    02-17
    赞同
    回复 9
    • 杰
      02-17
      看了一下 Taro 的文档,里面关于 dataset 的描述:https://docs.taro.zone/docs/vue-overall/#dataset。由于 wxml2canvas-2d 组件渲染文字用的是 data-text 属性来传入文字内容,但 Taro 不会真正注入 data-text,所以导致文字内容丢失。可以试试 Taro 文档中提到的 taro-plugin-inject 插件,将 data-text 属性配置注入到 Text 和 View 组件上。
      02-17
      回复
    • 🍀111
      🍀111
      02-17回复
      感谢,我试一试!
      02-17
      回复
    • 🍀111
      🍀111
      02-18
      注入了data-text,但是显示的就是配置后的固定值,页面是否引入该属性或者修改属性的值,都没反应。
      02-18
      回复
    • 杰
      02-18回复🍀111
      我没有使用过 taro-plugin-inject 插件,但藉由文档,我理解的配置意思应该是:
      02-18
      回复
    • 杰
      02-18回复🍀111
      或者配置中去掉单引号、配置的值赋为空字符串等,都可以尝试一下
      02-18
      回复
    查看更多(4)
  • Kaho💪
    Kaho💪
    02-08

    在mac设备上生成时会有大部分内容缺失 这是什么原因导致的呢

    02-08
    赞同
    回复 3
    • 杰
      02-09
      请问可以提供一下代码片段和相关 bug 表现的截图吗?由于没有 mac 设备,不是很好调试
      02-09
      回复
    • Kaho💪
      Kaho💪
      02-10回复
      是这样的 下面分享的第一个是从mac设备生成的 第二个是从手机设备生成的 手机设备生成出来的内容正常 但是mac设备生成出来的图里面文字全都消失了 这是什么原因导致的呢
      02-10
      回复
    • 杰
      02-10回复Kaho💪
      看不出头绪,mac 生成的分享卡片上蓝色的色块是背景图是吗?出现了错位并且被置于顶层了,因为有“保存名片”四个字是正确绘制了的,会不会是文字被盖住了?有没有办法提供一份能复现问题的代码片段呢?要不实在有些难以排查。
      https://developers.weixin.qq.com/miniprogram/dev/devtools/minicode.html
      02-10
      回复
  • CityHunter蕐
    CityHunter蕐
    01-22

    请问Uniapp上如何使用?

    01-22
    赞同
    回复 1
  • Mr.磊
    Mr.磊
    01-14

    01-14
    赞同
    回复 16
    • 杰
      01-14
      请检查 wxml 中是否包含 “wxml2canvas-container” 样式类名称的元素,或者为组件传入了 containerClass 属性,但与 wxml 中不一致,导致绘制时找不到容器元素
      01-14
      回复
    • Mr.磊
      Mr.磊
      01-14
      我的是在组件内部写的
      01-14
      回复
    • 杰
      01-14
      经测试发现在组件内部的确会出现该报错内容,已经找问题所在,问题将在下版本进行修复!感谢!
      01-14
      回复
    • Mr.磊
      Mr.磊
      01-14
      可以自定义背景吗?
      怎么做
      01-14
      回复
    • 杰
      01-14
      支持 background-color 和 background-image,只要写在对应 wxml 节点的样式中即可
      01-14
      回复
    查看更多(11)
  • bigGang
    bigGang
    01-09

    依据 wxml 结构以及 css 样式,生成画布内容,并将生成结果导出。

    文字加粗的问题css中的加粗会丢失,该如何解决

    01-09
    赞同
    回复 1
    • 杰
      01-10
      请问可以提供一下代码片段吗?
      01-10
      回复
  • W先生
    W先生
    01-06

      <view class="wxml2canvas-item descdata-text="这是证书内容">这是证书 <text data-text="hasdasd可自行查看最新class="wxml2canvas-item">hasdasd可自行查看最新</text> 内容</view>

    这个有嵌套的就重叠了

    01-06
    赞同
    回复 2
    • 杰
      01-07
      这类情况建议为每一段文字添加 text 节点并用 class 标记,目前无法识别文字和标签混合的情况,修改为如下:
      01-07
      回复
    • 杰
      01-07
      若多个 text 节点组合成的文字涉及换行,可能需要手动对每一行添加 text 节点进行分割,但在单个 text 内就不会出现上述的问题
      01-07
      回复
  • 逆水寒
    逆水寒
    2024-12-22

    <image class="wxml2canvas-item image" src="/your-image-path.png" />

    根据以上代码,我把图片路径改为小程序项目的图片路径:

    <image class="wxml2canvas-item imagesrc="../../../images/logo.jpg" />

    就提示找不到图片:

    GET http://127.0.0.1:37163/__pageframe__/miniprogram_npm/wxml2canvas-2d/images/logo.jpg 500 (Internal Server Error)(env: Windows,mp,1.06.2405020; lib: 3.5.1)

    根据这个提示,我把我的图片复制到miniprogram_npm/wxml2canvas-2d/这个路径下,就能正常显示,这样就出现一个问题,我要生成的海报图片中,需要嵌入的一些图片,经常是不固定的,即每次生成的海报中的图片是从数据库获取的,那这些图片又怎样渲染到海报中呢?

    2024-12-22
    赞同
    回复 1
    • 杰
      2024-12-24
      推荐图片使用绝对路径或者网络图片,使用相对路径的话目前组件无法定位该图片的真实路径
      2024-12-24
      回复
登录 后发表内容