更新记录

1.1.1(2026-11-04)

首个发布版本,支持 Android / iOS / 微信小程序三端。

  • 调用原生相机拍照并在照片上合成水印:Android 走 Camera2 + TextureView,iOS 走 AVFoundation 自定义相机页,微信小程序走 camera 组件 + OffscreenCanvas
  • 文字水印:支持多条叠加、多行文本,可设字号、行高、颜色、加粗、背景色、透明度
  • 图片水印:支持多张叠加,可设宽度/高度、等比缩放、旋转、透明度
  • 位置排版:九宫格锚点(9 个)+ 边距 + X/Y 偏移,按 1080 设计稿等比换算
  • 实时水印预览:未按下快门时水印即叠加在取景画面上,与成片所见即所得
  • 变焦能力:支持 zoom 初始倍数,Android/iOS 支持双指捏合实时变焦,小程序通过 setZoom 应用
  • 出图质量可调(1-100),Android 端出图分辨率上限约 1200 万像素,避免高像素机型内存溢出

平台兼容性

uni-app

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

lingbug-watermarkcam 接入说明文档

版本:1.0.0 | 适用:HBuilderX 3.98+ | 适配:Android / iOS / 微信小程序

lingbug-watermarkcam 是一个 UTS 插件,调用原生相机模块拍照,并在照片上合成可完全自定义的文字水印与图片水印。三端(app-android / app-ios / mp-weixin)提供统一 API。


一、插件安装

  1. 将 uni_modules/lingbug-watermarkcam 整个目录复制到你项目的 uni_modules/ 目录下(或通过 HBuilderX 导入插件)。
  2. 项目要求:
    • uni-app 项目(Vue 3)或 uni-app x 项目;
    • HBuilderX 3.98 及以上(UTS 编译器)。
  3. 无需引入任何第三方依赖。

插件目录结构

uni_modules/lingbug-watermarkcam/
├── package.json                 # 插件元信息
├── changelog.md
├── readme.md
└── utssdk/
    ├── interface.uts            # 三端公共类型定义(API 契约)
    ├── app-android/
    │   ├── index.uts            # Android 实现:Camera2 自定义相机 + Canvas 合成
    │   └── AndroidManifest.xml  # 自动合并的权限声明
    ├── app-ios/
    │   └── index.uts            # iOS 实现:AVFoundation 自定义相机 + CoreGraphics 合成
    ├── mp-weixin/
    │   └── index.uts            # 微信小程序实现:camera 组件 + OffscreenCanvas 合成
    └── components/
        └── wm-watermark-overlay/wm-watermark-overlay.vue  # 小程序端实时水印预览组件(easycom)

二、三端配置

2.1 Android

  • 权限:插件自带 AndroidManifest.xml,云打包/自定义基座时会自动合并 android.permission.CAMERA 权限声明,无需手动配置。
  • 运行权限:插件内部已调用系统权限弹窗申请相机权限,业务侧无需处理。
  • 照片保存位置:Android/data/<包名>/files/Pictures/watermark_camera/(应用私有目录,无需存储权限)。回调返回的 path 为绝对路径,可直接用于 image 组件展示或上传。
  • 调试要求:UTS 插件需使用自定义基座运行(HBuilderX 菜单:运行 → 运行到手机或模拟器 → 制作自定义调试基座)。

2.2 iOS

  • 隐私权限描述:在 manifest.json → App常用其它设置 → iOS隐私权限申请描述(或源码视图 app-plus.distribute.ios.privacyDescription)中添加:
{
  "app-plus": {
    "distribute": {
      "ios": {
        "privacyDescription": {
          "NSCameraUsageDescription": "需要使用相机拍摄照片并添加水印"
        }
      }
    }
  }
}
  • 照片保存在应用沙盒 tmp 目录,回调返回绝对路径。
  • iOS 端 UTS 插件需云打包/自定义基座才能生效。

2.3 微信小程序

小程序端无法弹出原生相机页面,本插件通过页面内的 <camera> 组件取景,因此:

  1. 调用 takePhoto 的页面必须放置 camera 组件(组件可以按需自定义样式、位置,也可以放在页面外视觉隐藏,但不能不写):
<camera device-position="back" flash="off" resolution="high"
        style="width: 100%; height: 500rpx;" />
  1. 基础库要求:≥ 2.16.1(OffscreenCanvas 2d 类型)。
  2. 图片水印域名:若水印图片使用网络地址,需在微信公众平台配置 downloadFile 合法域名;推荐使用本地路径(如 /static/logo.png)。
  3. 用户隐私:在 manifest.json → mp-weixin 中按需配置 requiredPrivateInfos / 隐私协议(如水印中包含位置信息时由业务自行获取并传入文字)。

三、API 参考

3.1 takePhoto(options, callback)

打开相机拍照并合成水印。三端签名完全一致,业务代码无需条件编译。

参数 类型 说明
options TakePhotoOptions 拍照与水印配置,见下表
callback (res: TakePhotoResult) => void 结果回调(异步,回调参数见下文)

TakePhotoOptions(所有字段必填;因 UTS iOS 平台限制,类型定义不支持字段默认值)

字段 类型 取值 / 建议值 说明
cameraDevice string "back" 或 "front"(建议 "back") 前/后置摄像头
flash string "off" / "on" / "auto"(建议 "off") 闪光灯模式
quality number 90 输出 JPEG 质量 1~100
zoom number(可选) 1.0 起,如 2 表示 2 倍变焦 初始变焦倍数。Android/iOS 打开相机后还可双指捏合实时调整(Android 顶部显示当前倍数),拍照时按当前倍数出片;小程序端拍照前通过 CameraContext.setZoom 应用(基础库 ≥2.10.0)。倍数超出设备上限时自动收敛
texts WatermarkTextItem[](可选) — 文字水印列表,可叠加多条
images WatermarkImageItem[](可选) — 图片水印列表,可叠加多张

WatermarkTextItem(一条文字水印;backgroundColor 可选,其余必填)

{
  text: {
    content: string,            // 文字内容,支持 "\n" 多行排版(必填)
    fontSize: number,           // 字号(设计稿像素,建议 28)(必填)
    lineHeight: number,         // 行高倍数(建议 1.4)(必填)
    color: string,              // 颜色 "#RRGGBB"(建议 "#FFFFFF")(必填)
    backgroundColor: string,    // 文字背景色(可选,不传则无背景)
    bold: boolean,              // 加粗(必填,不加大写 false)
    opacity: number             // 0~1(必填,不透明传 1)
  },
  position: WatermarkPosition
}

WatermarkImageItem(一张图片水印;height 可选,其余必填)

{
  image: {
    path: string,               // 图片路径(App:本地路径;小程序:本地或 https 地址)(必填)
    width: number,              // 目标宽度(设计稿像素)(必填)
    height: number,             // 目标高度(可选;不传时按原图宽高比自动计算)
    opacity: number,            // 0~1(必填,不透明传 1)
    rotation: number            // 顺时针旋转角度(度)(必填,不旋转传 0)
  },
  position: WatermarkPosition
}

WatermarkPosition(定位排版,文字与图片通用,所有字段必填)

字段 类型 建议值 说明
anchor string "bottomLeft" 九宫格锚点,见下
margin number 24 距照片边缘的边距
offsetX number 0 水平偏移,向右为正
offsetY number 0 垂直偏移,向下为正

anchor 取值(九宫格):

topLeft      topCenter      topRight
centerLeft   center         centerRight
bottomLeft   bottomCenter   bottomRight

竖直方向锚点仅以 top / center / bottom 开头生效,水平方向以 Left / Center / Right 结尾生效,组合使用即可。

TakePhotoResult(回调结果)

字段 类型 说明
success boolean 是否成功
errMsg string \| null 失败原因
path string \| null 成品照片路径(App:绝对路径;小程序:临时路径)
width number \| null 照片宽度(像素)
height number \| null 照片高度(像素)

3.2 尺寸单位(重要)

fontSize、width/height、margin、offsetX/offsetY 均以设计稿像素为单位(基准 1080px 宽)。实际绘制时按 照片宽度 / 1080 等比缩放,保证同一组参数在不同分辨率照片上视觉大小一致。

例:fontSize: 34 在 4000px 宽的照片上实际绘制约 34 × (4000/1080) ≈ 126px。

3.3 composeWatermark(srcPath, options, callback)(仅微信小程序)

小程序端可对已有图片单独合成水印(源图片可为本地临时路径或网络地址)。


四、完整示例

import { takePhoto } from '@/uni_modules/lingbug-watermarkcam'

takePhoto(
  {
    cameraDevice: 'back',
    flash: 'off',
    quality: 90,
    texts: [
      {
        // 多行文字 + 背景
        text: {
          content: '2026-10-30 11:20\n地点:示例项目工地',
          fontSize: 34,
          lineHeight: 1.5,
          color: '#FFFFFF',
          backgroundColor: '#000000',
          opacity: 0.8
        },
        position: { anchor: 'bottomLeft', margin: 40 }
      },
      {
        // 右上角金色加粗小字
        text: {
          content: 'Watermark Demo',
          fontSize: 24,
          color: '#FFD700',
          bold: true,
          opacity: 0.9
        },
        position: { anchor: 'topRight', margin: 40, offsetY: 0 }
      }
    ],
    images: [
      {
        // 左上角 logo,宽 160(自动等比)
        image: { path: '/static/logo.png', width: 160, opacity: 0.9 },
        position: { anchor: 'topLeft', margin: 40 }
      }
    ]
  },
  (res) => {
    if (res.success) {
      uni.previewImage({ urls: [res.path] })
    } else {
      uni.showToast({ title: res.errMsg || '拍照失败', icon: 'none' })
    }
  }
)

三端通用示例页面见 example/pages/watermark-demo.vue,将其复制到项目 pages 目录并在 pages.json 注册即可运行体验。


五、实时水印预览(拍照前预览界面显示水印)

插件支持所见即所得:打开相机、尚未按下快门时,水印即实时叠加在取景画面上。

实现原理:预览层与成片合成使用同一套 texts / images / position 参数,按当前预览区域宽度相对 1080 基准等比换算。按下快门后,成片仍按完整参数重新合成——以成片效果为准(预览为高保真近似)。

5.1 Android / iOS:自动生效

原生相机页内部已集成预览水印叠加层(Android:TextureView 上方叠加原生 TextView/ImageView;iOS:预览图层上方叠加 UILabel/UIImageView),调用 takePhoto 时传入什么水印参数,预览界面就直接显示什么,无需额外配置。

5.2 微信小程序:使用 wm-watermark-overlay 组件

小程序端相机是页面里的 <camera> 组件(其子节点只允许 cover-view / cover-image),因此插件提供了预览水印组件(uni_modules easycom 自动注册,无需 import):

<camera device-position="back" flash="off" resolution="high"
        style="width: 100%; height: 500rpx;">
  <!-- 参数与 takePhoto 的 texts / images 完全同构,建议与拍照参数共用同一份数据 -->
  <wm-watermark-overlay :texts="wmOptions.texts" :images="wmOptions.images" />
</camera>

组件行为:

  • 自动测量取景区域尺寸,按与成片一致的锚点/边距/偏移规则定位水印;
  • 文字水印逐行渲染,保证多行排版与成片一致;
  • 图片水印未指定 height 时自动读取原图宽高比等比缩放;
  • texts / images 数据变化时预览实时刷新(如更新时间戳文字)。

5.3 预览与成片的细微差异说明

场景 差异 说明
取景区域被裁切 预览画面 aspectFill 有裁边,成片是完整画面 靠近画面边缘的水印,预览位置可能略偏
文字行高 预览用系统控件默认行距,成片用 lineHeight 精确行高 多行文字的行距可能有细微差别
图片旋转 预览按视图中心旋转,成片按位图旋转后定位 旋转 + 靠边定位时预览可能有 1~2% 偏差

以上均为高保真近似;若业务要求预览与成片像素级一致,可适当将水印锚点远离画面边缘、避免依赖精确行高。


六、各端实现原理

端 相机 水印合成 保存
Android Camera2 API + TextureView 全屏预览,ImageReader 获取 JPEG android.graphics.Canvas(Paint/Matrix) 应用私有 Pictures 目录,JPEG
iOS AVFoundation AVCaptureSession + AVCapturePhotoOutput UIGraphicsImageRenderer(CoreGraphics) 沙盒 tmp 目录,JPEG
微信小程序 页面 <camera> 组件 + uni.createCameraContext().takePhoto();预览水印由 wm-watermark-overlay 组件(cover-view/cover-image)叠加 OffscreenCanvas 2d(canvas.getContext('2d')) wx.canvasToTempFilePath 临时文件

七、注意事项与 FAQ

  1. App 端必须使用自定义基座:标准基座不包含自定义 UTS 插件,需先「制作自定义调试基座」再真机运行。
  2. iOS 首次调用:系统会弹出相机权限弹窗;若用户拒绝,errMsg 会提示到设置中开启。
  3. Android 拍照方向:已按 Camera2 官方公式(屏幕方向 + sensorOrientation)计算 JPEG_ORIENTATION,并兼容「HAL 只写 EXIF 不旋转像素」的机型(按解码后宽高判据自动补转);如个别机型仍出现方向问题,可在反馈中说明机型。
  4. Android 预览黑边:预览画面按相机流宽高比等比放入屏幕(fit-center),与屏幕比例不一致时会上下/左右留黑边——这是保证不变形的正确行为;成片为完整画幅(多为 4:3),视野可能比取景框略大,属正常差异。
  5. 图片水印路径:
    • App 端:传本地文件绝对路径(如通过 plus.io 或下载后的文件路径);
    • 小程序端:本地路径或已配置域名的 https 地址。
  6. 小程序 camera 组件:必须在调用页存在;若业务不想展示取景画面,可将组件样式设置为极小/透明(不可用 v-if 移除)。
  7. 性能建议:水印数量一般不超过 10 条;图片水印建议预先压缩,避免大图解码耗时。
  8. UTS 编译器版本:不同 HBuilderX 版本的 UTS 语法略有差异,若编译报错请先升级到最新正式版 HBuilderX;iOS 端建议在 HBuilderX 最新 alpha/正式版 + 云打包环境下验证。
  9. Android 出图分辨率上限:为避免高像素机型(5000万/1亿像素传感器)解码合成时 OOM 闪退,Android 端拍照分辨率收敛到约 1200 万像素(超出时自动选最接近上限的尺寸),对水印相机场景完全够用。

八、错误码 / 常见 errMsg

errMsg 原因 处理
相机权限被拒绝 / 用户拒绝相机权限 未授权 引导用户到系统设置开启
未找到可用的前置/后置摄像头 设备无对应摄像头 检查 cameraDevice 参数
微信基础库版本过低 基础库 < 2.16.1 提示用户升级微信
当前页面未找到 camera 组件 小程序页面缺少 camera 添加 camera 组件
用户取消拍摄 用户点关闭 业务侧静默处理即可

隐私、权限声明

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

Android:android.permission.CAMERA(相机权限,用于取景预览与拍照;拍照结果仅写入应用私有目录,不需要存储权限) iOS:NSCameraUsageDescription(相机权限用途描述:用于取景预览与拍照合成水印) 微信小程序:scope.camera(通过 camera 组件使用,首次拍照时由微信弹窗授权,插件不额外申请权限)

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

无

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

无

暂无用户评论。