更新记录
1.0.0(2026-10-29)
下载此版本
- 首个版本:海报画板组件 ant-poster-board
- 双引擎:canvas 2d 同层渲染优先,旧版 canvas 兜底(CSS 放大法保高清)
- 高清导出:物理像素 = 逻辑 × pixelRatio(1~5,默认 2),零重采样
- 元素:rect / text(多行省略、描边、阴影)/ image(cover·contain·fill)/ avatar / qrcode / tag / line
- 模板:商品海报、邀请海报
- 兼容:H5 / App-vue / App-Harmony(4.23+) / 微信·支付宝·抖音·百度·QQ·快手·飞书·京东小程序
- 修复 H5/App-webview 端海报"图片只显示一角、文字出界"的问题。根因:uni-h5 对
CanvasRenderingContext2D 原型打了 hidpi 补丁,其管理的 canvas ctx 带 __hidpi__ 标记时 setTransform/drawImage 等坐标参数被自动乘 devicePixelRatio,导致组件按 pixelRatio 的坐标系被二次放大溢出画布
- 修复方式(三重防御):canvas 传
:hidpi="false" 关闭 uni 自动画布管理;绘制前强制 ctx.__hidpi__ = false 短路原型补丁;style 尺寸变更后等 nextTick+宏任务再接管画布,并在绘制后校验画布是否被 uni resize-sensor 重置、必要时重绘(导出前同样校验)
- 测试新增:uni-h5 hidpi 补丁复现用例(先复现溢出、再验证修复)、真实网络图完整海报回归用例,harness 11/11 通过
平台兼容性
uni-app(5.33)
| Vue2 |
Vue3 |
Chrome |
Safari |
app-vue |
app-nvue |
Android |
iOS |
鸿蒙 |
| √ |
√ |
√ |
√ |
√ |
× |
√ |
√ |
√ |
| 微信小程序 |
支付宝小程序 |
抖音小程序 |
百度小程序 |
快手小程序 |
京东小程序 |
鸿蒙元服务 |
QQ小程序 |
飞书小程序 |
小红书小程序 |
快应用-华为 |
快应用-联盟 |
| √ |
√ |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
uni-app x(5.33)
| Chrome |
Safari |
Android |
iOS |
鸿蒙 |
微信小程序 |
| √ |
√ |
√ |
√ |
√ |
√ |
ant-poster 海报画板
JSON 配置驱动的 uni-app 海报画板组件:一次配置,多端绘制,高清导出。
特性
- 全端兼容:H5 / App(Android·iOS) / App-Harmony(鸿蒙) / 微信·支付宝·抖音·百度·QQ·快手·飞书·京东小程序
- 高清导出:canvas 物理像素 = 逻辑尺寸 ×
pixelRatio(默认 2),750 设计稿导出 1500px 宽,pixelRatio: 3 达 2250px,导出零重采样不失真
- 双引擎自适应:优先 canvas 2d 同层渲染(微信 2.7.0+ / 支付宝 2.7.0+ / 抖音 1.78.0+ / H5 / App);canvas 2d 节点不可用的环境(如低版本鸿蒙 runtime)自动降级旧版 canvas 引擎,旧版引擎通过 CSS 放大法同样保证位图高清
- 元素丰富:矩形(圆角/渐变/边框)、文本(多行换行/省略/对齐/描边/阴影)、图片(cover/contain/fill 裁剪/圆角/占位)、圆形头像(描边)、二维码图片位(白底)、标签胶囊、线条
- 模板开箱即用:商品海报、邀请海报两个内置模板
- API 友好:
render() / exportImage() / renderAndExport(),Promise + 事件双通道;图片并发预载与缓存
快速开始
easycom 自动注册(项目根 uni_modules 下的组件无需配置):
<template>
<ant-poster-board ref="poster" :poster="poster" @render-success="onRendered" />
</template>
<script>
import { createProductPoster } from '@/uni_modules/ant-poster/common/templates.js'
export default {
data() {
return {
poster: createProductPoster({
image: 'https://example.com/goods.jpg',
badge: '限时特惠',
title: '新疆阿克苏冰糖心苹果 5kg 装 脆甜多汁',
price: '39.90',
originalPrice: '59.90',
shopAvatar: 'https://example.com/shop.png',
shopName: '天猫旗舰店',
qrcode: 'https://example.com/qrcode.png',
pixelRatio: 2 // 高清倍率:默认 2,海报配置或 exportImage({ pixelRatio: 3 }) 可调
})
}
},
methods: {
async onExport() {
const res = await this.$refs.poster.exportImage({ fileType: 'png' })
// res.tempFilePath / res.width(1500) / res.height
uni.saveImageToPhotosAlbum({ filePath: res.tempFilePath })
}
}
}
</script>
组件属性 ant-poster-board
| 属性 |
类型 |
默认值 |
说明 |
| poster |
Object |
- |
海报配置(见下) |
| autoRender |
Boolean |
true |
poster 变化后自动重绘 |
| hidden |
Boolean |
false |
隐藏画板(移出视口,仅导出场景) |
| fileType |
String |
png |
导出格式 png / jpg |
| quality |
Number |
1 |
导出质量(仅 jpg 有效) |
| pixelRatio |
Number |
0 |
高清倍率覆盖(>0 时优先于 poster.pixelRatio) |
| canvasId |
String |
自动 |
画板 id,同页多实例保持唯一 |
poster 配置
{
width: 750, // 设计稿逻辑宽(px)
height: 1200, // 设计稿逻辑高(px)
pixelRatio: 2, // 高清倍率,导出 = 逻辑 × 倍率(1~5)
background: '#ffffff', // 'transparent' / 颜色 / { linear: true, x0,y0,x1,y1, stops: [[0,'#a'],[1,'#b']] }(坐标 0~1 比例)
elements: [ /* 元素数组,按顺序绘制 */ ]
}
元素类型
// 矩形 / 色块 / 分割线
{ type: 'rect', x, y, w, h, background, radius: 12 | [tl,tr,br,bl], borderColor, borderWidth, opacity, rotate }
// 文本:自动换行 + maxLines 省略 + 对齐 + 描边 / 阴影
{ type: 'text', x, y, width, text, fontSize: 28, color, fontWeight, fontFamily,
lineHeight, align: 'left|center|right', maxLines, rotate,
shadow: { color, blur, offsetX, offsetY }, stroke: { color, lineWidth } }
// 图片:cover 裁剪铺满(默认)/ contain 完整显示 / fill 拉伸
{ type: 'image', x, y, w, h, src, mode: 'cover', radius, borderColor, borderWidth, background, fallbackText }
// 圆形头像(cover 裁剪 + 可选描边)
{ type: 'avatar', x, y, w, h, src, borderColor, borderWidth, fallbackText }
// 二维码图片位(建议 background 白底避免透明码扫不出)
{ type: 'qrcode', x, y, w, h, src, background: '#ffffff', radius, fallbackText }
// 标签胶囊:宽度按文字自动计算
{ type: 'tag', x, y, text, fontSize: 22, color, background, paddingLeft, paddingRight, paddingTop, paddingBottom, radius, align }
// 线条(细矩形实现)
{ type: 'line', x, y, w, h: 2, background }
方法(ref 调用)
| 方法 |
参数 |
返回 |
说明 |
| render |
(poster?) |
Promise\<poster> |
绘制;并发调用复用同一 Promise |
| exportImage |
({ pixelRatio?, fileType?, quality? }?) |
Promise\<{ tempFilePath, width, height, pixelRatio, fileType }> |
导出;pixelRatio 与当前不同时自动以新倍率重绘(保证真高清) |
| renderAndExport |
(poster?, opts?) |
Promise\<同上> |
一步绘制并导出 |
事件
| 事件 |
参数 |
说明 |
| render-start |
poster |
开始绘制 |
| render-progress |
{ loaded, total } |
图片预载进度 |
| render-success |
poster |
绘制完成 |
| render-error |
Error |
绘制失败 |
| export-success |
{ tempFilePath, width, height, pixelRatio, fileType } |
导出成功 |
| export-error |
Error |
导出失败 |
高清导出说明(为什么不会糊)
- canvas 2d 引擎(主路径):画布物理像素直接设为
width × pixelRatio,绘制坐标系整体 setTransform(pixelRatio,…) 放大,导出时不传 destWidth(原生按物理像素原样输出)——矢量元素(文字/圆角/线条)以目标分辨率原生栅格化,照片类元素 1:1 无重采样。
- 旧版 canvas 引擎(兜底):CSS 尺寸放大
pixelRatio 倍抬高画布位图分辨率,destWidth = 逻辑 × pixelRatio 原样导出。
exportImage({ pixelRatio: 3 }) 会以 3 倍率重绘后导出,而非对 2 倍图重采样放大。
H5 / App-webview hidpi 冲突防御(1.0.1)
uni-h5 运行时对 CanvasRenderingContext2D 原型打了 hidpi 补丁:其管理的 canvas 上下文带 __hidpi__ 标记时,setTransform、drawImage 等坐标参数会被自动乘 devicePixelRatio;同时组件内的 resize-sensor 会在 css 尺寸变化时把 canvas.width 重置为 offsetWidth。二者都会破坏本组件的物理像素接管(症状:海报图片只显示一角、文字区域出界消失)。组件已内置三重防御,业务侧无需处理:
- canvas 声明
:hidpi="false",关闭 uni 自动画布管理;
- 每次绘制前强制
ctx.__hidpi__ = false 短路原型补丁(小程序等端无此标记,赋值无副作用);
- 显示尺寸变更后延迟接管画布,绘制后校验
canvas.width,被重置则自动重绘(导出前同样校验)。
跨端注意事项
- 图片跨域(H5 / App webview / 鸿蒙 webview):网络图片服务端需开启 CORS(
Access-Control-Allow-Origin),组件已自动附加 crossOrigin="anonymous";未开启时该图会绘制失败并渲染 fallbackText 占位。
- 小程序网络图片域名:需加入各平台 downloadFile/getImageInfo 合法域名白名单。
- 鸿蒙:HBuilderX 4.23+;优先走 canvas 2d,节点不可用时自动降级旧版引擎。
- 保存相册:App/小程序配合
uni.saveImageToPhotosAlbum(小程序需在 manifest 声明保存权限);H5 端 tempFilePath 为 dataURL,可直接 <img> 预览或触发下载。
- 不支持 nvue / uni-app x(uvue)页面。
目录结构
ant-poster
├── common/ # 纯逻辑(Node 单测 + 浏览器 harness 与线上同路径)
│ ├── config.js # 配置/颜色/半径/渐变规范化
│ ├── text-layout.js # 换行、多行省略、cover/contain 裁剪计算
│ ├── image-loader.js # 跨端图片加载(浏览器/小程序双通路 + 缓存)
│ ├── drawer.js # 平台无关绘制引擎(新旧 canvas 共有能力子集)
│ └── templates.js # 商品/邀请海报模板
└── components/ant-poster-board/ # 组件:双引擎、高清导出、事件
测试
node test/run-poster.mjs # 纯逻辑单测:规范化/换行省略/裁剪/模板
# 浏览器像素级实测(验证高清导出尺寸与真实绘制)
python3 -m http.server 8899 --directory . # 或任意静态服务
# 打开 http://localhost:8899/test/poster-harness.html