更新记录

1.0.7(2026-09-16)

  • 补充 uni-app x(UTS)和 uni-app App nvue(JavaScript)的转换、保存及预览示例。
  • 修复示例页面 Android UTS 中 encodeURIComponent 返回值可空导致的字符串类型错误。
  • 本次修复位于示例页面,升级时需同步 pages/index/index.uvue。

1.0.6(2026-09-16)

  • iOS 照片单选与批量选择改为自定义原生相册面板,新增「全部 / 实况图」筛选、三列照片网格、选择顺序编号、数量提示和时间排序。
  • 支持跨筛选保留已选照片、选择数量限制、有限照片权限管理及取消退出回调。
  • 使用异步缩略图、单元格复用和预缓存,离屏取消图片请求,减少滚动时的资源开销。
  • 支持多选视频/实况图进行批量转换

1.0.5(2026-09-15)

  • 修复示例页面转换完成后一直停留在“正在校验文件并保存到系统相册”的问题
  • 修复示例页面保存结果的响应式更新,保存完成后及时更新相册状态。
  • 调整示例页面 iOS 普通视频预览,将原生绝对路径转换为经过转义的本地文件 URL,并补充加载、播放、暂停、结束和错误提示。
  • 修正示例页面相册保存提示中的平台名称,超时时提示前往系统相册确认实际保存结果。
查看更多

平台兼容性

uni-app(3.8.4)

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

uni-app x(3.8.4)

Chrome Safari Android Android插件版本 iOS iOS插件版本 鸿蒙 微信小程序
× × 5.0 1.0.0 12 1.0.0 × ×

其他

多语言 暗黑模式 宽屏模式 蒸汽模式
× × √ ×

实况图与视频互转

支持 iOS Live Photo 与 Android Motion Photo 的读取、转换、预览和相册保存,文件在本机处理。当前版本 1.0.7,变更见 更新日志。

接入

  1. 将插件导入项目的 uni_modules 目录。
  2. 编译自定义基座或正式包运行,新增或更新原生代码后需要重新编译。
  3. uni-app x 使用 .uvue 页面;uni-app 使用 App .nvue 页面。普通 Vue 页面、H5、小程序和 HarmonyOS 暂不支持。

最低系统:Android 5.0 / API 21、iOS 15.0。iOS 读取照片需照片库权限,保存需相册写入权限;插件已声明用途说明。

uni-app x 示例

下面代码放入已注册的 .uvue 页面。先转换,再点击保存;转换成功本身不代表已写入相册。

<template>
  <view style="padding: 20px;">
    <button :disabled="busy" @tap="chooseVideo">视频转实况图</button>
    <button :disabled="busy" @tap="exportVideo">实况图转视频</button>
    <button :disabled="busy || result == null" @tap="save">保存到相册</button>
    <text>{{ message }}</text>
  </view>
</template>

<script setup lang="uts">
import { ref } from 'vue'
import {
  GetKBLivePhotoSupport, ConvertKBVideoToLivePhoto,
  ConvertKBLivePhotoToVideo, SaveKBMediaToAlbum,
  KBLivePhotoConversionResult, KBLivePhotoFail
} from '@/uni_modules/kongbai-live-photo'

const busy = ref(false)
const message = ref('请选择操作')
const result = ref<KBLivePhotoConversionResult | null>(null)

const fail = (error : KBLivePhotoFail) => {
  busy.value = false
  message.value = error.code == 'cancel' ? '已取消' : error.message
}
const complete = (value : KBLivePhotoConversionResult) => {
  result.value = value
  busy.value = false
  message.value = value.mediaType == 'image' ? '选中普通图片,无需另存' : '转换完成,可预览或保存'
}
const begin = () : boolean => {
  if (busy.value) return false
  const support = GetKBLivePhotoSupport()
  if (!support.supported) { message.value = support.reason; return false }
  busy.value = true
  result.value = null
  message.value = '处理中…'
  return true
}
const chooseVideo = () => {
  if (!begin()) return
  uni.chooseMedia({
    count: 1, mediaType: ['video'], sourceType: ['album'],
    success: (res) => {
      if (res.tempFiles.length == 0) { busy.value = false; message.value = '未选择视频'; return }
      ConvertKBVideoToLivePhoto({ videoPath: res.tempFiles[0].tempFilePath }, complete, fail)
    },
    fail: (error) => { busy.value = false; message.value = error.errMsg }
  })
}
const exportVideo = () => {
  if (begin()) ConvertKBLivePhotoToVideo(null, complete, fail)
}
const save = () => {
  const value = result.value
  if (busy.value || value == null) return
  if (value.mediaType == 'image') { message.value = '原图已在相册,无需另存'; return }
  busy.value = true
  message.value = '正在保存…'
  SaveKBMediaToAlbum({
    mediaType: value.mediaType, imagePath: value.imagePath,
    videoPath: value.videoPath, androidProfile: 'auto'
  }, (saved) => {
    busy.value = false
    message.value = saved.savedToAlbum ? '已保存到相册' : '未确认保存成功'
  }, fail)
}
</script>

uni-app 示例

下面代码放入已注册的 App .nvue 页面,使用普通 JavaScript,不需要 UTS 类型标注。

<template>
  <view style="padding: 20px;">
    <button :disabled="busy" @tap="chooseVideo">视频转实况图</button>
    <button :disabled="busy" @tap="exportVideo">实况图转视频</button>
    <button :disabled="busy || result == null" @tap="save">保存到相册</button>
    <text>{{ message }}</text>
  </view>
</template>

<script>
import {
  GetKBLivePhotoSupport, ConvertKBVideoToLivePhoto,
  ConvertKBLivePhotoToVideo, SaveKBMediaToAlbum
} from '@/uni_modules/kongbai-live-photo'

export default {
  data() { return { busy: false, result: null, message: '请选择操作' } },
  methods: {
    fail(error) {
      this.busy = false
      this.message = error.code === 'cancel' ? '已取消' : error.message
    },
    complete(value) {
      this.result = value
      this.busy = false
      this.message = value.mediaType === 'image' ? '选中普通图片,无需另存' : '转换完成,可预览或保存'
    },
    begin() {
      if (this.busy) return false
      const support = GetKBLivePhotoSupport()
      if (!support.supported) { this.message = support.reason; return false }
      this.busy = true
      this.result = null
      this.message = '处理中…'
      return true
    },
    chooseVideo() {
      if (!this.begin()) return
      uni.chooseVideo({
        sourceType: ['album'],
        success: (res) => ConvertKBVideoToLivePhoto(
          { videoPath: res.tempFilePath }, (value) => this.complete(value), (error) => this.fail(error)
        ),
        fail: (error) => { this.busy = false; this.message = error.errMsg }
      })
    },
    exportVideo() {
      if (!this.begin()) return
      ConvertKBLivePhotoToVideo(null, (value) => this.complete(value), (error) => this.fail(error))
    },
    save() {
      const value = this.result
      if (this.busy || value == null) return
      if (value.mediaType === 'image') { this.message = '原图已在相册,无需另存'; return }
      this.busy = true
      this.message = '正在保存…'
      SaveKBMediaToAlbum({
        mediaType: value.mediaType, imagePath: value.imagePath,
        videoPath: value.videoPath, androidProfile: 'auto'
      }, (saved) => {
        this.busy = false
        this.message = saved.savedToAlbum ? '已保存到相册' : '未确认保存成功'
      }, (error) => this.fail(error))
    }
  }
}
</script>

预览与自选封面

两种页面都可将以下组件放入上面的 <view> 中,预览转换得到的实况图。组件由插件提供,使用默认 easycom 配置即可。

<kongbai-live-photo
  v-if="result != null && (result.mediaType == 'live_photo' || result.mediaType == 'motion_photo')"
  style="width: 300px; height: 300px;"
  :image-path="result.imagePath" :video-path="result.videoPath"
  content-mode="aspectFit" :auto-play="true" playback-style="full"
/>
  • 组件支持 load、play、stop、error 事件,以及 play()、stop()、reload() 方法。
  • mediaType == 'video' 使用普通 <video> 播放,mediaType == 'image' 使用 <image> 展示。
  • iOS 普通图片/视频控件使用本地 URL;将绝对路径转换为 'file://' + path.split('/').map(part => encodeURIComponent(part) ?? part).join('/')。保存接口和实况组件仍传原始本地路径。
  • 自选封面:在 ConvertKBVideoToLivePhoto 参数中添加 coverImagePath: 本地图片路径;不传时取视频首个有效帧,也可传 coverTime(秒)。

API 速查

从 @/uni_modules/kongbai-live-photo 导入。除能力检测外,调用格式均为 API(options, success, fail);失败回调包含 code、message、platform。

API 用途 / 主要参数
GetKBLivePhotoSupport() 同步获取 supported、reason 和双向转换能力
ChooseKBPhoto 选择照片,返回 imagePath、videoPath、isLivePhoto;options 可为 null
ChooseKBLivePhoto 同上,仍允许选中普通图片
ConvertKBVideoToLivePhoto videoPath 必填;coverImagePath、coverTime 可选
ConvertKBLivePhotoToVideo 打开照片选择器并导出;options 可为 null
SaveKBMediaToAlbum 传入转换结果的 mediaType、imagePath、videoPath;Android 推荐 androidProfile: 'auto'
ConvertKBLivePhotosToVideos 仅 iOS:批量选择 1–9 张照片,完成回调返回 JSON 字符串

详细参数与结果类型见 interface.uts。转换结果的 mediaType 用于区分普通图片、视频和实况图;不要只依据文件扩展名判断。

iOS 批量选择

uni-app x 的 UTS 和 uni-app 的 JavaScript 均可使用下面的调用。导入与调用必须放在 APP-IOS 条件编译范围内,Android 未导出此 API。

// #ifdef APP-IOS
import { ConvertKBLivePhotosToVideos } from '@/uni_modules/kongbai-live-photo'

// 放入按钮事件中执行。
function chooseBatch() {
  ConvertKBLivePhotosToVideos({
    count: 9,
    : (json) => console.log('进度', JSON.parse(json))
  }, (json) => {
    // 按选择顺序返回,每项包含 index、result、error。
    console.log('批量结果', JSON.parse(json))
  }, (error) => console.log(error.code, error.message))
}
// #endif

iOS 使用自定义相册,支持「全部 / 实况图」切换。实况图返回视频,普通图片返回图片;单项失败不影响后续项。需要保存时,在整批完成后逐项串行保存 mediaType == 'video' 的结果,不要在进度回调中保存,否则会返回 busy。

注意事项

  • 输入使用本地文件路径;转换输出为临时文件,长期使用需自行持久化。iOS 配对视频可能为 MOV,不要只改后缀当作 MP4。
  • 同一时间只执行一个选择、转换或保存操作;取消返回 cancel,权限不足返回 permission_denied。
  • Android 按设备品牌选择保存协议;vivo 双资源读取可能要求再选择配对 MP4,必须为同一组资源。
  • 示例展示基本调用。正式业务需处理页面退出和超时;超时后先确认相册实际结果,避免重复保存。
  • 1.0.7 的页面兼容修复需同步项目中的 pages/index/index.uvue。文档示例经过语法检查与接口核对,未完成双端真机验证。

隐私、权限声明

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

iOS 选择和读取照片时使用照片库 readWrite 权限,写入系统相册时检查 addOnly;Android 仅读取用户通过系统选择器主动选中的媒体,Android 10+ 通过 MediaStore 写入而不申请存储读取权限,Android 9 及以下仅在保存前检查 WRITE_EXTERNAL_STORAGE。

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

插件不采集、不上传用户照片、视频或设备数据;选择、读取、转换、校验和导出均在设备本地完成,临时产物保存在应用缓存目录。

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

无广告、无广告 SDK、无引流内容。