Appearance
使用方法
介绍
安卓实况图合成和分离UTS原生插件支持根据通过图片和视频参数合成实况图片,支持将实况图片分离成图片和视频文件,插件支持uniapp和uniapp x
插件说明
- 如果您在使用插件的过程中有任何问题,可以联系作者,作者将全力协助您只用插件
- 本文档同时提供了uniapp的用法示例和uniappx的用法示例,插件市场导入的示例项目仅为uniapp的用法示例,如果您需要uniappx的示例项目可以通过下方的链接下载示例
联系作者

关注微信公众号可联系作者
插件地址
权限
- android.permission.INTERNET
- android.permission.WRITE_EXTERNAL_STORAGE
- android.permission.READ_EXTERNAL_STORAGE
- android.permission.MANAGE_EXTERNAL_STORAGE
用法
在需要使用插件的页面加载以下代码
js
import * as module from "@/uni_modules/leven-uts-livePhoto"js
import * as module from "@/uni_modules/leven-uts-livePhoto"页面内容
vue
<template>
<view>
<!-- 实况类型 -->
<view style="display: flex; flex-direction: row; justify-content: center; align-items: center; height: 100rpx;">
<view style="width: 300rpx;">
<uni-data-checkbox v-model="type" :localdata="range"></uni-data-checkbox>
</view>
</view>
<!-- 合成实况-->
<uni-card title="合成" v-if="type == 1">
<button type="primary" @click="selectImage">选择JPEG静态图片</button>
<view class="text-notice">{{imagePath == "" ? "未选择" : imagePath}}</view>
<button type="primary" @click="selectVideo">选择视频文件</button>
<view class="text-notice">{{videoPath == "" ? "未选择" : videoPath}}</view>
<button type="primary" @click="combiner">开始合成</button>
<view class="text-notice">提示:Google Photos 通过文件名 *MP.jpg 识别动态照片,输出文件已自动加 MP 后缀。视频需为 H.264 编码、约 3 秒时长,静态图应对应视频中某一帧。
</view>
</uni-card>
<uni-card title="分离" v-if="type == 2">
<button type="primary" @click="selectMotionPhoto">选择 MotionPhoto JPEG</button>
<view class="text-notice">{{motionPhoto == "" ? "未选择" : motionPhoto}}</view>
<button type="primary" @click="split">开始分离</button>
</uni-card>
<!-- 日志 -->
<uni-card title="日志">
<view style="word-wrap: break-word;">
<text style="font-size: 12px; color: #999999;">{{logs}}</text>
</view>
</uni-card>
</view>
</template>
<script>
import * as module from "@/uni_modules/leven-uts-livePhoto"
export default {
data() {
return {
type: 1,
range: [{
"value": 1,
"text": "合成"
}, {
"value": 2,
"text": "分离"
}],
//选择的静态图片
imagePath: "",
//选择的视频文件
videoPath: "",
//实况图片
motionPhoto: "",
//日志信息
logs: ""
}
},
onLoad() {
this.requestPermissions();
},
methods: {
//显示消息
showMessage(message) {
uni.showToast({
title: message,
icon: "none"
})
},
//分离实况图片
split() {
try {
if (this.motionPhoto == "") {
throw new Error("请选择实况图片");
}
module.split({
params: {
saveDir: "/storage/emulated/0/levenLivePhoto/split",
path: this.motionPhoto
},
complete: (res) => {
if (res.code == 0) {
let data = res.data;
let videoPath = data.videoPath;
let imagePath = data.imagePath;
let message = "分离图片路径:" + imagePath + "\r" +
"分离视频路径:" + videoPath + "\n";
this.logs = message + this.logs
}
}
})
} catch (e) {
this.showMessage(e.message)
}
},
//选择实况图片
selectMotionPhoto() {
uni.chooseImage({
count: 1,
sourceType: ["album"],
sizeType: ["original"],
success: (res) => {
let tempFilePaths = res.tempFilePaths;
if (tempFilePaths.length > 0) {
this.motionPhoto = tempFilePaths[0].replace("file://", "");
}
}
})
},
//合成
combiner() {
try {
if (this.imagePath == "") {
throw new Error("请选择图片")
}
if (this.videoPath == "") {
throw new Error("请选择视频")
}
module.combiner({
params: {
saveDir: "/storage/emulated/0/levenLivePhoto/combiner",
imagePath: this.imagePath,
videoPath: this.videoPath
},
complete: (res) => {
console.log(res)
if (res.code == 0) {
let data = res.data;
let message = "合成结果:" + data.path;
console.log(message)
this.logs = message + "\n" + this.logs
}
}
})
} catch (e) {
this.showMessage(e.message)
}
},
//选择视频
selectVideo() {
uni.chooseVideo({
sourceType: ["album"],
compressed: false,
success: (res) => {
this.videoPath = res.tempFilePath.replace("file://", "")
}
})
},
//选择图片
selectImage() {
uni.chooseImage({
count: 1,
sourceType: ["album"],
sizeType: ["original"],
success: (res) => {
let tempFilePaths = res.tempFilePaths;
if (tempFilePaths.length > 0) {
this.imagePath = tempFilePaths[0].replace("file://", "");
}
}
})
},
toAllFilesPermissionPage() {
module.toAllFilesPermissionPage({
complete: (res) => {
if (res.code == 0) {
let data = res.data;
if (!data.status) {
this.showMessage("所有文件访问权限未打开")
} else {
this.showMessage("权限申请完成,可以使用插件")
}
} else {
this.showMessage(res.message)
}
}
})
},
checkAllFilesPermission() {
module.checkAllFilesPermission({
complete: (res) => {
if (res.code == 0) {
let data = res.data;
if (!data.status) {
this.toAllFilesPermissionPage();
} else {
this.showMessage("权限申请完成,可以使用插件")
}
} else {
this.showMessage(res.message)
}
}
})
},
//申请插件所需权限
requestPermissions() {
module.requestPermissions({
params: {
permissions: [
"android.permission.READ_EXTERNAL_STORAGE",
"android.permission.WRITE_EXTERNAL_STORAGE"
]
},
complete: (res) => {
if (res.code == 0) {
//判断是否有所有文件访问权限
this.checkAllFilesPermission();
} else {
this.showMessage(res.message)
}
}
})
},
}
}
</script>
<style>
.text-notice {
padding: 16px 0;
color: #999999;
word-wrap: break-word;
}
</style>vue
<template>
<!-- #ifdef APP -->
<scroll-view style="flex:1">
<!-- #endif -->
<view style="flex-direction: row; justify-content: center; align-items: center; height: 100rpx;">
<rice-radio-group v-model="type" direction="row">
<rice-radio :value="1" label="合成"></rice-radio>
<rice-radio :value="2" label="分离"></rice-radio>
</rice-radio-group>
</view>
<!-- 合成实况-->
<lux-card title="合成" v-if="type == 1">
<button type="primary" @click="selectImage">选择JPEG静态图片</button>
<view class="text-notice"><text class="text-notice-info">{{imagePath == "" ? "未选择" : imagePath}}</text></view>
<button type="primary" @click="selectVideo">选择视频文件</button>
<view class="text-notice"><text class="text-notice-info">{{videoPath == "" ? "未选择" : videoPath}}</text></view>
<button type="primary" @click="combiner">开始合成</button>
<view class="text-notice">
<text class="text-notice-info">提示:Google Photos 通过文件名 *MP.jpg 识别动态照片,输出文件已自动加 MP 后缀。视频需为 H.264 编码、约 3
秒时长,静态图应对应视频中某一帧。</text>
</view>
</lux-card>
<lux-card title="分离" v-if="type == 2">
<button type="primary" @click="selectMotionPhoto">选择 MotionPhoto JPEG</button>
<view class="text-notice"><text class="text-notice-info">{{motionPhoto == "" ? "未选择" : motionPhoto}}</text></view>
<button type="primary" @click="split">开始分离</button>
</lux-card>
<lux-card title="日志">
<text style="font-size: 12px; color: #999999;">{{logs}}</text>
</lux-card>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
</template>
<script setup>
import * as module from "@/uni_modules/leven-uts-livePhoto"
import { LevenOptions, LevenResult } from "@/uni_modules/leven-uts-livePhoto"
const type = ref(1)
const imagePath = ref("")
const videoPath = ref("")
const motionPhoto = ref("")
const logs = ref("")
//显示消息
function showMessage(message : string) {
uni.showToast({
title: message,
icon: "none"
})
}
//分离实况图片
function split() {
try {
if (motionPhoto.value == "") {
throw new Error("请选择实况图片");
}
module.split({
params: {
saveDir: "/storage/emulated/0/levenLivePhoto/split",
path: motionPhoto.value
},
fail: (res : LevenResult) => {
showMessage(res.message)
},
success: (res : LevenResult) => {
if (res.code == 0) {
let data = res.data;
let videoPath = data.getString("videoPath")!;
let imagePath = data.getString("imagePath")!;
let message = "分离图片路径:" + imagePath + "\n" +
"分离视频路径:" + videoPath + "\n";
logs.value = message + logs.value
}
}
})
} catch (e : Error) {
showMessage(e.message)
}
}
//选择实况图片
function selectMotionPhoto() {
uni.chooseImage({
count: 1,
sourceType: ["album"],
sizeType: ["original"],
success: (res) => {
let tempFilePaths = res.tempFilePaths;
if (tempFilePaths.length > 0) {
motionPhoto.value = tempFilePaths[0].replace("file://", "");
}
}
})
}
//合成
function combiner() {
try {
if (imagePath.value == "") {
throw new Error("请选择图片")
}
if (videoPath.value == "") {
throw new Error("请选择视频")
}
module.combiner({
params: {
saveDir: "/storage/emulated/0/levenLivePhoto/combiner",
imagePath: imagePath.value,
videoPath: videoPath.value
},
complete: (res) => {
console.log(res)
if (res.code == 0) {
let data = res.data;
let message = "合成结果:" + data.getString("path")!;
logs.value = message + "\n" + logs.value
}
}
})
} catch (e : Error) {
showMessage(e.message)
}
}
//选择视频
function selectVideo() {
uni.chooseVideo({
sourceType: ["album"],
compressed: false,
success: (res) => {
videoPath.value = res.tempFilePath.replace("file://", "")
}
})
}
//选择图片
function selectImage() {
uni.chooseImage({
count: 1,
sourceType: ["album"],
sizeType: ["original"],
success: (res) => {
let tempFilePaths = res.tempFilePaths;
if (tempFilePaths.length > 0) {
imagePath.value = tempFilePaths[0].replace("file://", "");
}
}
})
}
function toAllFilesPermissionPage() {
module.toAllFilesPermissionPage({
complete: (res : LevenResult) => {
if (res.code == 0) {
let data = res.data;
let status = data.getBoolean("status")!;
if (!status) {
showMessage("所有文件访问权限未打开")
} else {
showMessage("权限申请完成,可以使用插件")
}
} else {
showMessage(res.message)
}
}
} as LevenOptions)
}
function checkAllFilesPermission() {
module.checkAllFilesPermission({
complete: (res : LevenResult) => {
if (res.code == 0) {
let data = res.data;
let status = data.getBoolean("status")!;
if (!status) {
toAllFilesPermissionPage();
} else {
showMessage("权限申请完成,可以使用插件")
}
} else {
showMessage(res.message)
}
}
} as LevenOptions)
}
//申请插件所需权限
function requestPermissions() {
module.requestPermissions({
params: {
permissions: [
"android.permission.READ_EXTERNAL_STORAGE",
"android.permission.WRITE_EXTERNAL_STORAGE"
]
},
complete: (res : LevenResult) => {
if (res.code == 0) {
//判断是否有所有文件访问权限
checkAllFilesPermission();
} else {
showMessage(res.message)
}
}
} as LevenOptions)
}
onMounted(() => {
nextTick(() => {
requestPermissions();
})
})
</script>
<style>
.text-notice {
padding: 16px 0;
}
.text-notice-info {
color: #999999;
font-size: 14px;
}
</style>