Googleの「バグの報告」または「フィードバックツール」を使用すると、ブラウザウィンドウの領域を選択して、バグに関するフィードバックとともに送信されるスクリーンショットを作成できます。
重複質問に投稿されたJason Smallのスクリーンショット。
彼らはこれをどのように行っていますか?GoogleのJavaScriptのフィードバックAPIからロードされ、こことフィードバックモジュールのその概要は、スクリーンショット機能のデモンストレーションを行います。
Googleの「バグの報告」または「フィードバックツール」を使用すると、ブラウザウィンドウの領域を選択して、バグに関するフィードバックとともに送信されるスクリーンショットを作成できます。
重複質問に投稿されたJason Smallのスクリーンショット。
彼らはこれをどのように行っていますか?GoogleのJavaScriptのフィードバックAPIからロードされ、こことフィードバックモジュールのその概要は、スクリーンショット機能のデモンストレーションを行います。
回答:
JavaScriptはDOMを読み取り、を使用してDOMをかなり正確に表現できますcanvas
。私はHTMLをキャンバス画像に変換するスクリプトに取り組んでいます。あなたが説明したようなフィードバックを送信するためにそれを実装することを今日決定しました。
スクリプトを使用すると、フォームとともにクライアントのブラウザーで作成されたスクリーンショットを含むフィードバックフォームを作成できます。スクリーンショットはDOMに基づいており、実際のスクリーンショットを作成するのではなく、ページで利用可能な情報に基づいてスクリーンショットを作成するため、実際の表現に対して100%正確ではない場合があります。
これは、サーバーから任意のレンダリングを必要としません。画像全体がクライアントのブラウザ上で作成されたとして、。HTML2Canvasスクリプト自体は、非常に実験的な状態です。これは、必要なCSS3属性のほとんどを解析しないため、プロキシが使用可能であってもCORS画像をロードするためのサポートがないためです。
それでもブラウザーの互換性はかなり限られています(サポートできないものはありませんでした。クロスブラウザーをよりサポートする時間がないためです)。
詳細については、こちらの例をご覧ください。
http://hertzen.com/experiments/jsfeedback/
編集 html2canvasスクリプトは、ここで個別に、いくつかの例はここで利用できます。
編集2 Googleが非常によく似た方法を使用していることの別の確認(実際には、ドキュメントに基づいて、唯一の主な違いは、走査/描画の非同期方法です)は、GoogleフィードバックチームのElliott Sprehnによるこのプレゼンテーションで見つかります: http: //www.elliottsprehn.com/preso/fluentconf/
Webアプリは、次を使用してクライアントのデスクトップ全体の「ネイティブ」スクリーンショットを取得できるようになりましたgetUserMedia()
。
この例を見てください:
https://www.webrtc-experiment.com/Pluginfree-Screen-Sharing/
クライアントは(今のところ)chromeを使用している必要があり、chrome:// flagsで画面キャプチャのサポートを有効にする必要があります。
Navigator.getUserMedia()
は非推奨ですが、そのすぐ下に「...新しいnavigator.mediaDevices.getUserMedia()を使用してください」と表示されます。つまり、新しいAPIに置き換えられただけです。
ニクラスは言及を使用できhtml2canvasのブラウザでJSを使用してスクリーンショットを取るためのライブラリを。この点について、このライブラリを使用してスクリーンショットを撮る例を提供することで、彼の答えを拡張します。
に report()
機能でonrendered
データURIとして画像を取得した後、あなたはそれをユーザーに示し、彼はマウスで「バグ地域を」描くし、サーバーにスクリーンショットや地域の座標を送信できるようにすることができます。
で、この例 async/await
のバージョンが作られました。素敵でmakeScreenshot()
機能。
スクリーンショットを撮り、地域を選択し、バグを説明し、POSTリクエストを送信する簡単な例(ここではjsfiddle)(主な機能はreport()
)。
getDisplayMedia API を使用して、CanvasまたはJpeg Blob / ArrayBufferとしてスクリーンショットを取得します。
// 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)
}
try {
// for electron js
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)
}
return stream
}
async function takeScreenshotCanvas() {
const stream = await takeScreenshotStream()
if (!stream) {
return null
}
// 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()
})
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()
if (!canvas) {
return null
}
return getJpegBlob(canvas)
}
async function takeScreenshotJpegBytes() {
const canvas = await takeScreenshotCanvas()
if (!canvas) {
return null
}
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)
})
}
デモ:
// 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
let formdata = new FormData()
formdata.append("screenshot", screenshotJpegBlob)
await fetch('https://your-web-site.com/', {
method: 'POST',
body: formdata,
'Content-Type' : "multipart/form-data",
})
使用例: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}`));
また、スクリーンキャプチャAPIドキュメントも確認してください。