使用HTML5 / Canvas / JavaScript拍摄浏览器内屏幕截图

2020/09/19 11:01 · javascript ·  · 0评论

使用Google的“报告错误”或“反馈工具”,您可以选择浏览器窗口的区域来创建屏幕截图,并在屏幕上提交有关错误的反馈。

Google反馈工具截图
Jason Small的屏幕截图,张贴在一个重复的问题中

他们是如何做到的?Google的JavaScript反馈API已从此处加载它们对反馈模块的概述将演示屏幕截图功能。

JavaScript可以读取DOM并使用渲染该DOM的相当准确的表示形式canvas我一直在研究将HTML转换为画布图像的脚本。今天决定将其实现为发送您所描述的反馈。

该脚本允许您创建反馈表单,其中包括在客户端浏览器上创建的屏幕截图以及表单。屏幕截图基于DOM,因此可能无法真正表示真实屏幕截图的100%,因为它无法生成实际的屏幕截图,而是根据页面上的可用信息构建屏幕截图。

不需要来自服务器的任何渲染,因为整个图像是在客户端的浏览器上创建的。HTML2Canvas脚本本身仍处于试验性状态,因为它无法解析我想要的几乎所有CSS3属性,即使有可用的代理,它也不支持加载CORS图像。

仍然对浏览器的兼容性非常有限(不是因为无法支持更多功能,只是没有时间使其更受跨浏览器支持)。

有关更多信息,请在此处查看示例:

http://hertzen.com/experiments/jsfeedback/

编辑
html2canvas脚本现在可以
在此处单独使用,在此处可以使用一些示例

编辑2
Google反馈小组的Elliott Sprehn在此演示文稿中可以找到另一个证实Google使用了非常相似的方法(事实上,根据文档,唯一的不同是它们的遍历/绘制异步方法):
http: //www.elliottsprehn.com/preso/fluentconf/

您的网络应用现在可以使用getUserMedia()以下命令获取客户端整个桌面的“本地”屏幕截图

看一下这个例子:

https://www.webrtc-experiment.com/Pluginfree-Screen-Sharing/

客户端(现在必须)必须使用chrome,并且需要在chrome:// flags下启用屏幕捕获支持。

PoC

Niklas所述,您可以使用html2canvas库在浏览器中使用JS截屏。在这一点上,我将提供一个使用该库进行屏幕截图的示例(“概念证明”)来扩展他的答案:

获取图像作为数据URI后的report()功能中onrendered,您可以将其显示给用户,并允许他用鼠标绘制“错误区域”,然后将屏幕截图和区域坐标发送给服务器。

这个例子中, async/await版本是:具有很好的makeScreenshot()功能

更新

这个简单的示例可让您截取屏幕截图,选择区域,描述错误并发送POST请求(此处为jsfiddle)(主要功能是report())。

使用getDisplayMedia API 以Canvas或Jpeg Blob / ArrayBuffer的形式获取屏幕截图

FIX 1:仅针对Electron.js使用getUserMedia和chromeMediaSource
FIX 2:抛出错误,而是返回空对象
FIX 3:修复演示以防止错误:getDisplayMedia must be called from a user gesture handler

// docs: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia
// see: https://www.webrtc-experiment.com/Pluginfree-Screen-Sharing/#20893521368186473
// see: https://github.com/muaz-khan/WebRTC-Experiment/blob/master/Pluginfree-Screen-Sharing/conference.js

function getDisplayMedia(options) {
    if (navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia) {
        return navigator.mediaDevices.getDisplayMedia(options)
    }
    if (navigator.getDisplayMedia) {
        return navigator.getDisplayMedia(options)
    }
    if (navigator.webkitGetDisplayMedia) {
        return navigator.webkitGetDisplayMedia(options)
    }
    if (navigator.mozGetDisplayMedia) {
        return navigator.mozGetDisplayMedia(options)
    }
    throw new Error('getDisplayMedia is not defined')
}

function getUserMedia(options) {
    if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
        return navigator.mediaDevices.getUserMedia(options)
    }
    if (navigator.getUserMedia) {
        return navigator.getUserMedia(options)
    }
    if (navigator.webkitGetUserMedia) {
        return navigator.webkitGetUserMedia(options)
    }
    if (navigator.mozGetUserMedia) {
        return navigator.mozGetUserMedia(options)
    }
    throw new Error('getUserMedia is not defined')
}

async function takeScreenshotStream() {
    // see: https://developer.mozilla.org/en-US/docs/Web/API/Window/screen
    const width = screen.width * (window.devicePixelRatio || 1)
    const height = screen.height * (window.devicePixelRatio || 1)

    const errors = []
    let stream
    try {
        stream = await getDisplayMedia({
            audio: false,
            // see: https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamConstraints/video
            video: {
                width,
                height,
                frameRate: 1,
            },
        })
    } catch (ex) {
        errors.push(ex)
    }

    // for electron js
    if (navigator.userAgent.indexOf('Electron') >= 0) {
        try {
            stream = await getUserMedia({
                audio: false,
                video: {
                    mandatory: {
                        chromeMediaSource: 'desktop',
                        // chromeMediaSourceId: source.id,
                        minWidth         : width,
                        maxWidth         : width,
                        minHeight        : height,
                        maxHeight        : height,
                    },
                },
            })
        } catch (ex) {
            errors.push(ex)
        }
    }

    if (errors.length) {
        console.debug(...errors)
        if (!stream) {
            throw errors[errors.length - 1]
        }
    }

    return stream
}

async function takeScreenshotCanvas() {
    const stream = await takeScreenshotStream()

    // from: https://stackoverflow.com/a/57665309/5221762
    const video = document.createElement('video')
    const result = await new Promise((resolve, reject) => {
        video.onloadedmetadata = () => {
            video.play()
            video.pause()

            // from: https://github.com/kasprownik/electron-screencapture/blob/master/index.js
            const canvas = document.createElement('canvas')
            canvas.width = video.videoWidth
            canvas.height = video.videoHeight
            const context = canvas.getContext('2d')
            // see: https://developer.mozilla.org/en-US/docs/Web/API/HTMLVideoElement
            context.drawImage(video, 0, 0, video.videoWidth, video.videoHeight)
            resolve(canvas)
        }
        video.srcObject = stream
    })

    stream.getTracks().forEach(function (track) {
        track.stop()
    })
    
    if (result == null) {
        throw new Error('Cannot take canvas screenshot')
    }

    return result
}

// from: https://stackoverflow.com/a/46182044/5221762
function getJpegBlob(canvas) {
    return new Promise((resolve, reject) => {
        // docs: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob
        canvas.toBlob(blob => resolve(blob), 'image/jpeg', 0.95)
    })
}

async function getJpegBytes(canvas) {
    const blob = await getJpegBlob(canvas)
    return new Promise((resolve, reject) => {
        const fileReader = new FileReader()

        fileReader.addEventListener('loadend', function () {
            if (this.error) {
                reject(this.error)
                return
            }
            resolve(this.result)
        })

        fileReader.readAsArrayBuffer(blob)
    })
}

async function takeScreenshotJpegBlob() {
    const canvas = await takeScreenshotCanvas()
    return getJpegBlob(canvas)
}

async function takeScreenshotJpegBytes() {
    const canvas = await takeScreenshotCanvas()
    return getJpegBytes(canvas)
}

function blobToCanvas(blob, maxWidth, maxHeight) {
    return new Promise((resolve, reject) => {
        const img = new Image()
        img.onload = function () {
            const canvas = document.createElement('canvas')
            const scale = Math.min(
                1,
                maxWidth ? maxWidth / img.width : 1,
                maxHeight ? maxHeight / img.height : 1,
            )
            canvas.width = img.width * scale
            canvas.height = img.height * scale
            const ctx = canvas.getContext('2d')
            ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height)
            resolve(canvas)
        }
        img.onerror = () => {
            reject(new Error('Error load blob to Image'))
        }
        img.src = URL.createObjectURL(blob)
    })
}

演示:

document.body.onclick = async () => {
    // take the screenshot
    var screenshotJpegBlob = await takeScreenshotJpegBlob()

    // show preview with max size 300 x 300 px
    var previewCanvas = await blobToCanvas(screenshotJpegBlob, 300, 300)
    previewCanvas.style.position = 'fixed'
    document.body.appendChild(previewCanvas)

    // send it to the server
    var formdata = new FormData()
    formdata.append("screenshot", screenshotJpegBlob)
    await fetch('https://your-web-site.com/', {
        method: 'POST',
        body: formdata,
        'Content-Type' : "multipart/form-data",
    })
}

// and click on the page

以下是使用示例:getDisplayMedia

document.body.innerHTML = '<video style="width: 100%; height: 100%; border: 1px black solid;"/>';

navigator.mediaDevices.getDisplayMedia()
.then( mediaStream => {
  const video = document.querySelector('video');
  video.srcObject = mediaStream;
  video.onloadedmetadata = e => {
    video.play();
    video.pause();
  };
})
.catch( err => console.log(`${err.name}: ${err.message}`));

另外值得一提的是Screen Capture API文档。

本文地址:http://javascript.askforanswer.com/shiyonghtml5-canvas-javascriptpaisheliulanqineipingmujietu.html
文章标签: ,   ,   ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!