更新记录

1.1.0(2026-02-05) 下载此版本

✨ 新增功能

  • 二维码集成:内置 weapp-qrcode.js 库,无需额外依赖
    • 支持 colorDarkcolorLight 自定义二维码颜色
    • 支持 L/M/Q/H 四种纠错等级配置
    • 智能计算二维码版本和容量
    • 二维码实例缓存机制,避免重复创建


平台兼容性

uni-app(5.0)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
- -
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
- - - - - - - - - - -

uni-app x(4.87)

Chrome Safari Android iOS 鸿蒙 微信小程序
- - - - - -

cl-fastPoster

告别手动计算坐标,用 JSON 配置一键生成高清海报
基于 JSON 配置驱动的 Canvas 海报生成组件,支持流式布局


核心亮点: 传统 Canvas 绘图需要手动计算每个元素的 x、y 坐标,代码繁琐易出错。cl-fastPoster 采用类 CSS 的流式布局引擎,只需配置 JSON,自动完成坐标计算、二维码生成、高清导出,让海报生成像写配置文件一样简单。


✨ 特性

  • 🎨 JSON 驱动 - 使用声明式 JSON 配置生成海报
  • 📐 流式布局 - 类似 CSS 的相对定位和流式排版
  • 📱 单位自适应 - 输入 rpx,自动转换 px,支持高清导出
  • 🖼️ 异步资源 - 自动下载网络图片并缓存
  • 📱 二维码集成 - 内置 weapp-qrcode.js,支持自定义颜色和纠错等级
  • 🎯 高清导出 - 支持自定义 pixelRatio,导出 2x/3x 高清图片
  • 🔄 框架兼容 - 支持 Vue2、Vue3、小程序、App

📦 安装

cl-fastPoster 文件夹复制到项目的 uni_modules 目录下。

🚀 快速开始

基础使用

<template>
  <view>
    <button @click="generatePoster">生成海报</button>
    <image v-if="posterPath" :src="posterPath" mode="widthFix" />
    <cl-fast-poster 
      ref="poster" 
      :list="posterConfig"
      @success="onSuccess"
      @error="onError"
    />
  </view>
</template>

<script>
export default {
  data() {
    return {
      posterPath: '',
      posterConfig: [
        {
          type: 'container',
          style: {
            padding: 40,
            backgroundColor: '#ffffff'
          },
          children: [
            {
              type: 'text',
              content: 'Hello FastPoster!',
              style: {
                fontSize: 48,
                color: '#333333',
                fontWeight: 'bold'
              }
            }
          ]
        }
      ]
    };
  },
  methods: {
    generatePoster() {
      this.$refs.poster.generate();
    },
    onSuccess(path) {
      this.posterPath = path;
      console.log('海报生成成功:', path);
    },
    onError(err) {
      console.error('海报生成失败:', err);
    }
  }
};
</script>

📖 API 文档

Props

属性 类型 默认值 说明
list Array [] 海报配置数据
canvasId String 'fast-poster-canvas' Canvas ID
width Number 750 画布宽度(rpx)
height Number 1334 画布高度(rpx)
pixelRatio Number 2 导出图片像素比

Events

事件名 参数 说明
success (path: string) 生成成功,返回临时文件路径
error (error: Error) 生成失败,返回错误信息

Methods

方法名 参数 说明
generate - 开始生成海报

🎯 配置项详解

Element 类型

1. Container(容器)

{
  type: 'container',
  style: {
    width: 750,              // 宽度(rpx)
    padding: 40,             // 内边距(rpx),支持数组 [top, right, bottom, left]
    backgroundColor: '#fff', // 背景颜色
    borderRadius: 20,        // 圆角(rpx)
    flexDirection: 'column', // 布局方向:'column' | 'row'
    marginTop: 20,           // 外边距(rpx)
    marginBottom: 20
  },
  children: []               // 子元素数组
}

2. Image(图片)

{
  type: 'image',
  src: 'https://example.com/image.jpg', // 图片地址(支持网络/本地)
  style: {
    width: 200,              // 宽度(rpx)
    height: 200,             // 高度(rpx)
    borderRadius: 100,       // 圆角(rpx),设置为宽度一半可实现圆形
    mode: 'aspectFill',      // 裁剪模式
    marginTop: 20
  }
}

3. Text(文本)

{
  type: 'text',
  content: '这是一段文本',
  style: {
    fontSize: 32,            // 字体大小(rpx)
    color: '#333333',        // 文字颜色
    width: 600,              // 文本容器宽度(rpx)
    lineClamp: 2,            // 最多显示行数,超出显示省略号
    lineHeight: 48,          // 行高(rpx)
    fontWeight: 'bold',      // 字重:'normal' | 'bold'
    textAlign: 'left',       // 对齐:'left' | 'center' | 'right'
    marginTop: 20
  }
}

4. QRCode(二维码)

{
  type: 'qrcode',
  content: 'https://example.com',        // 二维码内容
  style: {
    width: 200,                           // 二维码大小(rpx)
    height: 200,                          // 高度(通常与宽度相同)
    colorDark: '#000000',                 // 前景色(二维码颜色)
    colorLight: '#FFFFFF',                // 背景色
    correctLevel: 'H',                    // 纠错等级:'L' | 'M' | 'Q' | 'H'
    marginTop: 20
  }
}

纠错等级说明:

  • L:约 7% 的字码可被修正
  • M:约 15% 的字码可被修正
  • Q:约 25% 的字码可被修正
  • H:约 30% 的字码可被修正(推荐,适合品牌 logo 覆盖)

🎨 完整示例

电商商品海报

const posterConfig = [
  {
    type: 'container',
    style: {
      width: 750,
      backgroundColor: '#f5f5f5',
      padding: 30
    },
    children: [
      // 背景图
      {
        type: 'image',
        src: 'https://example.com/bg.jpg',
        style: {
          width: 690,
          height: 800,
          borderRadius: 20,
          marginBottom: 20
        }
      },
      // 商品信息容器
      {
        type: 'container',
        style: {
          backgroundColor: '#ffffff',
          padding: 30,
          borderRadius: 20
        },
        children: [
          // 头像和店铺名
          {
            type: 'container',
            style: {
              flexDirection: 'row',
              marginBottom: 20
            },
            children: [
              {
                type: 'image',
                src: 'https://example.com/avatar.jpg',
                style: {
                  width: 80,
                  height: 80,
                  borderRadius: 40,
                  marginRight: 20
                }
              },
              {
                type: 'container',
                children: [
                  {
                    type: 'text',
                    content: '店铺名称',
                    style: {
                      fontSize: 32,
                      color: '#333333',
                      fontWeight: 'bold',
                      marginBottom: 10
                    }
                  },
                  {
                    type: 'text',
                    content: '欢迎光临',
                    style: {
                      fontSize: 24,
                      color: '#999999'
                    }
                  }
                ]
              }
            ]
          },
          // 商品标题
          {
            type: 'text',
            content: '【新品】这是一个非常长的商品标题,可能会换行显示,超过两行会显示省略号',
            style: {
              fontSize: 36,
              color: '#333333',
              fontWeight: 'bold',
              lineClamp: 2,
              lineHeight: 52,
              marginBottom: 20
            }
          },
          // 价格
          {
            type: 'container',
            style: {
              flexDirection: 'row',
              marginBottom: 30
            },
            children: [
              {
                type: 'text',
                content: '¥',
                style: {
                  fontSize: 32,
                  color: '#ff4444',
                  fontWeight: 'bold'
                }
              },
              {
                type: 'text',
                content: '99.00',
                style: {
                  fontSize: 56,
                  color: '#ff4444',
                  fontWeight: 'bold'
                }
              },
              {
                type: 'text',
                content: '¥199.00',
                style: {
                  fontSize: 28,
                  color: '#999999',
                  marginLeft: 20
                }
              }
            ]
          },
          // 分割线容器
          {
            type: 'container',
            style: {
              height: 1,
              backgroundColor: '#eeeeee',
              marginBottom: 30
            }
          },
          // 二维码提示
          {
            type: 'container',
            style: {
              flexDirection: 'row'
            },
            children: [
              {
                type: 'container',
                style: {
                  flex: 1
                },
                children: [
                  {
                    type: 'text',
                    content: '长按识别二维码',
                    style: {
                      fontSize: 28,
                      color: '#666666',
                      marginBottom: 10
                    }
                  },
                  {
                    type: 'text',
                    content: '立即购买',
                    style: {
                      fontSize: 32,
                      color: '#333333',
                      fontWeight: 'bold'
                    }
                  }
                ]
              },
              {
                type: 'qrcode',
                content: 'https://example.com/product/123',
                style: {
                  width: 180,
                  height: 180,
                  colorDark: '#000000',
                  colorLight: '#FFFFFF',
                  correctLevel: 'H'
                }
              }
            ]
          }
        ]
      }
    ]
  }
];

💡 核心原理

1. 单位系统

  • 输入:rpx(响应式像素)
  • 转换:px = rpx * (systemInfo.windowWidth / 750)
  • 导出:实际像素 = px * pixelRatio

2. 布局引擎

预计算阶段(Pre-layout)
├── 递归遍历 JSON 树
├── 计算每个元素的尺寸(width, height)
├── 计算相对坐标(x, y)
└── 生成布局树(Computed Tree)

绘制阶段(Draw)
├── 遍历布局树
├── 按计算好的坐标绘制
└── 导出高清图片

3. 流式布局

column 模式下:

  • 每个元素的 Y 坐标 = 上一个元素底部 + 当前元素 marginTop
  • 容器高度 = 所有子元素高度之和 + padding

4. 异步资源

  • 收集所有网络图片 URL
  • 使用 Promise.all 批量下载
  • 缓存到 localImageMap
  • 绘制时使用本地路径

⚠️ 注意事项

  1. 高清导出:默认 pixelRatio=2 导出 2 倍图,移动端推荐设置为 2-3
  2. 二维码内容:建议使用 correctLevel='H' 确保扫码成功率
  3. 性能优化:大量图片时建议控制尺寸和质量,避免内存溢出
  4. 平台差异:不同平台的 Canvas API 可能有细微差异
  5. 文本测量:部分平台不支持 ctx.measureText,组件已做降级处理
  6. Canvas 尺寸:Canvas 物理尺寸为 逻辑尺寸 * pixelRatio,确保高清显示

🎯 高清图片导出

组件通过以下机制实现高清图片导出:

// 1. Canvas 物理尺寸使用高倍率
<canvas :width="canvasWidth * pixelRatio" :height="canvasHeight * pixelRatio" />

// 2. 导出时指定目标分辨率
uni.canvasToTempFilePath({
  width: canvasWidth,           // 逻辑像素截取
  height: canvasHeight,
  destWidth: canvasWidth * 2,   // 输出 2 倍图
  destHeight: canvasHeight * 2
})

推荐配置:

  • 手机端:pixelRatio: 2(默认)
  • 高端设备:pixelRatio: 3
  • 打印用途:pixelRatio: 4

🔧 进阶使用

动态生成配置

methods: {
  createPosterConfig(data) {
    return [
      {
        type: 'container',
        style: { padding: 30, backgroundColor: '#fff' },
        children: [
          {
            type: 'text',
            content: data.title,
            style: { fontSize: 36, fontWeight: 'bold' }
          },
          {
            type: 'image',
            src: data.imageUrl,
            style: { width: 690, height: 400, marginTop: 20 }
          },
          {
            type: 'qrcode',
            content: data.qrUrl,
            style: { 
              width: 200, 
              height: 200,
              colorDark: '#000000',
              colorLight: '#FFFFFF',
              correctLevel: 'H',
              marginTop: 20
            }
          }
        ]
      }
    ];
  },

  async loadDataAndGenerate() {
    const data = await this.fetchProductData();
    this.posterConfig = this.createPosterConfig(data);
    await this.$nextTick();
    this.$refs.poster.generate();
  }
}

自定义二维码样式

// 黑色二维码(默认)
{
  type: 'qrcode',
  content: 'https://example.com',
  style: {
    width: 200,
    height: 200,
    colorDark: '#000000',
    colorLight: '#FFFFFF',
    correctLevel: 'H'
  }
}

// 品牌色二维码
{
  type: 'qrcode',
  content: 'https://example.com',
  style: {
    width: 200,
    height: 200,
    colorDark: '#ff6b35',     // 橙色前景
    colorLight: '#fff9f5',    // 浅橙背景
    correctLevel: 'H'
  }
}

保存到相册

methods: {
  async savePoster() {
    try {
      await uni.saveImageToPhotosAlbum({
        filePath: this.posterPath
      });
      uni.showToast({ title: '保存成功' });
    } catch (err) {
      console.error('保存失败:', err);
      uni.showToast({ title: '保存失败', icon: 'none' });
    }
  }
}

🐛 常见问题

1. 二维码不显示?

  • 检查 content 是否为空
  • 确认 widthheight 设置正确
  • 查看控制台是否有 QRCode 错误日志

2. 图片模糊?

  • 提高 pixelRatio(默认为 2,可设置为 3)
  • 确保原始图片分辨率足够高
  • 检查 Canvas 尺寸是否正确

3. 第一次生成白屏?

  • 已通过延迟初始化修复(100ms Canvas 初始化 + 500ms 导出延迟)
  • 如仍有问题,可适当增加延迟时间

4. 内容被截断?

  • 确保 canvasToTempFilePathwidth/height 使用逻辑像素
  • destWidth/destHeight 使用物理像素(逻辑像素 * pixelRatio)

📚 技术栈

  • 二维码生成:weapp-qrcode.js(内置)
  • Canvas API:uni-app Canvas 2D 上下文
  • 布局引擎:自研流式布局算法
  • 图片处理:uni.downloadFile + uni.canvasToTempFilePath

📄 License

MIT

隐私、权限声明

1. 本插件需要申请的系统权限列表:

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

许可协议

MIT协议

暂无用户评论。