在微信小程序开发中,相机功能已成为许多应用的核心组成部分。本文将介绍如何使用UniApp框架实现一个功能丰富的相机组件,支持拍照、录像、前后摄像头切换以及双指缩放等功能。

功能概述

  • 这个相机组件具备以下核心功能:

  • 拍照功能:支持高质量图片拍摄

  • 录像功能:支持最长60秒的视频录制

  • 前后摄像头切换:轻松切换前置和后置摄像头

  • 双指缩放:通过手势控制相机变焦

  • 操作模式切换:在拍照和录像模式间流畅切换

    实现细节

    相机基础设置

    首先,我们使用UniApp的camera组件作为基础

    双指缩放实现

    双指缩放是通过监听触摸事件并计算两指之间的距离变化来实现的

    模式切换与媒体捕获

    通过滑动切换拍照和录像模式,并分别实现拍照和录像功能

    用户界面设计

    组件界面采用黑色主题,符合相机应用的常见设计风格

    样式设计

    使用SCSS编写样式,确保界面美观且响应式

    完整代码如下

<template><view class="container"><camera:device-position="cameraType"flash="off"@error="handleCameraError"@touchstart="handleZoomStart"@touchmove="handleZoomMove"style="width: 100%; height: 75vh"ref="cameraRef":resolution="'high'"></camera><!-- 缩放级别显示(可选) --><view class="zoom-indicator">缩放: {{ currentZoom.toFixed(1) }}x</view><view class="btn_group"><viewclass="top"@touchstart="handleTouchStart"@touchmove="handleTouchMove"@touchend="handleTouchEnd"style="height: 100rpx"><view:style="{color: currentIndex === index ? 'yellow' : '#FFFFFF',transform: `translateX(${currentIndex === index ? '20rpx' : '0'})`,}"class="top_item"v-for="(item, index) in typeList":key="index">{{ item?.name }}</view></view><view class="bottom"><image@tap="handlePreviewImage(photoPath, [photoPath])"v-if="photoPath"class="pic":src="photoPath"/><view v-else class="pic">暂无</view><view><uni-icons@click="handleClick":color="isRecord ? '#ff0000' : `#ffffff`"type="circle-filled"size="56"></uni-icons></view><view><uni-icons@click="handleLoop"type="loop"color="#ffffff"size="40"></uni-icons></view></view></view></view>
</template><script setup lang="ts">
import { reactive, ref, onMounted, watch } from "vue";
import { handlePreviewImage } from "@/utils/common";// 相机实例
const cameraRef = ref(null);
// 照片路径
const photoPath = ref("");
//摄像头类型
const cameraType = ref<"back" | "front">("back");
//记录loop切换的类型
const loopFlag = ref(false);
//记录是否开始录制
const isRecord = ref(false);// 缩放相关
const initialDistance = ref(0); // 初始双指距离
const currentZoom = ref(1); // 当前缩放级别(初始1)
const maxZoom = 2.5; // 最大缩放级别
const minZoom = 1; // 最小缩放级别//操作类型
const typeList = reactive([{name: "拍照",type: 1,},{name: "录像",type: 2,},
]);const currentIndex = ref(0);
const startX = ref(1);
const endX = ref(1);// 双指缩放逻辑
const handleZoomStart = (e: TouchEvent) => {if (e.touches.length >= 2) {initialDistance.value = Math.hypot(e.touches[0].clientX - e.touches[1].clientX,e.touches[0].clientY - e.touches[1].clientY);}
};const handleZoomMove = (e: TouchEvent) => {if (e.touches.length >= 2 && initialDistance.value > 0) {const currentDistance = Math.hypot(e.touches[0].clientX - e.touches[1].clientX,e.touches[0].clientY - e.touches[1].clientY);// 计算缩放变化(更平滑的算法)const zoomDelta = (currentDistance - initialDistance.value) / 200;let newZoom = currentZoom.value + zoomDelta;newZoom = Math.max(minZoom, Math.min(maxZoom, newZoom));if (newZoom !== currentZoom.value) {currentZoom.value = newZoom;setCameraZoom(newZoom);}initialDistance.value = currentDistance;}
};// 设置相机缩放
const setCameraZoom = (zoom: number) => {const cameraContext = uni.createCameraContext();cameraContext.setZoom({zoom: zoom,success: () => console.log("缩放设置成功:", zoom),fail: (err) => console.error("缩放失败:", err),});
};// 初始化时设置默认缩放
onMounted(() => {setCameraZoom(1); // 初始化为1x
});// 其他原有方法保持不变(handleTouchStart、handleClick等...)
function handleTouchStart(e: any) {console.log(e, "start");startX.value = e.touches[0].clientX;
}function handleTouchMove(e: any) {endX.value = e.touches[0].clientX;
}function handleTouchEnd() {const diffX = startX.value - endX.value;if (diffX > 50 && currentIndex.value < typeList.length - 1) {currentIndex.value++;} else if (diffX < -50 && currentIndex.value > 0) {currentIndex.value--;}startX.value = endX.value = 0;
}const handleLoop = () => {loopFlag.value = !loopFlag.value;cameraType.value = loopFlag.value ? "front" : "back";
};const handleClick = () => {if (currentIndex.value === 0) {takePhoto();} else {isRecord.value ? stopRecord() : startRecord();}
};const takePhoto = async () => {try {const cameraContext = uni.createCameraContext();cameraContext.takePhoto({quality: "high",success: (res) => {photoPath.value = res.tempImagePath;},fail: console.error,});} catch (e) {console.error("相机异常", e);}
};const startRecord = () => {uni.showToast({ title: "开始录像", duration: 500 });const cameraContext = uni.createCameraContext();cameraContext.startRecord({timeout: 60000,success: () => (isRecord.value = true),fail: (err) => {isRecord.value = false;console.error("开始录像失败", err);},});
};const stopRecord = () => {isRecord.value = false;const cameraContext = uni.createCameraContext();cameraContext.stopRecord({success: (res) => {uni.showToast({ title: "录像结束", duration: 500 });photoPath.value = res?.tempThumbPath;},fail: console.error,});
};const handleCameraError = (e: any) => {console.error("相机错误", e);
};//如果切换拍照、录像,处于录像状态,则停止录像
watch(currentIndex, (val) => {if (isRecord.value) {stopRecord();}
});
</script><style scoped lang="scss">
.container {display: flex;flex-direction: column;align-items: center;background: black;color: white;width: 100vw;height: 100vh;position: relative;
}.zoom-indicator {position: absolute;top: 20rpx;left: 20rpx;background: rgba(0, 0, 0, 0.5);color: white;padding: 10rpx 20rpx;border-radius: 20rpx;z-index: 10;
}.btn_group {flex: 1;width: 100vw;display: flex;flex-direction: column;box-sizing: border-box;.top {width: 100%;display: flex;align-items: center;justify-content: center;.top_item {width: auto;color: white;margin-right: 32rpx;transition: all 0.3s ease;}}.bottom {margin-top: 20rpx;flex: 1;box-sizing: border-box;padding: 0 60rpx;display: flex;align-items: center;justify-content: space-between;.pic {width: 70rpx;height: 70rpx;border-radius: 12rpx;background: white;color: black;font-size: 20rpx;line-height: 70rpx;text-align: center;}}
}
</style>

希望本文对您实现相机功能有所帮助。注意:没有加录制超时逻辑,需要的话自行在startRecord回调中添加!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/95121.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/95121.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/95121.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python pyqt5开发DoIP上位机【诊断回复的函数都是怎么调用的?】

目录 文章合集 一、底层网络接收:`_receive_loop`(触发起点) 调用时机: 核心代码: 作用: 二、数据解析:`handle_received_data`(判断是否为诊断回复) 调用时机: 核心代码(诊断回复相关部分): 作用: 三、UI显示:`add_trace_entry`(展示到界面) 调用时机: 信号…

谈物质的运动与运动的物质

运动的物质是不是物质的运动&#xff0c;如果假设是&#xff08;第一假设&#xff09;&#xff0c;那末运动的物质是物质的运动&#xff0c;而运动是物质的根本属性&#xff0c;又运动的物质是物质&#xff0c;则物质的运动是物质&#xff0c;既然运动是物质的根本属性&#xf…

【MLLM】多模态理解Ovis2.5模型架构和训练流程

note 模型架构&#xff1a;延续 Ovis 系列创新的结构化嵌入对齐设计。 Ovis2.5 由三大组件构成&#xff1a;动态分辨率 ViT 高效提取视觉特征&#xff0c;Ovis 视觉词表模块实现视觉与文本嵌入的结构对齐&#xff0c;最后由强大的 Qwen3 作为语言基座&#xff0c;处理多模态嵌…

3.3单链表专题

顺序表这种在标准库已经实现好了&#xff0c;直接调用 pushback pushfront 这些o(1)表示不额外开辟空间src为value继续走&#xff0c;下一个不是value&#xff0c;src值给dst空间&#xff0c;dst&#xff0c;dst刚好等于2&#xff0c;就是新数组长度。若从前向后两个数组元素依…

linux系统学习(15.启动管理)

目录 一、运行级别 1.运行级别 2.运行级别命令 (1)runlevel (2)init 运行级别 3.永久修改启动级别&#xff08;ubantu20.04&#xff09; 二、启动过程 &#x1f539; 总结 三、启动引导程序grub配置文件 一、运行级别 1.运行级别 2.运行级别命令 (1)runlevel (2)ini…

检索优化-混合检索

混合检索&#xff08;Hybrid Search&#xff09;是一种结合了 稀疏向量&#xff08;Sparse Vectors&#xff09; 和 密集向量&#xff08;Dense Vectors&#xff09; 优势的先进搜索技术。旨在同时利用稀疏向量的关键词精确匹配能力和密集向量的语义理解能力&#xff0c;以克服…

Day17(前端:JavaScript基础阶段)

接续上文:Day16(前端:JavaScript基础阶段)_前端题目 csdn-CSDN博客 点关注不迷路哟。你的点赞、收藏&#xff0c;一键三连&#xff0c;是我持续更新的动力哟&#xff01;&#xff01;&#xff01; 主页:一位搞嵌入式的 genius-CSDN博客 系列文章专栏: https://blog.csdn.ne…

OpenCV 轮廓分析实战:从检测到形状匹配的完整指南

轮廓&#xff08;Contour&#xff09;是图像中连续且具有相同灰度值的像素集合&#xff0c;是描述目标形状、位置和结构的核心特征。在计算机视觉中&#xff0c;轮廓分析广泛应用于目标定位、形状识别、尺寸测量等场景&#xff08;如工业零件检测、手写数字识别&#xff09;。本…

2025最新uni-app横屏适配方案:微信小程序全平台兼容实战

以下为uni-app实现微信小程序横屏适配技术方案&#xff0c;包含核心原理、配置方法、代码示例和注意事项&#xff1a;一、横屏适配原理 微信小程序默认采用竖屏模式&#xff0c;横屏适配需通过以下机制实现&#xff1a; 全局配置&#xff1a;在app.json中声明支持横屏页面级配置…

深入解析Nginx常见模块1

在Web服务器和反向代理服务器领域,Nginx凭借其高性能、稳定性和丰富的功能获得了广泛的应用。本文将介绍一些Nginx中常见的模块,帮助你更好地理解和使用它们。 Nginx模块简介 Nginx的模块系统是其强大功能的核心所在,它允许用户根据需要灵活配置服务器的行为。Nginx的模块大…

浅谈new与::operator new

目录 前言 1.为什么C要引入new/delete&#xff1f; 2.operator new与operator delete函数 它们的实际作用 Placement New&#xff08;定位new表达式&#xff09; 总结 前言 在写上一篇博客“vector的模拟实现”时&#xff0c;我一直很好奇vector的private成员为什么要用三个封…

Java中Integer转String

在 Java 中&#xff0c;将 Integer 转换为 String 有多种方法&#xff0c;以下是常见的几种方式&#xff1a;1. 使用 Integer.toString() 方法javaInteger num 123; String str Integer.toString(num); // 直接调用 Integer 的静态方法2. 使用 String.valueOf()javaInteger n…

智能装备如何与软件结合?

一、什么是智能装备&#xff1f; 智能装备是具备“感知-决策-执行-自适应”闭环能力的智能化系统&#xff0c;本质是“传统物理装备”与“数字智能”的深度融合。它不仅能完成预设动作&#xff08;如传统机械臂焊接&#xff09;&#xff0c;还能通过传感器“观察”环境、用算法…

react性能优化有哪些

React 性能优化的手段比较多&#xff0c;既有代码层面的&#xff0c;也有构建层面的&#xff0c;还涉及到运行时调优。我帮你系统性梳理一份&#xff1a;&#x1f539; 一、渲染性能优化1. 减少不必要的渲染React.memo&#xff1a;对函数组件做浅比较&#xff0c;避免相同 prop…

腾讯云OpenCloudOS 9系统部署OpenTenBase数据库详细教程

OpenTenBase简介OpenTenBase是一个关系型数据库集群平台&#xff0c;提供写入可靠性和多节点数据同步功能。可以在一台或多台主机上配置OpenTenBase&#xff0c;并将数据存储在多个物理主机上。OpenTenBase架构组件&#xff1a;Coordinator Node (CN)&#xff1a;应用程序访问入…

【计算机视觉】Pixel逐像素分类Mask掩码分类理解摘要

目标检测和实例分割是计算机视觉的基本任务。目标检测的传统方法中通常利用边界框技术进行对象定位&#xff0c;然后利用逐像素分类为这些本地化实例分配类。但是当处理同一类的重叠对象时&#xff0c;或者在每个图像的对象数量不同的情况下&#xff0c;这些方法通常会出现问题…

C++之stack类的代码及其逻辑详解

1. stack介绍及使用方法stack是一种后进先出的数据结构&#xff0c;所以在C的STL库中也同样遵循了这一点&#xff0c;我们在使用的时候不支持随机访问或迭代器遍历。注意事项调用 top() 或 pop() 前需确保栈非空&#xff0c;否则可能引发未定义行为。stack 没有 clear() 函数&a…

Spring Cache实现简化缓存功能开发

一. 介绍Spring Cache 是 Spring 框架提供的缓存抽象层&#xff0c;它简化了在应用中添加缓存功能的开发工作。通过 Spring Cache&#xff0c;开发者无需关注具体缓存实现的细节&#xff0c;只需通过注解就能快速实现方法级别的缓存管理。核心特点1. 与具体缓存实现解耦&#x…

Lombok(简化Java当中的开发)

Lombok概述 以前的Java项目中,充斥着太多不友好的代码:POJO的getter/setter/toString/构造方法;打印日志;I/O流的关闭操作等等,这些代码既没有技术含量,又影响着代码的美观,Lombok应运而生。 LomBok可以通过注解,帮助开发人员消除JAVA中尤其是POJO类中的冗长代码。 使…

【DeepSeek】公司内网部署离线deepseek+docker+ragflow本地模型实战

企业内部可能有些数据比较敏感&#xff0c;不能连接互联网。本次实验操作是将deepseek完全离线后迁移至内网使用&#xff0c;实验基于Windows server 2022 datacenter系统安装deepseek、docker、ragflow。 目录使用VMware新建WIN2022虚拟机一、安装DeepSeek模型二.安装Docker使…