更新记录

1.0.0(2026-06-15) 下载此版本

jz-layer-poster 更新日志

v1.0.0 (2026-06-15)

首个正式版本。

核心能力

  • 全端兼容 — H5、App-Vue、微信 / 支付宝 / 字节 / 百度 / QQ / 快手 / 京东 小程序统一接入
  • Class 架构 — 每种图层独立为类(BaseLayer / ImageLayer / TextLayer / RectLayer / CircleLayer / LineLayer / QrcodeLayer / WatermarkLayer),渲染器、解析器、链式构建器均为独立类
  • Canvas 2D 绘制 — 小程序优先使用 Canvas 2D API,H5 / App 自动降级到旧版 uni.createCanvasContext
  • 统一 PosterSchema 协议 — 五种调用方式最终都被解析为同一份 schema 数据,由 LayerParser + CanvasRenderer 统一渲染

五种调用方式

  • JSONdrawByJSON(jsonString | object, options)
  • JS 对象drawByObject(schema, options),支持函数式动态值
  • 链式调用create(w, h).image().text().rect().circle().line().qrcode().watermark().draw()
  • Vue 组件 ref(schema 模式)drawByRef(ref, { layers })
  • Vue 组件 ref(DOM 截图模式)drawByRef(ref, { mode: 'dom' | 'dom-as-image' }),仅 H5 / App-Vue

图层类型

  • image — 图片图层(http/https / 本地 / base64),支持圆角与三种填充模式(fill / aspectFill / aspectFit
  • text — 文字图层,支持自动换行(maxWidth)、行数截断(lineClamp)、省略号(overflow: 'ellipsis')、字间距、装饰线、阴影
  • rect — 矩形,支持圆角、边框
  • circle — 圆形,支持边框
  • line — 直线,支持虚线(lineDash
  • qrcode — 二维码,支持容错等级(L/M/Q/H)、内边距、圆角背景、中心 logo(圆形 / 圆角 / 描边 / 内边距)
  • watermark — 水印图层,支持文字 / 图片两种内容,single / tile / tile-x / tile-y 四种排布模式,9 宫格预设位置 + 自定义坐标,旋转,整体裁剪到画布

渐变能力

  • 背景渐变 / 边框渐变 / 文字渐变 — 所有 *Color / backgroundColor / strokeColor / color 字段以及海报全局 backgroundColor 均支持传入 GradientSpec 对象,旧的字符串色值完全兼容
  • 类型linear(含 angle 角度模式与 from/to 端点模式)、radial(圆心比例 + stops)
  • 文字渐变 — 按文字实际像素宽高自动计算端点,支持多行、letterSpacing 场景

DOM 截图(H5 + App-Vue)

  • 通过 html2canvas + renderjs 把普通 vue 模板直接截图为海报,无需任何 data-poster 标记
  • H5 / App-Vue 共用同一份 renderjs 模块dom-capture.renderjs.js),调用方无需区分平台
  • 两种模式:
    • 'dom' — 直接将截图导出为最终海报
    • 'dom-as-image' — 将截图作为最底层图层,再叠加 layers 数组中的 watermark / qrcode 等内容
  • html2canvaspeerDependency,按需安装

可视化拖拽编辑器

  • JzPosterEditor 类 — H5 端通过 createApp 动态挂载到 document.body,App / 小程序端通过 <jz-poster-editor> 组件声明式挂载
  • 拖拽实现 — 全端统一使用 movable-area + movable-view,不区分平台
  • 图层操作 — 选中后显示左上角移动、右上角删除、右下角缩放 + 旋转控制点
  • 图层管理 — 排序、显隐、锁定、删除
  • 属性面板 — 样式 / 位置 / 大小可视化编辑
  • 导出 — JSON 字符串、JS 对象、链式调用代码三种格式
  • 导入 — 通过 JSON 还原编辑状态,支持二次编辑

组件 API(<jz-layer-poster>

  • Props:width / height / backgroundColor / unit / fileType / quality / hideCanvas
  • 方法:draw(schema, options) / drawByJSON(json, options) / captureDom(payload, timeout) / saveToAlbum(tempFilePath)
  • 事件:drawProgress / drawComplete / drawError

JS API

  • jzLayerPoster.create(w, h, options) — 创建链式构建器
  • jzLayerPoster.drawByJSON / drawByObject / drawByRef / drawBySchema — schema 渲染
  • jzLayerPoster.editor.open(options) / .close() — 启动 / 关闭可视化编辑器
  • jzLayerPoster.getVersion() — 获取版本号

多端兼容

H5、App-Vue、微信 / 支付宝 / 字节跳动 / 百度 / 快手 / 京东 / QQ 小程序。

注:App-NVue 不支持(无 Canvas 2D & 无 DOM);DOM 截图模式仅支持 H5 / App-Vue。

默认依赖

  • html2canvas(peerDependency,仅 DOM 截图模式需要)

平台兼容性

uni-app(4.62)

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

jz-layer-poster

全端兼容的 uni-app 海报绘制插件。基于 Canvas 2D 绘制,支持 JSON / JS 对象 / 链式调用 / Vue ref / DOM 截图 五种调用方式,内置可视化拖拽编辑器。Class 架构,每种图层独立类,易于扩展。

version license


目录


核心特性

特性 说明
全端兼容 H5、App-Vue、微信 / 支付宝 / 字节 / 百度 / QQ / 快手 / 京东 小程序
五种调用方式 JSON / JS 对象 / 链式调用 / Vue ref / DOM 截图
Canvas 2D 绘制 小程序端优先使用 Canvas 2D API,H5 / App 自动降级
7 种图层类型 image / text / rect / circle / line / qrcode / watermark
渐变能力 背景 / 边框 / 文字 三处均支持 linear / radial 渐变
二维码增强 容错等级、内边距、圆角背景、中心 logo(圆形 / 圆角 / 描边)
水印图层 文字 / 图片两种内容形态,single / tile / tile-x / tile-y 四种排布,支持旋转
DOM 截图 H5 / App-Vue 通过 html2canvas + renderjs 把 vue 模板直接转成海报
可视化编辑器 内置拖拽编辑器(H5 动态挂载,App / 小程序声明式挂载),可导出 JSON / 链式代码
Class 架构 每种图层独立类,渲染器 / 解析器 / 构建器 均为独立类,便于二次扩展

快速开始

1. 安装

jz-layer-poster 目录放入项目的 src/uni_modules/ 下,uni-app 会自动通过 easycom 注册组件。

如需使用 DOM 截图 功能(H5 / App-Vue 端),请额外安装 html2canvas

npm install html2canvas

2. 在模板中声明式调用(推荐,全端通用)

<template>
  <view>
    <jz-layer-poster ref="posterRef" :width="375" :height="600" />
    <button @click="onDraw">生成海报</button>
    <image v-if="tempFilePath" :src="tempFilePath" mode="widthFix" />
  </view>
</template>

<script setup>
import { ref } from 'vue'

const posterRef = ref(null)
const tempFilePath = ref('')

async function onDraw() {
  const res = await posterRef.value.draw({
    width: 375,
    height: 600,
    backgroundColor: '#fff',
    layers: [
      { type: 'image', src: 'https://example.com/bg.png', x: 0, y: 0, width: 375, height: 600 },
      { type: 'text',  content: '活动标题', x: 30, y: 120, fontSize: 28, color: '#333', fontWeight: 'bold' },
      { type: 'qrcode', content: 'https://example.com', x: 137, y: 460, size: 100 }
    ]
  })
  tempFilePath.value = res.tempFilePath
}
</script>

3. JS API 直接调用(无需写组件标签)

import jzLayerPoster from '@/uni_modules/jz-layer-poster'

const res = await jzLayerPoster
  .create(375, 600, { backgroundColor: '#fff' })
  .image('https://example.com/bg.png', 0, 0, 375, 600)
  .text('活动标题', { x: 30, y: 120, fontSize: 28, color: '#333', fontWeight: 'bold' })
  .qrcode('https://example.com', 137, 460, 100)
  .draw({ canvasId: 'xxx', componentInstance: this })

console.log(res.tempFilePath)

JS API 调用时必须显式传入 canvasIdcomponentInstance,因此推荐通过组件 ref 调用(见 JS API)。


五种调用方式

方式 1:JSON 字符串

const schema = JSON.stringify({
  width: 375, height: 600,
  layers: [
    { type: 'rect', x: 0, y: 0, width: 375, height: 600, backgroundColor: '#1a1a2e' },
    { type: 'text', content: 'Hello', x: 30, y: 100, fontSize: 24, color: '#fff' }
  ]
})
await posterRef.value.drawByJSON(schema)

方式 2:JS 对象(支持函数 / 动态值)

await jzLayerPoster.drawByObject({
  width: 375, height: 600,
  layers: [
    { type: 'text', content: () => `当前时间:${Date.now()}`, x: 20, y: 50 }
  ]
}, { canvasId, componentInstance })

方式 3:链式调用

jzLayerPoster.create(375, 600)
  .rect(0, 0, 375, 600, { backgroundColor: '#fff' })
  .image('https://x.com/bg.png', 0, 0, 375, 200)
  .text('Hello', { x: 30, y: 240, fontSize: 24, color: '#333' })
  .circle(40, 320, 30, { backgroundColor: '#1989fa' })
  .line(0, 360, 375, 360, { strokeColor: '#eee' })
  .qrcode('https://x.com', 137, 460, 100, {
    errorCorrectLevel: 'H',
    logo: { src: '/static/logo.png', size: 24, borderRadius: 12 }
  })
  .watermark('© 2026 jz-layer-poster', { mode: 'tile', rotate: -30, opacity: 0.15 })
  .draw({ canvasId, componentInstance })

方式 4:Vue 组件 ref(schema 模式)

<template>
  <jz-layer-poster ref="posterRef" :width="375" :height="600" />
</template>

<script setup>
const posterRef = ref(null)
async function gen() {
  const res = await jzLayerPoster.drawByRef(posterRef, {
    width: 375, height: 600,
    layers: [/* ... */]
  })
}
</script>

方式 5:DOM 截图(H5 / App-Vue)

直接把普通 vue 模板转成海报图片,无需任何 data-poster 标记

<template>
  <view ref="cardRef" class="card">
    <image src="/static/bg.png" />
    <text class="title">Hello</text>
    <text class="desc">无需 schema,直接截图</text>
  </view>
  <button @click="gen">生成</button>
</template>

<script>
import { getCurrentInstance } from 'vue'
import jzLayerPoster from '@/uni_modules/jz-layer-poster'

export default {
  setup() {
    const instance = getCurrentInstance().proxy
    return { instance }
  },
  methods: {
    async gen() {
      const res = await jzLayerPoster.drawByRef(this.$refs.cardRef, {
        mode: 'dom',
        componentInstance: this.instance,
        html2canvas: { scale: 2, useCORS: true }
      })
      console.log(res.tempFilePath)
    }
  }
}
</script>

DOM 截图仅支持 H5App-Vue。NVue / 小程序请使用前 4 种 schema 方式。


图层类型

类型 说明
image ImageLayer 图片图层(网络 / 本地 / base64),支持圆角与三种填充模式
text TextLayer 文字图层,支持自动换行、行数截断、阴影、字间距、装饰线、渐变
rect RectLayer 矩形,支持圆角、边框、渐变背景
circle CircleLayer 圆形,支持边框、渐变背景
line LineLayer 直线,支持虚线、渐变描边
qrcode QrcodeLayer 二维码,支持容错等级、内边距、圆角、中心 logo
watermark WatermarkLayer 水印(文字 / 图片),支持 9 宫格定位、平铺、旋转

PosterSchema 协议

type PosterSchema = {
  width: number              // 画布宽度
  height: number             // 画布高度
  unit?: 'px' | 'rpx'        // 单位,默认 'px'
  backgroundColor?: string   // 画布背景色,默认 '#fff',支持 GradientSpec
  layers: Layer[]            // 图层数组(按数组顺序从下到上绘制,可用 zIndex 二次排序)
}

公共字段(所有图层)

字段 类型 默认值 说明
type string 图层类型,必填
x y number 0 坐标(语义因图层而异)
opacity number 1 透明度 0–1
zIndex number 0 层级,相同时按数组顺序

image 图层

字段 默认值 说明
src 图片地址(http(s) / /static/... / base64),必填
width height 绘制尺寸,必填
borderRadius 0 圆角
mode 'fill' fill / aspectFill(裁剪填满) / aspectFit(等比缩放)

text 图层

字段 默认值 说明
content 文字内容,必填
fontSize 14 字号
color '#000' 颜色,支持 GradientSpec
fontWeight 'normal' normal / bold
fontFamily 字体
textAlign 'left' left / center / right
lineHeight fontSize * 1.5 行高
maxWidth 0 最大宽度(>0 时自动换行)
lineClamp 0 最大行数(>0 时超出截断)
overflow 'clip' clip / ellipsis(省略号)
letterSpacing 0 字间距
textDecoration 'none' none / underline / line-through
shadow null { offsetX, offsetY, blur, color }

rect 图层

字段 默认值 说明
width height 宽高,必填
backgroundColor 填充色,支持 GradientSpec
borderRadius 0 圆角
borderWidth 0 边框宽度
borderColor '#000' 边框颜色,支持 GradientSpec

circle 图层

字段 默认值 说明
x y 圆心坐标
radius 半径,必填
backgroundColor 填充色,支持 GradientSpec
borderWidth 0 边框宽度
borderColor '#000' 边框颜色

line 图层

字段 默认值 说明
x1 y1 x2 y2 起点 / 终点
strokeColor '#000' 颜色,支持 GradientSpec
strokeWidth 1 线宽
lineDash [] 虚线模式 [实线长, 间隔]

渐变能力

backgroundColor / borderColor / strokeColor / color 与海报全局 backgroundColor 均支持传入 GradientSpec 对象(旧的字符串色值完全兼容)。

线性渐变

{
  type: 'rect', x: 0, y: 0, width: 375, height: 200,
  backgroundColor: {
    type: 'linear',
    angle: 90,             // 0=左→右 / 90=上→下 / 180=右→左 / 270=下→上
    stops: [
      { offset: 0,   color: '#ff6b6b' },
      { offset: 1,   color: '#feca57' }
    ]
  }
}

径向渐变

{
  type: 'circle', x: 100, y: 100, radius: 60,
  backgroundColor: {
    type: 'radial',
    center: [0.5, 0.5],   // 圆心比例(相对图层 box)
    stops: [
      { offset: 0, color: '#ffffff' },
      { offset: 1, color: '#1989fa' }
    ]
  }
}

文字渐变

{
  type: 'text', content: '炫彩标题', x: 30, y: 100, fontSize: 32, fontWeight: 'bold',
  color: {
    type: 'linear', angle: 0,
    stops: [
      { offset: 0, color: '#ff6b6b' },
      { offset: 1, color: '#5f27cd' }
    ]
  }
}

文字渐变端点会按文字实际像素宽高自动计算(含多行 / letterSpacing 场景)。


水印图层

// 单个水印 + 9 宫格
{ type: 'watermark', contentType: 'text', text: '机密文件',
  mode: 'single', position: 'bottom-right', padding: 20,
  fontSize: 14, color: '#999', opacity: 0.6 }

// 平铺水印 + 旋转
{ type: 'watermark', text: '© jz-layer-poster',
  mode: 'tile', rotate: -30, gapX: 60, gapY: 80, opacity: 0.15 }

// 图片水印(一行平铺)
{ type: 'watermark', contentType: 'image', src: '/static/logo.png',
  width: 40, height: 40, mode: 'tile-x', gapX: 80 }
字段 默认值 说明
contentType 'text' text / image
text / src 内容(二选一)
mode 'single' single / tile / tile-x / tile-y
position 'center' 9 宫格:top-left / top-center / top-right / middle-left / center / middle-right / bottom-left / bottom-center / bottom-right / custom
rotate 0 旋转角度(度,顺时针)
gapX / gapY 40 平铺间距
padding 0 平铺时整体网格距画布边距
opacity 0.2 默认偏淡
clip true 旋转后超出画布是否裁掉

链式调用:

.watermark('© jz-layer-poster', { mode: 'tile', rotate: -30, opacity: 0.15 })
.watermark('/static/logo.png',  { mode: 'tile-x', width: 40, height: 40, gapX: 80 })

字符串以 http(s): / /static / file: / data: 开头时自动识别为图片,否则为文字。


二维码图层

{
  type: 'qrcode', x: 137, y: 460, size: 100,
  content: 'https://example.com',
  backgroundColor: '#ffffff',
  foregroundColor: '#1a1a2e',
  padding: 8,                 // 静默区
  borderRadius: 12,           // 背景圆角
  errorCorrectLevel: 'H',     // L / M / Q / H,含 logo 时建议 H
  logo: {
    src: '/static/logo.png',
    size: 24,                 // 建议 ≤ size * 0.25
    borderRadius: 12,         // = size/2 即圆形
    borderWidth: 2,           // 白色描边
    borderColor: '#ffffff'
  }
}

链式:

.qrcode('https://example.com', 137, 460, 100, {
  padding: 8, borderRadius: 12, errorCorrectLevel: 'H',
  logo: { src: '/static/logo.png', size: 24, borderRadius: 12 }
})

DOM 截图(H5 / App-Vue)

适用场景:vue 模板做主视觉,无需写 schema

1. 单纯 DOM 截图

const res = await jzLayerPoster.drawByRef(this.$refs.cardRef, {
  mode: 'dom',
  componentInstance: this,
  html2canvas: { scale: 2, useCORS: true, backgroundColor: null },
  fileType: 'png',
  quality: 1
})
// res.tempFilePath / res.base64

2. DOM 截图作为图片图层混合(vue 模板做主视觉 + canvas 加水印 / 二维码)

const res = await jzLayerPoster.drawByRef(this.$refs.cardRef, {
  mode: 'dom-as-image',
  width: 375,
  height: 667,
  componentInstance: this,
  canvasId: 'jzPoster',
  layers: [
    { type: 'qrcode',    content: 'https://example.com', x: 137, y: 540, size: 100 },
    { type: 'watermark', text: '© 2026', mode: 'tile', rotate: -30, opacity: 0.15 }
  ]
})
模式 说明
'schema'(默认) 走旧的 layers 渲染逻辑(全端)
'dom' 直接 html2canvas 截图(仅 H5 / App-Vue)
'dom-as-image' 截图后作为最底层 image 图层,再叠加你提供的 layers(仅 H5 / App-Vue)

H5 / App-Vue 共用同一份 renderjs 模块(dom-capture.renderjs.js),调用方无需感知平台差异。


可视化编辑器

内置拖拽编辑器,可在线设计海报,导出 JSON / JS 对象 / 链式代码。

H5:JS API 动态挂载

import jzLayerPoster from '@/uni_modules/jz-layer-poster'

jzLayerPoster.editor.open({
  width: 375,
  height: 600,
  backgroundColor: '#fff',
  layers: [],                  // 可传初始图层进入编辑
  onExport(data) {
    console.log(data.json)        // JSON 字符串
    console.log(data.object)      // JS 对象
    console.log(data.chainCode)   // 链式调用代码
  },
  onClose() { console.log('closed') }
})

App / 小程序:组件声明式挂载

<template>
  <jz-poster-editor
    v-if="show"
    :width="375" :height="600"
    :initial-layers="layers"
    @export="onExport"
    @close="show = false"
  />
</template>

编辑器能力

  • 设置画布尺寸、背景色
  • 拖拽 / 缩放 / 旋转 图层(基于 movable-area + movable-view,全端统一)
  • 选中图层显示:左上角移动、右上角删除、右下角缩放 + 旋转
  • 图层管理:排序、显隐、锁定、删除
  • 属性面板:样式、位置、大小
  • 导出 / 导入 JSON 还原

详见 docs/poster-editor.md


组件 API

<jz-layer-poster> 组件 props:

Prop 类型 默认值 说明
width Number 375 画布宽度(px)
height Number 600 画布高度(px)
backgroundColor String '#ffffff' 默认背景色
unit String 'px' 'px' / 'rpx'
fileType String 'png' 导出文件格式
quality Number 1 导出质量 0–1
hideCanvas Boolean true 是否隐藏 canvas(绘制不可见时设为 true)

事件:

事件 参数 说明
drawProgress progress (0–1) 绘制进度
drawComplete { tempFilePath, base64 } 绘制完成
drawError Error 绘制失败

实例方法(通过 ref 调用):

方法 说明
draw(schema, options?) 按 PosterSchema 绘制并导出
drawByJSON(json, options?) JSON 字符串方式绘制
captureDom(payload, timeout?) 触发 DOM 截图(仅 H5 / App-Vue)
saveToAlbum(tempFilePath) 保存到相册

JS API

import jzLayerPoster, { jzPosterEditor } from '@/uni_modules/jz-layer-poster'

// 链式构建
jzLayerPoster.create(width, height, options?)

// schema 方式(需传 canvasId / componentInstance)
jzLayerPoster.drawByJSON(schemaOrJsonString, options)
jzLayerPoster.drawByObject(schema, options)

// ref 方式(推荐,自动注入 canvasId / componentInstance)
jzLayerPoster.drawByRef(ref, options)

// 编辑器
jzLayerPoster.editor.open(options)
jzLayerPoster.editor.close()
jzPosterEditor.open(options)

drawByRef(ref, options)options 关键字段:

字段 默认值 说明
mode 'schema' 'schema' / 'dom' / 'dom-as-image'
componentInstance App-Vue / 小程序需传 this,setup 里传 getCurrentInstance().proxy
width height 自动 DOM 模式下可选,schema 模式按 ref 组件 props
layers dom-as-image 模式下叠加的图层
html2canvas {} html2canvas 透传配置(仅 dom 系模式)
fileType 'png' 导出格式
quality 1 导出质量
success fail 回调

DrawResult

{
  tempFilePath: string   // 小程序 / App 临时文件路径
  base64?: string        // H5 默认返回 base64
  width: number
  height: number
}

常见问题

Q1:小程序端图片不显示?

小程序端必须先在 manifest.jsonmp-weixin.permission.scope.userLocation(或对应小程序后台)配置 downloadFile 合法域名,否则网络图片下载失败。建议同时把图片域名配置到小程序服务器域名白名单。

Q2:H5 端跨域图片导出黑屏 / 报 SecurityError?

跨域图片必须满足:图片服务器返回 Access-Control-Allow-Origin: *,并在 ImageLoader 内部以 crossOrigin = 'anonymous' 加载。如使用 DOM 截图,请在 html2canvas 选项中传 useCORS: true

Q3:文字模糊 / 锯齿明显?

  • 小程序:使用 Canvas 2D 类型(默认),并传入更高的 pixelRatio
  • H5 / DOM 截图:在 html2canvas 选项中设置 scale: 23

Q4:能否实现 Canvas + DOM 混合?

可以,使用 mode: 'dom-as-image'。把 vue 模板截图作为底图,再叠加 canvas 绘制的 watermark / qrcode 等图层(详见 DOM 截图 第 2 节)。

Q5:NVue 项目可以使用吗?

Schema 方式(前 4 种)需要 Canvas 2D,NVue 暂不支持,建议在 NVue 页面跳转到 Vue 页面再绘制。DOM 截图方式 NVue 完全不支持(无 DOM)。

Q6:JS 对象方式中函数值(动态值)是怎么解析的?

drawByObject 在解析阶段会调用所有顶层为 function 的字段并取其返回值,因此可以这样写:

{ type: 'text', content: () => `下单:${orderId.value}`, x: 20, y: 50 }

Q7:链式调用必须传 canvasId / componentInstance 吗?

是的,因为 JzLayerPoster 单例本身不持有 canvas 节点。推荐做法是把链式构建器和组件 ref 配合使用,或直接调用 posterRef.value.draw(schema) 由组件内部完成。

Q8:水印 tile 模式旋转后边缘有空隙?

tile 模式以画布中心为旋转中心对整片网格统一旋转,旋转后超出画布的部分默认会被 clip: true 裁掉。如希望铺满,可适当增大 gapX/gapY 之外的内容宽度,或将 padding 设为负值。


许可证

MIT License

隐私、权限声明

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

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

插件不采集任何数据

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

许可协议

MIT协议

暂无用户评论。