news 2026/9/3 10:53:59

Uniapp微信小程序:自定义相机模块,补足原生相机无法调用闪光灯的缺陷

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Uniapp微信小程序:自定义相机模块,补足原生相机无法调用闪光灯的缺陷

相机模块代码:

<template> <view class="custom-upload" v-if="show"> <!-- 相机弹出层 --> <u-overlay :show="show" mask-click-able="false"> <view class="nav"> <view class="back-btn" @tap="back"> 退出 </view> <view class="flash" @tap="showOptionsPopup = true"> <!-- 闪光灯按钮 --> <image @click="setFlash()" v-if="flashStatus === 'auto'" src="@/static/flash/auto.png" mode="widthFix" style="width: 26px; height: 26px"></image> <image @click="setFlash()" v-if="flashStatus === 'off'" src="@/static/flash/off.png" mode="widthFix" style="width: 26px; height: 26px"></image> <image @click="setFlash()" v-if="flashStatus === 'torch'" src="@/static/flash/torch.png" mode="widthFix" style="width: 26px; height: 26px"></image> </view> </view> <view class="camera-container"> <view v-if="!isCameraReady" class="loading"> <u-loading mode="circle"></u-loading> <text>相机启动中...</text> </view> <camera v-if="show" device-position="back" :flash="flashStatus" style="width: 100%; height: 100%" @ready="onCameraReady" @error="onCameraError" :frame-size="frameSize" :resolution="resolution"> <cover-view class="camera-btn" @tap.stop="takePhoto"> <cover-view class="btn"></cover-view> </cover-view> </camera> </view> </u-overlay> </view> </template> <script> import { nextTick } from 'vue'; export default { name: "myCamera", props: { show: { type: Boolean, default: false, }, }, data() { return { showOptionsPopup: false, // 闪光灯选项弹出层 flashStatus: "auto", // 闪光灯,值为auto, on, off, torch flashList: ["auto", "torch", "off"], index: 0, cameraContext: null, isCameraReady: false, frameSize: "medium", // 使用中等尺寸,加快加载速度 resolution: "medium", // 使用中等分辨率,加快加载速度 }; }, watch: { show(newVal) { if (newVal) { // 显示时立即初始化相机 nextTick(() => { this.initCamera(); }) } else { this.isCameraReady = false; } }, }, methods: { initCamera() { if (!this.cameraContext) { this.cameraContext = uni.createCameraContext(); } }, onCameraReady() { console.log("相机就绪"); this.isCameraReady = true; }, onCameraError(err) { console.error("相机错误:", err); this.isCameraReady = false; uni.showToast({ title: "相机启动失败", icon: "none", }); }, async takePhoto() { if (!this.cameraContext) { this.initCamera(); } try { const res = await new Promise((resolve, reject) => { this.cameraContext.takePhoto({ quality: "normal", // 使用普通质量,加快拍照速度 success: resolve, fail: reject, }); }); const tempUrl = res.tempImagePath; this.$emit("getUrl", tempUrl); this.$emit("close"); } catch (err) { console.log("拍照报错:", err); uni.showToast({ title: "拍照失败", icon: "none", }); } }, back() { this.$emit("close"); }, setFlash() { if (this.index == 2) { this.index = 0; this.flashStatus = this.flashList[this.index]; } else { this.index++; this.flashStatus = this.flashList[this.index]; } }, }, beforeDestroy() { this.cameraContext = null; }, }; </script> <style lang="scss"> .custom-upload { // border: 1px dashed red; display: flex; flex-direction: row; flex-wrap: wrap; background-color: #fff; .file-item { position: relative; display: flex; flex-direction: column; justify-content: center; align-items: center; border-radius: 2px; margin: 0 8px 8px 0; box-sizing: border-box; .upload-deletable { position: absolute; top: 0; right: 0; background-color: #373737; height: 14px; width: 14px; display: flex; flex-direction: row; border-bottom-left-radius: 100px; align-items: center; justify-content: center; z-index: 3; .upload-deletable-icon { position: absolute; -webkit-transform: scale(0.7); transform: scale(0.7); top: 0px; right: 0px; } } .upload-success { position: absolute; bottom: 0; right: 0; display: flex; flex-direction: row; border-style: solid; border-top-color: transparent; border-left-color: transparent; border-bottom-color: #5ac725; border-right-color: #5ac725; border-width: 9px; align-items: center; justify-content: center; .upload-success-icon { position: absolute; -webkit-transform: scale(0.7); transform: scale(0.7); bottom: -10px; right: -10px; } } } .upload-button { padding: 10rpx; display: flex; flex-direction: column; justify-content: center; align-items: center; background-color: #f4f5f7; border-radius: 2px; margin: 0 8px 8px 0; box-sizing: border-box; .upload-button-text { margin-top: 8rpx; color: #ccc; text-align: center; } } .option-list { display: flex; flex-direction: column; justify-content: center; align-items: center; padding: 40rpx 40rpx 20rpx 40rpx; .option-btn { border-bottom: 1px solid #ccc6; padding: 30rpx; width: 100%; text-align: center; font-size: 16px; } .option-btn-close { padding: 30rpx; width: 100%; text-align: center; font-size: 16px; } } .nav { height: 100rpx; padding: 0 30rpx; padding-left: 40rpx; box-sizing: border-box; width: 100%; display: flex; flex-direction: row; justify-content: space-between; align-items: center; background-color: black; .back-btn { color: #fff; } } .camera-container { position: relative; width: 100vw; height: 100vh; background-color: #000; // .back-btn { // color: #fff; // position: absolute; // bottom: 140rpx; // left: 100rpx; // background-color: #fff; // border-radius: 50%; // padding: 15rpx; // display: flex; // place-content: center; // z-index: 999; // .back-btn-icon { // transform: rotate(180deg); // } // } // .flash-btn { // position: absolute; // top: 100rpx; // left: 100rpx; // } .camera-btn { position: absolute; bottom: 200rpx; left: 50%; transform: translateX(-50%); display: flex; align-items: center; justify-content: center; width: 170rpx; height: 170rpx; border-radius: 50%; background-color: #626264; .btn { width: 135rpx; height: 135rpx; border-radius: 50%; background-color: #fff; } } .loading { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); display: flex; flex-direction: column; align-items: center; color: #fff; text { margin-top: 20rpx; font-size: 28rpx; } } } } </style>

调用方式:

<cameraModule class="cameraModule" :show="cameraShow" @close="cameraClose" @getUrl="getPhoto" />
const cameraShow = ref(false) const cameraClose = () => { console.log('关闭相机'); cameraShow.value = false } const getPhoto = async (url) => { console.log('获取照片地址', url); // 压缩图片 const compressRes = await new Promise((resolve, reject) => { uni.compressImage({ src: url, quality: 70, success: resolve, fail: reject }) }) // 上传图片 const uploadRes = await new Promise((resolve, reject) => { uni.uploadFile({ url: completion('(###上传地址###)'), filePath: compressRes.tempFilePath, name: 'file', header: { 'Authorization': 'Bearer ' + uni.getStorageSync( 'token') }, success: resolve, fail: reject }) }) // 处理返回数据 const data = JSON.parse(uploadRes.data) if (data.state !== 'success') throw new Error(data.message || '上传失败') // 添加到附件列表 deviceStatusAttachments.value.push(data.data.fileurl) uni.showToast({ icon: 'success', title: '上传成功', duration: 2000 }) }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/3 10:53:58

除湿机彩屏显示方案:LT165A驱动320x240 TFT-LCD开发详解

做除湿机主控开发时&#xff0c;很多团队会遇到一个典型问题&#xff1a;产品功能已经比较完整&#xff0c;但显示界面还停留在数码管或段码屏上。用户能看到的只有“湿度 60%”“连续模式”“水箱满”这类干巴巴的状态&#xff0c;产品卖点很难直观地呈现。于是不少方案开始升…

作者头像 李华
网站建设 2026/9/3 10:52:53

GLM-5.3实测:代码生成、安全分析与基准测试的真实价值

大家在挑选大模型的时候&#xff0c;往往最关心三个问题&#xff1a;代码写得好不好、安不安全、跑分是不是真的有用。最近 GLM-5.3 成了圈子里讨论度很高的一个名字&#xff0c;很多开发者都在观望它到底值不值得接入自己的工具链。本文不打算只念参数表&#xff0c;而是按实际…

作者头像 李华
网站建设 2026/9/3 10:52:35

GESP C++ 二级(2026.09)

【GESP C二级考试考点详细解读】 【GESP C二级考试考点详细解读】_gesp二级考试知识点-CSDN博客 GESP 二级真题考点分析和解题思路 GESP 二级真题考点分析和解题思路_gesp二级相关的数学内容-CSDN博客 GESP二级知识点复习参考 GESP二级知识点复习参考_gesp二级基础知识-CSDN博客…

作者头像 李华
网站建设 2026/9/3 10:52:32

10分钟快速搭建 SkyWalking 服务

从 0 开始入门 SkyWalking,搭建 SkyWalking 服务,并接入 Java 项目中实现分布式链路追踪。 Tags 目录: 1. 概述 2. 搭建 SkyWalking 单机环境 3. 搭建 SkyWalking 集群环境 4. 告警 5. 注意事项 6. Spring Boot 使用示例 1. 概述 1.1 概念 SkyWalking 是什么? FROM Ap…

作者头像 李华
网站建设 2026/9/3 10:47:52

QtScrcpy:不装 App 就能投屏控手机的跨平台工具,5 分钟跑通

QtScrcpy&#xff1a;不装 App 就能投屏控手机的跨平台工具&#xff0c;5 分钟跑通 【免费下载链接】QtScrcpy Android real-time display control software 项目地址: https://gitcode.com/GitHub_Trending/qt/QtScrcpy QtScrcpy 是一款基于 scrcpy 技术的跨平台工具&a…

作者头像 李华