仿照 https://www.bilibili.com/video/BV1AQ4y1c7Le?p=2
原版使用 electron 实现
打算使用 tauri 来实现一下
项目地址:https://github.com/FanLu1994/tauri-vue-camera
# 调用摄像头
- web 中调用摄像头需要申请权限
|  | await navigator.mediaDevices.getUserMedia({   | 
|  |   audio: true,   | 
|  |   video: true,   | 
|  | }); | 
- 然后获取摄像头列表
|  |  | 
|  | const { videoInputs: cameras } = useDevicesList({   | 
|  |   requestPermissions: true,   | 
|  |   onUpdated() {   | 
|  |     if (!cameras.value.find(i => i.deviceId === currentCamera.value)) {   | 
|  |       currentCamera.value = cameras.value[0].deviceId   | 
|  |       onCameraChange(currentCamera.value)   | 
|  |       }   | 
|  |     },   | 
|  | }) | 
- 然后获取视频流
|  | // template | 
|  | <video ref="videoRef" muted autoplay class="h-100 w-auto video"   | 
|  |        :class="{'circle':shape==='circle','rectangle':shape==='rectangle','mirror':mirror?'mirror':''}"   | 
|  |        data-tauri-drag-region/> | 
|  |  | 
|  | function onCameraChange(cameraId) {   | 
|  |   currentCamera.value = cameraId   | 
|  |   if (currentStream){   | 
|  |     currentStream.getTracks().forEach(track => track.stop());   | 
|  |   }   | 
|  |   if(currentCamera.value){   | 
|  |     let constraints = { deviceId: { exact: currentCamera.value } };   | 
|  |     navigator.mediaDevices   | 
|  |         .getUserMedia({ video: constraints, audio: false })   | 
|  |         .then(function (stream) {   | 
|  |           videoRef.value.srcObject = stream;   | 
|  |           currentStream = stream;   | 
|  |         });   | 
|  |   }   | 
|  | } | 
# 无边框和拖动
都可以在 /src-tauri/tauri.conf.json 中配置
# 无边框配置
|  | { | 
|  | 	... | 
|  | 	"tauri": {   | 
|  | 		...  | 
|  | 	  "windows": [   | 
|  | 	    {   | 
|  | 	      "title": "tauri-vue-camera",   | 
|  | 	      "label": "tauri-vue-camera",   | 
|  | 	      "width": 300,   | 
|  | 	      "height": 200,   | 
|  | 	      "center": true,   | 
|  | 	      "resizable": true,   | 
|  | 	      "transparent": true,   | 
|  | 	      "hiddenTitle": true,   | 
|  | 	      "alwaysOnTop":true,   | 
|  | 	      "decorations": false   | 
|  | 	    }   | 
|  | 	  ],   | 
|  | 		...   | 
|  | 	} | 
|  | 	... | 
|  | } | 
# 拖动配置
|  | { | 
|  | 	... | 
|  | 	"tauri": {   | 
|  | 		...  | 
|  | 		"window": {   | 
|  | 		  "startDragging": true   | 
|  | 		} | 
|  | 		...   | 
|  | 	} | 
|  | 	... | 
|  | } | 
在 html 中指定可以拖动的区域,加上 data-tauri-drag-region
|  | <div class="container" data-tauri-drag-region> | 
# 摄像头镜像
镜像使用 css 来实现即可
 html  mirror 变量来控制
|  | <video ref="videoRef" muted autoplay class="h-100 w-auto video"   | 
|  |        :class="{'circle':shape==='circle','rectangle':shape==='rectangle','mirror':mirror?'mirror':''}"   | 
|  |        data-tauri-drag-region/> | 
css:
|  | .video.mirror{   | 
|  |   transform: rotateY(180deg);   | 
|  | } | 
# 退出窗口
因为隐藏了标题栏,没有了退出窗口,所以自定义一个点击事件来退出程序
# 首先注册一个 tauri 事件
|  | #[tauri::command]   | 
|  | fn close(){   | 
|  |     std::process::exit(0);   | 
|  | } | 
|  |  | 
|  | fn main() {   | 
|  |  | 
|  |     tauri::Builder::default()   | 
|  |         .invoke_handler(tauri::generate_handler![greet])   | 
|  |         .invoke_handler(tauri::generate_handler![close])   | 
|  |         .run(tauri::generate_context!())   | 
|  |         .expect("error while running tauri application");   | 
|  | } | 
# 然后自定义点击回调事件
|  | const onClose = async () => {   | 
|  |   await invoke('close', {})   | 
|  | } |