九旬的博客-编程思维

浏览器截图方案分析

页面截屏是前端经常遇到的需求,比如页面生成海报,弹窗图片分享等。

以下是我整理三种截图方案:

  1. html2canvas
  2. dom-to-image
  3. webRTC

html2canvas

html2canvas 用的比较广泛的前端截图方案,先将 DOM 一个个 转为 Canvas 然后导出图片(使用 canvas 自带的 toDataUrl、toBobl)即可。使用起来应该是兼容性比较好的方案了,能解决大部分的需求,但是也有一写小问题,如:

  1. 图片跨域,开启
  2. CSS 属性错乱
  3. 遇到 canvas 元素导出后为透明色。

大部分问题还是可以通过配置和百度解决的

const getDomImg = (eleId) => {
    html2canvas(document.getElementById(eleId), {
        //superMap整个页面的节点
        backgroundColor: null, //画出来的图片有白色的边框,不要可设置背景为透明色(null)
        allowTaint: true,
        useCORS: true, //支持图片跨域
        scale: 1, //设置放大的倍数
    })
        .then((canvas) => {
            //截图用img元素承装,显示在页面的上
            let img = new Image();
            img.src = canvas.toDataURL("image/jpg"); // toDataURL :图片格式转成 base64
            // 直接下载
            let a = document.createElement("a");
            a.href = canvas.toDataURL("image/jpeg");
            a.download = "test";
            a.click();
        })
        .catch((err) => {
            console.log("html2canvas err", err);
        });
};

dom-to-image

使用 svg,通过 createObjectURL 或 encodeURIComponent 处理 svg 得到图像资源,可以把 svg 绘制到 canvas。

dom-to-image-moredom-to-image的升级版 将 HTMl 放到 SVG 里,然后创建一个以 SVG 作为源的 Image 元素

但是也有一些问题如: svg 中不允许外部资源(js,css,img 的 url 等),svg 中不支持执行 js,需要经过处理,也不能完全还原

const getDomImg = (eleId) => {
    domtoimage
        .toPng(document.getElementById(eleId))
        .then(function (dataUrl) {
            let a = document.createElement("a");
            a.href = dataUrl;
            a.download = "test";
            a.click();
        })
        .catch(function (error) {
            console.error("生成失败", error);
        });
};

webRTC

使用浏览器原生 API——webERT中的getDisplayMedia可以将窗口中的资源以录屏方式从其中拿出一帧,但是需要用户授权和做一些窗口选择,相比于前两种方案做不到默认截图。

但是优势也很明显,就是不会有什么样式错乱、图片跨域等问题。因为使用的浏览器原生方法,基本上用户看到是什么样子,截图出来就是什么样子,1: 1 还原。

function getDomImg(videoId: string) {
    const videoElem: any = document.getElementById(videoId);

    const displayMediaOptions = {
        audio: false,
        video: { width: window.screen.width, height: window.screen.height }, // cursor: "always"
    };

    navigator.mediaDevices
        .getDisplayMedia(displayMediaOptions)
        .then((stream) => {
            videoElem.srcObject = stream;
            setTimeout(() => {
                var canvas = document.createElement("canvas");
                canvas.width = videoElem.clientWidth;
                canvas.height = videoElem.clientHeight;
                canvas
                    .getContext("2d")
                    .drawImage(
                        videoElem,
                        0,
                        0,
                        window.screen.width,
                        window.screen.height,
                        0,
                        0,
                        canvas.width,
                        canvas.height
                    );
                var dataURL = canvas.toDataURL("image/webp");
                let a = document.createElement("a");
                a.href = dataURL;
                a.download = "test";
                a.click();
                let tracks = videoElem.srcObject.getTracks();
                tracks.forEach((track) => track.stop());
                videoElem.srcObject = null;
            }, 200);
        })
        .finally(() => {});
}

需要在代码中放入一个 Video 标签

<video id="video"></video>

截图后上传

如果有将截图上传的需求,可以转换一个格式在上传。

canvas 导出的 base64 是不可以直接上传到服务器的,所以需要转一下格式,我这边找了转换 Blob 和 file 两种格式的方法。我用的将图片转为 Blob后上传的。

  1. base64转化为Blob对象
// 
function convertBase64ToBlob(imageEditorBase64) {
    var base64Arr = imageEditorBase64.split(",");
    var imgtype = "";
    var base64String = "";
    if (base64Arr.length > 1) {
        //如果是图片base64,去掉头信息
        base64String = base64Arr[1];
        imgtype = base64Arr[0].substring(
            base64Arr[0].indexOf(":") + 1,
            base64Arr[0].indexOf(";")
        );
    }
    // 将base64解码
    var bytes = atob(base64String);
    //var bytes = base64;
    var bytesCode = new ArrayBuffer(bytes.length);
    // 转换为类型化数组
    var byteArray = new Uint8Array(bytesCode);

    // 将base64转换为ascii码
    for (var i = 0; i < bytes.length; i++) {
        byteArray[i] = bytes.charCodeAt(i);
    }

    // 生成Blob对象(文件对象)
    return new Blob([bytesCode], { type: imgtype });
}

  1. base64 转 formData

function base64ToFile(data, fileName) {
    const dataArr = data.split(",");
    const byteString = atob(dataArr[1]);
    const options: any = {
        type: "image/jpeg",
        endings: "native",
    };
    const u8Arr = new Uint8Array(byteString.length);
    for (let i = 0; i < byteString.length; i++) {
        u8Arr[i] = byteString.charCodeAt(i);
    }
    let formData = new FormData();
    let fileOfBlob = new File([u8Arr], fileName + ".jpg", options); //返回文件流
    formData.append("file", fileOfBlob);
    console.log("file", formData);
    debugger;
    return formData;
}

示例

GitHub:https://github.com/AnsonZnl/w...

代码基于 Create React App 演示三种截图方法的基本使用方式。

参考

版权声明:本文版权归作者所有,遵循 CC 4.0 BY-SA 许可协议, 转载请注明原文链接
https://segmentfault.com/a/1190000043372936

个人文章-编程思维

前言极度投入,深度沉浸,边界清晰前端小菜鸡一枚,分享的文章纯属个人见解,若有不正确或可待讨论点可随意评论,与各位同学一起学习~欢迎关注 『前端进阶圈』 公众号 ,一起探索学习前端技术......公众号回复 加群 或 扫码, 即可加入前端交流学习群,长期交流学习......公众号回复 加好友,即可r添加为好友热点面试题:

hawk-编程思维

最近在重构管理前端代码(Vue Element Admin实现)的时候,发现鼠标每次悬停在顶导菜单,控制台就会输出错误信息:Uncaught RangeError: Maximum call stack size exceeded搜索了一下,发现还不是框架的的bug,而是 Element UI 组件库中,NavMenu

九旬的博客-编程思维

前言我们在 Web 开发过程中,有很多用到音频元素audio的场景,如音乐播放器、语音播放等功能,但是由于原生组件有以下缺点:原生UI样式丑,在追求美观的页面中需要重写元素样式,而且支持重写的样式不多。浏览器兼容问题,不同浏览器的音频元素展示不同。因为以上两个问题,这就导致我们需要对原生的audio进行修改时比较困难。

个人文章-编程思维

一.SSR 简介SSR(Server-Side Rendering)并不是什么新奇的概念,前后端分层之前很长的一段时间里都是以服务端渲染为主(JSP、PHP),在服务端生成完整的 HTML 页面——《前端渲染模式的探索》我们之所以要在服务端完成组件渲染工作,就是因为其有性能与可访问性两大优势二.两大优势性能与 CSR模

个人文章-编程思维

1.css的格式选择器{声明}2.选择器标签选择器p{ }类选择器.XXX{ } (一个标签可以有多个属性,用空格分开,<p calss=”iss sd”></p>)id选择器#XXX{} (id值每个值都不一样,只能调用一次)通配符选择器*{ } (选取所有元素)3.css的字

九旬的博客-编程思维

前端图片压缩方案压缩图片原理:先通过 js 中 img 构造函数,实例化 img 对象,后将图片的路径给转移到中,再建立一个 canvas 画布,后对画布进行各方面的数值的设置。如代码所示:<!DOCTYPE html> <html lang="en"> <head>

个人文章-编程思维

大致有两种web开发模式1.基于服务端渲染的传统 Web 开发模式2.基于前后端分离的新型 Web 开发模式服务端渲染:服务器发送给客户端的 HTML 页面,是在服务器通过字符串的拼接动态生成。因此,客户端不需要使用 Ajax 这样的技术额外请求页面的数据优点前端耗时少。因为服务器端负责动态生成 HTML 内容,浏览器

个人文章-编程思维

本章的前提就是大家都知道动画的基本属性,例如animation-name、animation-duration、animation-timing-function、animation-delay、animation-iteration-count和animation-direction 属性。了解更多 animatio

九旬的博客-编程思维

前言我们在 Web 开发过程中,有很多用到音频元素audio的场景,如音乐播放器、语音播放等功能,但是由于原生组件有以下缺点:原生UI样式丑,在追求美观的页面中需要重写元素样式,而且支持重写的样式不多。浏览器兼容问题,不同浏览器的音频元素展示不同。因为以上两个问题,这就导致我们需要对原生的audio进行修改时比较困难。

个人文章-编程思维

开发环境JDK 1.8Lombok - 1.18.16背景公司因为开源节流需要,需要区分重要日志和非重要日志,一个月光日志的费用有30w之多确实恐怖了一些(PS:够几十个程序员的工资了)。所以,公司要求将日志区分开,去除一些非必要的日志,记录的日志也最多保留15天,重要日志30天。为了区分场景日志,单独搞了个Logge

个人文章-编程思维

预览服务器上的html静态网页远程服务器中html静态文件不方便直接查看使用vscode快速预览远程服务器上的html静态文件我遇到的场景:我使用windows电脑办公,使用vscode的ssh远程到一台ubuntu主机上开发近期使用pytest+allure做一些测试实验,生成的报告是静态html在本地windows