前端(十八):移动端H5调用摄像头拍照旋转解决方案

简介: 移动端H5调用摄像头拍照旋转解决方案

展示

展示

布局样式

<script src="https://cdn.bootcss.com/vConsole/3.3.4/vconsole.min.js"></script>

<style>
    canvas{
    
    
        width: 200px;
        height: 200px;
        opacity: 0;
    }
    .upload{
    
    
        width: 200px;
        height: 200px;
        display: inline-block;
        border-radius: 10px;
        border: 1px dashed rgb(122, 201, 221);
        background-color: rgb(188, 224, 223);
        overflow: hidden;
        position: relative;
        display: inline-block;
    }
    #upload-img{
    
    
        width: 100%;
        height: 100%;
        object-fit: cover;
    }
    input[type='file']{
    
    
        position: absolute;
        top: 0;
        left: 0;
        font-size: 1000%;
        opacity: 0;
    }
</style>
<div>
    <div class="upload">
        <img id="upload-img" src="" alt="">
        <input id="file" type="file" capture="camera" accept="image/*" onchange="handleFileChange(this)">
    </div>

    <canvas id="canvas" width="200" height="200"></canvas>
</div>

<script src="https://cdn.jsdelivr.net/npm/exif-js"></script>

逻辑

var vConsole = new VConsole();

/**
 * 监听调用摄像头
 */
async function handleFileChange(element) {
   
   
    var file = element.files[0];
    const or = await getImageTag(file);
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onloadend = function () {
   
   
        const result = this.result;
        const img = new Image();
        img.src = result;
        img.onload = function () {
   
   
            const data = getRotateImg(img, or);
            const f = dataURLtoFile(data);

            /*
            // 模拟上传文件后的回显
            const reader1 = new FileReader();
            reader1.readAsDataURL(f);
            reader1.onloadend = function () {
                const result1 = this.result;
                document.getElementById("upload-img").src = result1;
            }
            */

        };
    };
}

/**
 * @desc 获取图片旋转方向
 */
const getImageTag = (file) => {
   
   
    if (!file) return 0;
    return new Promise((resolve, reject) => {
   
   
        EXIF.getData(file, function () {
   
   
            const o = EXIF.getTag(this, 'Orientation');
            resolve(o); // 6 1 3 8
        });
    });
};

/**
 * @desc 获取旋转后的图片
 * @param {Object} img 图片文件
 * @param {Number} or 旋转信息
 */
function getRotateImg(img, or) {
   
   
    const canvas = document.getElementById('canvas');
    const ctx = canvas.getContext('2d');
    // 原始图片
    const width = img.width;
    const height = img.height;
    canvas.width = width;
    canvas.height = height;
    ctx.drawImage(img, 0, 0, width, height);
    // 旋转后图片
    switch (or) {
   
   
        case 6: // 顺时针旋转90度
            return rotateImg(img, 'right', canvas);
            break;
        case 8: // 逆时针旋转90度
            return rotateImg(img, 'left', canvas);
            break;
        case 3: // 顺时针旋转180度
            return rotateImg(img, 'right', canvas, 2);
            break;
        default:
            break;
    }
}

/**
 * @desc 旋转canvas,会对源数据canvas进行修改
 * @param {Object} img 图片文件
 * @param {String} dir 方向 left逆时针|right顺时针
 * @param {Object} canvas 画布
 * @param {Number} s 向指定方向旋转几步,1步为90度
 */
const rotateImg = (img, dir = 'right', canvas, s = 1) => {
   
   
    const MIN_STEP = 0;
    const MAX_STEP = 3;

    const width = canvas.width || img.width;
    const height = canvas.height || img.height;
    let step = 0;

    if (dir === 'right') {
   
   
        step += s;
        step > MAX_STEP && (step = MIN_STEP);
    } else {
   
   
        step -= s;
        step < MIN_STEP && (step = MAX_STEP);
    }

    const degree = step * 90 * Math.PI / 180;
    const ctx = canvas.getContext('2d');

    switch (step) {
   
   
        case 1:
            canvas.width = height;
            canvas.height = width;
            ctx.rotate(degree);
            ctx.drawImage(img, 0, -height, width, height);
            break;
        case 2:
            canvas.width = width;
            canvas.height = height;
            ctx.rotate(degree);
            ctx.drawImage(img, -width, -height, width, height);
            break;
        case 3:
            canvas.width = height;
            canvas.height = width;
            ctx.rotate(degree);
            ctx.drawImage(img, -width, 0, width, height);
            break;
        default:
            canvas.width = width;
            canvas.height = height;
            ctx.drawImage(img, 0, 0, width, height);
            break;
    }

    return canvas.toDataURL('image/jpg');
};


/**
 * @desc 将base64的图片转为文件流
 * @param {String} dataUrl base64数据
 * @return {Object} 文件流
 */
const dataURLtoFile = (dataUrl) => {
   
   
    const filename = `img${
     
     Date.now()}`;
    const arr = dataUrl.split(',');
    const mime = arr[0].match(/:(.*?);/)[1];
    const bstr = atob(arr[1]);
    let n = bstr.length;
    const u8arr = new Uint8Array(n);
    while (n--) {
   
   
        u8arr[n] = bstr.charCodeAt(n);
    }
    return new File([u8arr], filename, {
   
   
        type: mime
    });
};
目录
相关文章
|
3天前
|
移动开发 前端开发 JavaScript
惊!这些前端技术竟然能让你的网站在移动端大放异彩!
随着互联网技术的发展,移动设备成为主要的上网工具。本文介绍了几种关键的前端技术,包括响应式设计、图片优化、字体选择、HTML5和CSS3的应用、性能优化及手势操作设计,帮助开发者提升网站在移动端的显示效果和用户体验。示例代码展示了如何实现简单的双向绑定功能。
11 3
|
8天前
|
移动开发 前端开发 JavaScript
前端实训,刚入门,我用原生技术(H5、C3、JS、JQ)手写【网易游戏】页面特效
于辰在大学期间带领团队参考网易游戏官网的部分游戏页面,开发了一系列前端实训作品。项目包括首页、2021校园招聘页面和明日之后游戏页面,涉及多种特效实现,如动态图片切换和人物聚合效果。作品源码已上传至CSDN,视频效果可在CSDN预览。
13 0
前端实训,刚入门,我用原生技术(H5、C3、JS、JQ)手写【网易游戏】页面特效
|
28天前
|
前端开发 JavaScript
乾坤qiankun(微前端)样式隔离解决方案--使用插件替换前缀
乾坤qiankun(微前端)样式隔离解决方案--使用插件替换前缀
268 8
|
28天前
|
机器学习/深度学习 弹性计算 自然语言处理
前端大模型应用笔记(二):最新llama3.2小参数版本1B的古董机测试 - 支持128K上下文,表现优异,和移动端更配
llama3.1支持128K上下文,6万字+输入,适用于多种场景。模型能力超出预期,但处理中文时需加中英翻译。测试显示,其英文支持较好,中文则需改进。llama3.2 1B参数量小,适合移动端和资源受限环境,可在阿里云2vCPU和4G ECS上运行。
|
1月前
|
设计模式 前端开发 JavaScript
前端编程的异步解决方案有哪些?
本文首发于微信公众号“前端徐徐”,介绍了异步编程的背景和几种常见方案,包括回调、事件监听、发布订阅、Promise、Generator、async/await和响应式编程。每种方案都有详细的例子和优缺点分析,帮助开发者根据具体需求选择最合适的异步编程方式。
64 1
|
1月前
|
编解码 前端开发 UED
前端:移动端视口配置
移动端视口配置是指针对移动设备浏览器设置视口的宽度、高度和缩放等属性,以确保网页能根据不同的屏幕尺寸和分辨率进行适配,提供更好的用户体验。合理的视口配置是移动优先设计的关键环节。
|
3月前
|
开发者 安全 UED
JSF事件监听器:解锁动态界面的秘密武器,你真的知道如何驾驭它吗?
【8月更文挑战第31天】在构建动态用户界面时,事件监听器是实现组件间通信和响应用户操作的关键机制。JavaServer Faces (JSF) 提供了完整的事件模型,通过自定义事件监听器扩展组件行为。本文详细介绍如何在 JSF 应用中创建和使用事件监听器,提升应用的交互性和响应能力。
35 0
|
3月前
|
前端开发 JavaScript 中间件
【前端状态管理之道】React Context与Redux大对决:从原理到实践全面解析状态管理框架的选择与比较,帮你找到最适合的解决方案!
【8月更文挑战第31天】本文通过电子商务网站的具体案例,详细比较了React Context与Redux两种状态管理方案的优缺点。React Context作为轻量级API,适合小规模应用和少量状态共享,实现简单快捷。Redux则适用于大型复杂应用,具备严格的状态管理规则和丰富的社区支持,但配置较为繁琐。文章提供了两种方案的具体实现代码,并从适用场景、维护成本及社区支持三方面进行对比分析,帮助开发者根据项目需求选择最佳方案。
57 0
|
3月前
|
前端开发 测试技术 API
现代前端开发中的跨平台挑战与解决方案探讨
随着移动设备和桌面端用户体验的日益融合,现代前端开发面临着跨平台兼容性的重大挑战。本文将探讨这些挑战的根源,并介绍一些创新的解决方案,帮助开发人员更好地应对不同平台之间的差异,提升应用程序的用户体验和性能。
|
28天前
|
存储 人工智能 前端开发
前端大模型应用笔记(三):Vue3+Antdv+transformers+本地模型实现浏览器端侧增强搜索
本文介绍了一个纯前端实现的增强列表搜索应用,通过使用Transformer模型,实现了更智能的搜索功能,如使用“番茄”可以搜索到“西红柿”。项目基于Vue3和Ant Design Vue,使用了Xenova的bge-base-zh-v1.5模型。文章详细介绍了从环境搭建、数据准备到具体实现的全过程,并展示了实际效果和待改进点。
118 2