ब्राउज़र स्क्रीनशॉट लेने के लिए HTML5 / कैनवास / जावास्क्रिप्ट का उपयोग करना


923

Google का "बग की रिपोर्ट करें" या "फ़ीडबैक टूल" आपको एक स्क्रीनशॉट बनाने के लिए अपनी ब्राउज़र विंडो का एक क्षेत्र चुनने देता है जो बग के बारे में आपकी प्रतिक्रिया के साथ प्रस्तुत किया जाता है।

Google फ़ीडबैक टूल स्क्रीनशॉट जेसन स्मॉल द्वारा स्क्रीनशॉट, एक डुप्लिकेट प्रश्न में पोस्ट किया गया ।

वे यह कैसे कर रहे हैं? Google की जावास्क्रिप्ट प्रतिक्रिया एपीआई यहां से भरी हुई है और फीडबैक मॉड्यूल के उनके अवलोकन से स्क्रीनशॉट क्षमता प्रदर्शित होगी।


2
Elliott Sprehn ने कुछ दिनों पहले एक ट्वीट में लिखा था:> @CatChen यह स्टैकओवरफ्लो पोस्ट सटीक नहीं है। Google फ़ीडबैक का स्क्रीनशॉट पूरी तरह से क्लाइंट साइड में किया जाता है। :)
गोरान रािक

1
यह सीम तार्किक है क्योंकि वे उपयोगकर्ता के पृष्ठ को कैसे प्रस्तुत कर रहे हैं, इसे ठीक उसी तरह से पकड़ना चाहते हैं, न कि कैसे वे अपने इंजन का उपयोग करते हुए सर्वर साइड पर इसे प्रस्तुत करेंगे। यदि आप केवल वर्तमान पृष्ठ DOM को सर्वर पर भेजते हैं तो यह किसी भी विसंगतियों को याद करेगा कि ब्राउज़र HTML को कैसे प्रस्तुत कर रहा है। इसका मतलब यह नहीं है कि चेन का जवाब स्क्रीनशॉट लेने के लिए गलत है, ऐसा लगता है कि Google इसे एक अलग तरीके से कर रहा है।
गोरान राकिक

इलियट ने आज जन कूकी का उल्लेख किया, और मुझे यह लिंक जनवरी के ट्वीट में मिला: jankuca.tumblr.com/post/7391640769/…
कैट चेन

मैं इसे बाद में खोदूंगा और देखूंगा कि क्लाइंट-साइड रेंडरिंग इंजन के साथ यह कैसे किया जा सकता है और जांच लें कि क्या Google वास्तव में इस तरह से करता है।
कैट चेन

मैं तुलनाDocumentPosition, getBoxObjectFor, toDataURL, drawImage, ट्रैकिंग पैडिंग और इस तरह की चीजों का उपयोग देखता हूं। यह हज़ारों पंक्तियों की हज़ारों पंक्तियों को हज़म करने के लिए और यद्यपि के माध्यम से देखना है। मैं इसका एक खुला स्रोत लाइसेंस प्राप्त संस्करण देखना पसंद करूंगा, मैंने इलियट स्प्रीएन से संपर्क किया है!
ल्यूक स्टेनली

जवाबों:


1152

जावास्क्रिप्ट डोम पढ़ सकते हैं और उस का एक बहुत ही सटीक प्रतिनिधित्व प्रस्तुत कर सकते हैं canvas। मैं एक स्क्रिप्ट पर काम कर रहा हूं जो HTML को एक कैनवास छवि में परिवर्तित करता है। आपके द्वारा बताई गई प्रतिक्रियाओं को भेजने में इसे लागू करने के लिए आज निर्णय लिया गया।

स्क्रिप्ट आपको फीडबैक फॉर्म बनाने की अनुमति देती है जिसमें एक स्क्रीनशॉट शामिल होता है, जो क्लाइंट के ब्राउज़र पर फॉर्म के साथ बनाया जाता है। स्क्रीनशॉट डोम पर आधारित है और जैसे वास्तविक प्रतिनिधित्व के लिए 100% सटीक नहीं हो सकता है क्योंकि यह एक वास्तविक स्क्रीनशॉट नहीं बनाता है, लेकिन पेज पर उपलब्ध जानकारी के आधार पर स्क्रीनशॉट का निर्माण करता है।

यह सर्वर से किसी भी प्रतिपादन की आवश्यकता नहीं है , क्योंकि क्लाइंट के ब्राउज़र पर पूरी छवि बनाई गई है। HTML2Canvas स्क्रिप्ट स्वयं अभी भी एक बहुत ही प्रायोगिक स्थिति में है, क्योंकि यह CSS3 के लगभग इतने गुणों को पार्स नहीं करता है, जो मैं इसे चाहूंगा, और न ही इसके लिए कोई प्रॉक्सी उपलब्ध होने पर भी CORS छवियों को लोड करने का कोई समर्थन नहीं है।

अभी भी काफी सीमित ब्राउज़र संगतता (क्योंकि अधिक समर्थित नहीं हो सकती है, बस इसे और अधिक क्रॉस ब्राउज़र समर्थित बनाने का समय नहीं है)।

अधिक जानकारी के लिए, यहां दिए गए उदाहरण देखें:

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

html2canvas स्क्रिप्ट संपादित करें अब यहां और कुछ उदाहरणों के लिए अलग से उपलब्ध है

2 संपादित करें एक और पुष्टि कि Google एक बहुत ही समान विधि का उपयोग करता है (वास्तव में, प्रलेखन के आधार पर, केवल प्रमुख अंतर उनके ट्रैवर्सिंग / ड्राइंग का एसिंक्स विधि है) इस प्रस्तुति में Google फ़ीडबैक टीम से इलियट स्प्रीन् द्वारा पाया जा सकता है: http: //www.elliottsprehn.com/preso/fluentconf/


1
बहुत शांत, सिकुली या सेलेनियम विभिन्न साइटों पर जाने के लिए अच्छा हो सकता है, परीक्षण उपकरण से साइट के एक शॉट की तुलना अपने html2canvas.js में पिक्सेल समानता के संदर्भ में छवि प्रदान की गई है! आश्चर्य है कि अगर आप स्वचालित रूप से डोम के कुछ हिस्सों को एक बहुत ही सरल फॉर्मूला सॉल्वर से पार कर सकते हैं, तो यह पता लगाने के लिए कि ब्राउज़र के लिए वैकल्पिक डेटा स्रोतों को पार्स कैसे किया जाए जहां getBoundingClientRect उपलब्ध नहीं है। अगर यह खुला स्रोत था, तो मैं शायद इसका इस्तेमाल करूंगा, इसके साथ खुद से बात करने पर विचार कर रहा था। अच्छा काम निकलस!
ल्यूक स्टेनली

1
@ ल्यूक स्टेनली की संभावना है कि मैं इस सप्ताह के अंत में जीथब पर स्रोत को फेंक दूंगा, फिर भी कुछ मामूली साफ-सुथरे बदलाव और बदलाव जो मैं पहले करना चाहता हूं, साथ ही वर्तमान में होने वाली अनावश्यक jQuery निर्भरता से छुटकारा पा सकता हूं।
निकल्स

43
स्रोत कोड अब github.com/niklasvh/html2canvas पर उपलब्ध है , स्क्रिप्ट के कुछ उदाहरणों में html2canvas.hertzen.com का उपयोग किया गया है। अभी भी बहुत सारे कीड़े ठीक करने के लिए, इसलिए मैं अभी तक एक जीवित वातावरण में स्क्रिप्ट का उपयोग करने की अनुशंसा नहीं करूंगा।
निकल्स

2
एसवीजी के लिए इसे काम करने के लिए कोई भी समाधान एक बड़ी मदद होगी। यह highcharts.com के साथ काम नहीं करता है
जगदीप

3
@ निक्लेस मैं देख रहा हूं कि आपका उदाहरण एक वास्तविक परियोजना में विकसित हुआ। हो सकता है कि परियोजना की प्रायोगिक प्रकृति के बारे में अपनी सबसे उत्कट टिप्पणी अपडेट करें। लगभग 900 कमिट्स के बाद, मैं इस बिंदु पर एक प्रयोग से थोड़ा अधिक
सोचूंगा

70

अपने वेब एप्लिकेशन अब ग्राहक के पूरे का उपयोग कर डेस्कटॉप के 'स्थानीय' स्क्रीनशॉट ले सकते हैं getUserMedia():

इस उदाहरण पर एक नज़र डालें:

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

क्लाइंट को क्रोम (अभी के लिए) का उपयोग करना होगा और क्रोम: // झंडे के नीचे स्क्रीन कैप्चर समर्थन को सक्षम करने की आवश्यकता होगी।


2
मुझे सिर्फ स्क्रीनशॉट लेने का कोई डेमो नहीं मिल रहा है - सब कुछ स्क्रीन-शेयरिंग के बारे में है। कोशिश करनी होगी।
jwl

8
@ XMight, आप स्क्रीन कैप्चर सपोर्ट फ्लैग को टॉगल करके इसे अनुमति दे सकते हैं या नहीं।
मैट सिंक्लेयर

19
@ XMight कृपया इस तरह मत सोचो। वेब ब्राउज़र को बहुत सारी चीजें करने में सक्षम होना चाहिए, लेकिन दुर्भाग्य से वे अपने कार्यान्वयन के अनुरूप नहीं हैं। यह बिल्कुल ठीक है, अगर किसी ब्राउज़र में ऐसी कार्यक्षमता है, जब तक उपयोगकर्ता से पूछा जा रहा है। कोई भी आपके ध्यान के बिना स्क्रीनशॉट नहीं बना पाएगा। लेकिन क्लिपबोर्ड एपीआई जैसे खराब कार्यान्वयन में बहुत अधिक भय उत्पन्न होता है, जिसे पूरी तरह से अक्षम कर दिया गया है, इसके बजाय पुष्टि डायलॉग बनाने के लिए, जैसे कि वेबकैम,
मिक्स

3
इसे हटा दिया गया था और इसे डेवलपर.
mozilla.org/en-US/docs/Web/API/Navigator/getUserMedia के

7
@AgustinCautin Navigator.getUserMedia()को पदावनत कर दिया गया है, लेकिन इसके ठीक नीचे लिखा है "... कृपया नए नेविगेटर का उपयोग करें ।mediaDevices.getUserMedia () ", अर्थात इसे बस एक नए एपीआई के साथ बदल दिया गया था।
लेवांत

37

जैसा कि निकलास ने उल्लेख किया है कि आप ब्राउज़र में JS का उपयोग करके स्क्रीनशॉट लेने के लिए html2canvas लाइब्रेरी का उपयोग कर सकते हैं । मैं इस लाइब्रेरी का उपयोग करके स्क्रीनशॉट लेने का एक उदाहरण प्रदान करके इस बिंदु पर उनके उत्तर का विस्तार करूंगा:

में report()में समारोह onrenderedडेटा यूआरआई के रूप में छवि प्राप्त करने के बाद आप यह उपयोगकर्ता को दिखाने के लिए और उसे माउस द्वारा "बग क्षेत्र" आकर्षित और फिर सर्वर से एक स्क्रीनशॉट और इस क्षेत्र निर्देशांक भेजने के लिए अनुमति दे सकते हैं।

में इस उदाहरण async/await संस्करण बनाया गया था: अच्छा के साथ makeScreenshot()समारोह

अपडेट करें

सरल उदाहरण जो आपको स्क्रीनशॉट लेने, क्षेत्र का चयन करने, बग का वर्णन करने और POST अनुरोध ( यहां jsfiddle ) (मुख्य फ़ंक्शन है report()) भेजने की अनुमति देता है ।


10
आप शून्य अंक देना चाहते हैं, तो भी स्पष्टीकरण के साथ टिप्पणी छोड़
कामिल Kiełczewski

मुझे लगता है कि जिस कारण से आप निराश हो रहे हैं, सबसे अधिक संभावना है कि html2canvas लाइब्रेरी उनकी लाइब्रेरी है, न कि एक उपकरण जिसे उन्होंने केवल बताया।
zfrisch

यदि आप पोस्ट-प्रोसेसिंग प्रभाव (ब्लर फ़िल्टर के रूप में) को कैप्चर नहीं करना चाहते हैं तो यह ठीक है।
vintproykt

सीमाएं वे सभी छवियां जो स्क्रिप्ट का उपयोग करती हैं, उन्हें उसी मूल के तहत रहने की आवश्यकता होती है, जो उन्हें प्रॉक्सी की सहायता के बिना पढ़ने में सक्षम हो। इसी तरह, यदि आपके पास पृष्ठ पर अन्य कैनवास तत्व हैं, जो क्रॉस-ओरिजिनल सामग्री के साथ दागी गई हैं, तो वे गंदे हो जाएंगे और अब html2canvas द्वारा पठनीय नहीं होंगे।
अरविंद 3

13

GetDisplayMedia API का उपयोग करके कैनवस या Jpeg ब्लॉब / 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",
})

आश्चर्य है कि यह केवल 1 अपवोट क्यों था, यह वास्तव में मददगार साबित हुआ!
जय दादानिया

कृपया यह कैसे काम करता है? क्या आप मेरे जैसे नवाबों के लिए डेमो दे सकते हैं? Thx
kabrice

@kabrice मैंने एक डेमो जोड़ा। बस कोड को क्रोम कंसोल में रखें। यदि आपको पुराने ब्राउज़रों के समर्थन की आवश्यकता है, तो उपयोग करें: babeljs.io/en/repl
निकोले

7

Heres एक उदाहरण का उपयोग कर: 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}`));

इसके अलावा स्क्रीन कैप्चर एपीआई डॉक्स की जाँच करने लायक है ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.