仿照 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', {}) |
| } |