仿照 https://www.bilibili.com/video/BV1AQ4y1c7Le?p=2

原版使用 electron 实现
打算使用 tauri 来实现一下

项目地址:https://github.com/FanLu1994/tauri-vue-camera

# 调用摄像头

  1. web 中调用摄像头需要申请权限
await navigator.mediaDevices.getUserMedia({  
  audio: true,  
  video: true,  
});
  1. 然后获取摄像头列表
// 这里使用了 vueuse 的 hook https://vueuse.org/core/useDevicesList/
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)  
      }  
    },  
})
  1. 然后获取视频流
// 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/>
//js
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', {})  
}