手机浏览器实现拍照功能

1. 简单搭建结构和样式demo

<div>
     <video style="width: 100vw;height: 80vh;" id="preview" autoplay playsinline></video>
     <img id="img" width="100" src="" alt="">
     <canvas id="snapshot" style="display: none;"></canvas>
     <ul style="position: absolute;left: 0;top:0;display: flex;padding-left: 30px;">
            <li onClick="zoomFocus(0.5)" style="padding: 10px;background-color: #000;color:#fff;list-style: none;margin-right: 10px;">0.5</li>
            <li onClick="zoomFocus(1)" style="padding: 10px;background-color: #000;color:#fff;list-style: none;margin-right: 10px;">1</li>
            <li onClick="zoomFocus(2)" style="padding: 10px;background-color: #000;color:#fff;list-style: none;">2</li>
         </ul>
     <button onclick="takeSnapshot()">拍照</button>
</div>

2. 使用navigator.mediaDevices.getUserMedia api获取stream对象

 navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } })
            .then(function (stream) {
                document.getElementById('preview').srcObject = stream;
            }).catch(function (err) {
                console.log("无法获取媒体设备", err);
            });

3. 判断手机是否是竖直摆放还是横向摆放

  if ('orientation' in screen) {
                    // 监听屏幕方向变化事件
                    screen.orientation.addEventListener('change', () => {
                        console.log(`${screen.orientation.type === 'landscape-primary' ? '当前手机是横向摆放' : '当前手机是竖直摆放'}`);
                    });
                    // 获取当前屏幕方向
                    console.log(`${screen.orientation.type === 'landscape-primary' ? '当前手机是横向摆放' : '当前手机是竖直摆放'}`);
                } else {
                    console.log('浏览器不支持 Screen Orientation API');
                }

4. 判断手机屏幕和地方是否是水平还是垂直

                if (typeof DeviceOrientationEvent.requestPermission === 'function') {
                    //ios兼容
                    // 这个API存在,说明是iOS 13+,需要请求权限
                    DeviceOrientationEvent.requestPermission()
                        .then(permissionState => {
                            if (permissionState === 'granted') {
                                // 权限被授予,现在可以添加事件监听器了
                                window.addEventListener('deviceorientation', function (event) {
                                    const beta = event.beta;  // 前后倾斜角度 [-180,180]
                                    const gamma = event.gamma; // 左右倾斜角度 [-90,90]

                                    if (beta !== null && gamma !== null) {
                                        // 设备大致与地面平行
                                        if (Math.abs(beta) < 15 && Math.abs(gamma) < 15) {
                                            
                                            alert('设备与地面平行')
                                        }
                                        // 设备大致垂直于地面
                                        else if (Math.abs(beta) > 75 && Math.abs(gamma) < 15) {
                                         
                                            alert('设备垂直于地面')
                                        }
                                        else {
                                            
                                            alert('设备处于其他倾斜状态')
                                        }
                                    }
                                }, false);
                            } else {
                                console.log("设备方向访问权限未被授予");
                            }
                        })
                        .catch(console.error);
                } else if ('DeviceOrientationEvent' in window) {
                    //安卓兼容
                    window.addEventListener('deviceorientation', function (event) {
                        const beta = event.beta;  // 前后倾斜角度 [-180,180]
                        const gamma = event.gamma; // 左右倾斜角度 [-90,90]

                        if (beta !== null && gamma !== null) {

                            if (Math.abs(beta) < 15 && Math.abs(gamma) < 15) {
                                // 设备大致与地面平行
                                console.log('设备与地面平行')
                            } else if (Math.abs(beta) > 75 && Math.abs(gamma) < 15) {
                                // 设备大致垂直于地面
                                console.log('设备垂直于地面')
                            }
                            else {
                                console.log('设备处于其他倾斜状态')
                            }
                        }
                    }, false);
                }

5. 调整手机摄像头变焦倍数

async function zoomFocus(zoom) {
            try {
                this.stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } });
                document.getElementById('preview').srcObject = this.stream;
                if (!this.stream) return;
                const track = this.stream.getVideoTracks()[0];
                const capabilities = track.getCapabilities();
                // 检查是否支持焦距调整
                if (!capabilities.zoom) return;
                track.applyConstraints({
                    advanced: [{ zoom: zoom }]
                }).catch(e => console.error(e));
                
            } catch (error) {
                console.error('启动摄像头失败:', error);
            }
        }

以下为完整代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div>
        <video style="width: 100vw;height: 80vh;" id="preview" autoplay playsinline></video>
        <img id="img" width="100" src="" alt="">
        <canvas id="snapshot" style="display: none;"></canvas>
        <ul>
           <li onClick="zoomFocus(0.5)">0.5</li>
           <li onClick="zoomFocus(1)">1</li>
           <li onClick="zoomFocus(2)">2</li>
        </ul>
        <button onclick="start()">拍照</button>

    </div>

    <script>
        navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } })
            .then(function (stream) {
                document.getElementById('preview').srcObject = stream;
                //判断手机是否是竖直摆放还是横向摆放
                if ('orientation' in screen) {
                    // 监听屏幕方向变化事件
                    screen.orientation.addEventListener('change', () => {
                        console.log(`${screen.orientation.type === 'landscape-primary' ? '当前手机是横向摆放' : '当前手机是竖直摆放'}`);
                    });
                    // 获取当前屏幕方向
                    console.log(`${screen.orientation.type === 'landscape-primary' ? '当前手机是横向摆放' : '当前手机是竖直摆放'}`);
                } else {
                    console.log('浏览器不支持 Screen Orientation API');
                }
                //判断手机屏幕和地方是否是水平还是垂直
                if (typeof DeviceOrientationEvent.requestPermission === 'function') {
                    //ios兼容
                    // 这个API存在,说明是iOS 13+,需要请求权限
                    DeviceOrientationEvent.requestPermission()
                        .then(permissionState => {
                            if (permissionState === 'granted') {
                                // 权限被授予,现在可以添加事件监听器了
                                window.addEventListener('deviceorientation', function (event) {
                                    const beta = event.beta;  // 前后倾斜角度 [-180,180]
                                    const gamma = event.gamma; // 左右倾斜角度 [-90,90]

                                    if (beta !== null && gamma !== null) {
                                        // 设备大致与地面平行
                                        if (Math.abs(beta) < 15 && Math.abs(gamma) < 15) {
                                            console.log("设备与地面平行");
                                        }
                                        // 设备大致垂直于地面
                                        else if (Math.abs(beta) > 75 && Math.abs(gamma) < 15) {
                                            console.log("设备垂直于地面");
                                        }
                                        else {
                                            console.log("设备处于其他倾斜状态");
                                        }
                                    }
                                }, false);
                            } else {
                                console.log("设备方向访问权限未被授予");
                            }
                        })
                        .catch(console.error);
                } else if ('DeviceOrientationEvent' in window) {
                    //安卓兼容
                    window.addEventListener('deviceorientation', function (event) {
                        const beta = event.beta;  // 前后倾斜角度 [-180,180]
                        const gamma = event.gamma; // 左右倾斜角度 [-90,90]

                        if (beta !== null && gamma !== null) {

                            if (Math.abs(beta) < 15 && Math.abs(gamma) < 15) {
                                // 设备大致与地面平行
                                alert('设备与地面平行')
                            } else if (Math.abs(beta) > 75 && Math.abs(gamma) < 15) {
                                // 设备大致垂直于地面
                                alert('设备垂直于地面')
                            }
                            else {
                                alert('设备处于其他倾斜状态')
                            }
                        }
                    }, false);
                }
            })
            .catch(function (err) {
                console.log("无法获取媒体设备", err);
            });

        function start() {
            let video = document.getElementById('preview');
            let canvas = document.getElementById('snapshot');
            let img = document.getElementById('img');
            let context = canvas.getContext('2d');

            canvas.width = video.videoWidth;
            canvas.height = video.videoHeight;
            context.drawImage(video, 0, 0, canvas.width, canvas.height);

            // 导出图片
            let imageUrl = canvas.toDataURL('image/png');
            // 可以将 imageUrl 用于显示或保存操作
            console.log(imageUrl)
            img.src = imageUrl
        }
         async function zoomFocus(zoom) {
            try {
                this.stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } });
                document.getElementById('preview').srcObject = this.stream;
                if (!this.stream) return;
                const track = this.stream.getVideoTracks()[0];
                const capabilities = track.getCapabilities();
                // 检查是否支持焦距调整
                if (!capabilities.zoom) return;
                track.applyConstraints({
                    advanced: [{ zoom: zoom }]
                }).catch(e => console.error(e));
                
            } catch (error) {
                console.error('启动摄像头失败:', error);
            }
        }
    </script>
</body>

</html>
<think>好的,我现在需要帮助用户解决在Vue3项目中实现手机浏览器调用摄像头拍照并上传的问题。首先,我得回忆一下相关的知识,并结合提供的引用内容来构建答案。 用户提到了Vue3、手机浏览器摄像头拍照和上传。根据引用内容,有几个关键点需要注意: 1. 权限问题:引用[2]提到本地调试时Chrome可能需要配置,线上必须使用HTTPS。这点很重要,因为很多摄像头API在非安全环境下会被阻止。 2. 调用摄像头的API:使用navigator.mediaDevices.getUserMedia,这在引用[1]和[3]中都有提到。Vue3和Vue2的用法应该类似,但需要注意Vue3的组合式API写法。 3. 前后摄像头的配置:引用[3]中的例子使用了facingMode来指定后置摄像头,这在移动设备上可能需要调整。 4. 拍照后的处理:如何将视频帧转换为图片数据,可能是base64或者Blob,然后上传到服务器。 5. 兼容性和错误处理:不同浏览器的支持情况,如何处理权限拒绝或设备不支持的情况。 接下来需要分步骤思考实现过程: 第一步,请求摄像头权限。使用getUserMedia方法,这里需要处理Promise,可能需要async/await。同时,配置video的constraints,比如分辨率和前后摄像头。在移动端,默认可能是前置,要切换后置可能需要设置facingMode为'environment',如引用[3]所示。 第二步,将视频流显示在页面上。创建一个video元素,将其srcObject设为获取到的流,并播放。需要注意在Vue中通过ref来操作DOM元素。 第三步,拍照功能。创建一个canvas元素,将video的当前帧绘制到canvas上,然后转换为图片数据。这里可以用toDataURL得到base64,或者toBlob获取Blob对象,方便上传。 第四步,上传图片数据。使用FormData包装后,通过axios或其他HTTP库发送到服务器。需要处理可能的错误,比如网络问题。 第五步,清理资源。关闭摄像头,释放视频流,避免占用设备。 此外,需要注意的细节包括: - 在组件卸载时停止视频流,防止内存泄漏。 - 处理移动端浏览器的兼容性问题,比如Safari可能需要额外的配置。 - 测试HTTPS环境下的功能,因为引用[2]强调线上必须HTTPS才能自动调起摄像头。 - 错误处理,比如用户拒绝权限时给出提示,或者设备没有摄像头的情况。 现在需要将这些步骤结构化为代码示例,并确保符合Vue3的组合式API风格,比如使用ref、onMounted、onUnmounted等。同时,引用中的信息需要正确标注,比如关于HTTPS的要求来自引用[2],前后摄像头配置参考引用[3]。 可能遇到的问题: - 视频元素无法播放,可能需要添加autoplay和playsinline属性,特别是iOS设备。 - 不同浏览器对facingMode的支持不同,需要测试。 - 图片质量和大小的控制,可能在canvas的toBlob方法中调整参数。 最后,要生成相关问题,比如如何切换前后摄像头,如何处理权限拒绝,如何优化图片上传等,这些都能帮助用户进一步了解相关实现。</think>### Vue3手机浏览器调用摄像头拍照上传实现方法 在Vue3中实现手机浏览器调用摄像头拍照上传功能,需遵循以下步骤: --- #### 一、环境准备 1. **HTTPS协议**:线上部署必须使用HTTPS协议才能自动调用摄像头[^2] 2. **Vue3项目**:使用`<script setup>`语法或组合式API --- #### 二、核心代码实现 ```vue <template> <video ref="videoRef" autoplay playsinline></video> <button @click="capture">拍照</button> <img :src="previewUrl" v-if="previewUrl"> <button @click="upload" v-if="previewUrl">上传</button> </template> <script setup> import { ref, onMounted, onUnmounted } from 'vue' const videoRef = ref(null) const previewUrl = ref('') let mediaStream = null // 初始化摄像头 const initCamera = async () => { try { mediaStream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment', // 后置摄像头,前置用'user' width: { ideal: 1280 }, height: { ideal: 720 } } }) videoRef.value.srcObject = mediaStream } catch (error) { console.error('摄像头访问失败:', error) } } // 拍照 const capture = () => { const canvas = document.createElement('canvas') const video = videoRef.value canvas.width = video.videoWidth canvas.height = video.videoHeight canvas.getContext('2d').drawImage(video, 0, 0) previewUrl.value = canvas.toDataURL('image/jpeg', 0.8) } // 上传 const upload = async () => { const blob = await fetch(previewUrl.value).then(r => r.blob()) const formData = new FormData() formData.append('file', blob, 'photo.jpg') try { await axios.post('/api/upload', formData) alert('上传成功') } catch (error) { console.error('上传失败:', error) } } // 生命周期 onMounted(initCamera) onUnmounted(() => { mediaStream?.getTracks().forEach(track => track.stop()) }) </script> ``` --- #### 三、关键配置说明 1. **摄像头方向控制**: - `facingMode: 'environment'` 调用后置摄像头 - `facingMode: 'user'` 调用前置摄像头[^3] 2. **移动端适配**: - `playsinline`属性确保iOS设备内联播放 - 建议设置固定宽高比例(如16:9) --- #### 四、常见问题解决 1. **权限被拒绝**: - 检查HTTPS协议(本地调试可用`localhost`) - 引导用户手动开启权限[^2] 2. **黑屏问题**: - 确保`srcObject`正确赋值 - 检查浏览器兼容性列表 3. **图片方向错误**: - 使用`exif-js`库处理图片元数据 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值