實現微信小程序的UniApp相機組件:拍照、錄像與雙指縮放

在微信小程序開發中,相機功能已成為許多應用的核心組成部分。本文將介紹如何使用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使…