ব্রাউজার ইন স্ক্রিনশট নিতে HTML5 / ক্যানভাস / জাভাস্ক্রিপ্ট ব্যবহার করা


923

গুগলের "একটি বাগের প্রতিবেদন করুন" বা "প্রতিক্রিয়া সরঞ্জাম" আপনাকে একটি ত্রুটি সম্পর্কে আপনার প্রতিক্রিয়ার সাথে জমা দেওয়া একটি স্ক্রিনশট তৈরি করতে আপনাকে আপনার ব্রাউজার উইন্ডোর একটি অঞ্চল নির্বাচন করতে দেয়।

গুগল প্রতিক্রিয়া সরঞ্জামের স্ক্রিনশট নকল প্রশ্নে পোস্ট করা জেসন স্মল দ্বারা স্ক্রিনশট ।

কিভাবে তারা এই করছেন? গুগলের জাভাস্ক্রিপ্ট ফিডব্যাক এপিআই এখান থেকে লোড করা হয়েছে এবং প্রতিক্রিয়া মডিউলের তাদের ওভারভিউ স্ক্রিনশট ক্ষমতা প্রদর্শন করবে।


2
এলিয়ট স্প্রেহন কিছুদিন আগে একটি টুইটে লিখেছিলেন :> @ গুগলচেন যে স্ট্যাকওভারফ্লো পোস্টটি সঠিক নয়। গুগল ফিডব্যাকের স্ক্রিনশটটি সম্পূর্ণ ক্লায়েন্টের পক্ষেই সম্পন্ন হয়। :)
গোরান রিকিক

এই ব্যবহারকারীর ব্রাউজারটি ঠিক কীভাবে কোনও পৃষ্ঠা রেন্ডার করছে তা ধরতে চাইলে তারা এটিকে যৌক্তিক করে তোলে, তাদের ইঞ্জিনটি ব্যবহার করে কীভাবে তারা সার্ভার সাইডে এটি রেন্ডার করে। আপনি যদি কেবলমাত্র পৃষ্ঠাটি ডিওএম সার্ভারে প্রেরণ করেন তবে এটি ব্রাউজার কীভাবে HTML উপস্থাপন করছে তাতে কোনও অসঙ্গতি মিস হবে। এর অর্থ এই নয় যে স্ক্রিনশট নেওয়ার জন্য চেনের উত্তরটি ভুল, এটি দেখে মনে হচ্ছে গুগল এটি অন্যভাবে করছে।
গোরান রিক 18

এলিয়ট আজ জান কুয়ার কথা উল্লেখ করেছেন এবং আমি জানার টুইটটিতে
ক্যাট চেন

আমি এটি পরে খনন করব এবং ক্লায়েন্ট-সাইড রেন্ডারিং ইঞ্জিনের সাহায্যে এটি কীভাবে করা যায় তা দেখুন এবং গুগলের প্রকৃতপক্ষে সেভাবে এটি করা হচ্ছে কিনা তা পরীক্ষা করব।
বিড়াল চেন

আমি তুলনাডোকামেন্ট পজিশন, গেটবক্সঅবজেক্টফোর, টুডাটা ইউআরএল, ড্রইম্যাজ, ট্র্যাকিং প্যাডিং এবং এর মতো জিনিসগুলির ব্যবহার দেখতে পাচ্ছি। এটি কয়েক হাজার লাইন অবলুপ্ত কোডটি ডি-ওবফাসকেট করতে এবং যদিও তা দেখার জন্য। আমি এর একটি ওপেন সোর্স লাইসেন্সেড সংস্করণ দেখতে পছন্দ করব, আমি এলিয়ট স্প্রেহনের সাথে যোগাযোগ করেছি!
লুক স্ট্যানলি

উত্তর:


1152

জাভাস্ক্রিপ্টটি ডিওএম পড়তে পারে এবং এটির ব্যবহারের মোটামুটি নির্ভুল প্রতিনিধিত্ব করতে পারে canvas। আমি এমন স্ক্রিপ্টে কাজ করছি যা এইচটিএমএলকে ক্যানভাস চিত্রে রূপান্তর করে। আপনার বর্ণিত মতামত ফিডব্যাকগুলি প্রেরণে এর বাস্তবায়ন করার সিদ্ধান্ত নিয়েছে আজ।

স্ক্রিপ্টটি আপনাকে ফিডব্যাক ফর্ম তৈরি করতে দেয় যা ফর্মের সাথে ক্লায়েন্টের ব্রাউজারে তৈরি একটি স্ক্রিনশট অন্তর্ভুক্ত করে। স্ক্রিনশটটি ডিওএম এর উপর ভিত্তি করে এবং প্রকৃত উপস্থাপনার ক্ষেত্রে এটি 100% সঠিক নাও হতে পারে কারণ এটি কোনও আসল স্ক্রিনশট তৈরি করে না, তবে পৃষ্ঠায় উপলব্ধ তথ্যের উপর ভিত্তি করে স্ক্রিনশট তৈরি করে।

এটি সার্ভার থেকে কোনও রেন্ডারিংয়ের প্রয়োজন হয় না , কারণ পুরো চিত্রটি ক্লায়েন্টের ব্রাউজারে তৈরি হয়েছিল। এইচটিএমএল 2 ক্যানভাস স্ক্রিপ্ট নিজেই এখনও খুব পরীক্ষামূলক অবস্থায় রয়েছে, কারণ এটি যে সিএসএস 3 গুণাবলীর কাছে চাইবে তার প্রায় পার্স করে না, এমনকি প্রক্সি পাওয়া গেলেও সিওআরএস চিত্র লোড করার কোনও সমর্থন নেই।

এখনও যথেষ্ট সীমিত ব্রাউজারের সামঞ্জস্যতা (কারণ আরও বেশি সমর্থন করা যায়নি, কেবল এটি আরও ক্রস ব্রাউজার সমর্থিত করার জন্য সময় পাননি)।

আরও তথ্যের জন্য, উদাহরণগুলি এখানে দেখুন:

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

এইচটিএমএল 2 ক্যানভাস স্ক্রিপ্ট সম্পাদনা করুন এখন এখানে আলাদাভাবে এবং কিছু উদাহরণ পাওয়া যায় ।

সম্পাদনা 2 আরেকটি নিশ্চিতকরণ যে গুগল একটি খুব অনুরূপ পদ্ধতি ব্যবহার করে (বাস্তবে, ডকুমেন্টের উপর ভিত্তি করে, কেবল প্রধান পার্থক্য তাদের ট্র্যাসিং / অঙ্কনয়ের অ্যাসিঙ্ক পদ্ধতি) গুগল প্রতিক্রিয়া দল থেকে এলিয়ট স্প্রেন এই উপস্থাপনাটিতে খুঁজে পেতে পারেন: HTTP: //www.elliottsprehn.com/preso/fluentconf/


1
খুব দুর্দান্ত, সিকুলি বা সেলেনিয়াম বিভিন্ন সাইটগুলিতে যাওয়ার জন্য ভাল হতে পারে, পরীক্ষার সরঞ্জাম থেকে আপনার এইচটিএমএল 2 ক্যানভাস.জেএস এর রেন্ডার করা চিত্রটির সাথে পিক্সেলের মিলের সাথে তুলনা করে সাইটটির শট তুলনা করুন! অবাক হোন যদি আপনি স্বয়ংক্রিয়ভাবে ডিওএমের অংশগুলিকে খুব সাধারণ সূত্র সল্ভারের সাথে অতিক্রম করতে পারেন যেখানে গেটবাউন্ডিংক্লায়েন্টআরেক্ট উপলব্ধ নেই এমন ব্রাউজারগুলির জন্য কীভাবে বিকল্প ডেটা উত্সগুলি পার্স করতে হয় তা সন্ধান করতে পারেন। আমি সম্ভবত এটি ব্যবহার করতাম যদি এটি ওপেন সোর্স হয় তবে আমি নিজে এটির সাথে কথা বলার চিন্তা করছিলাম। ভাল কাজ নিক্লাস!
লুক স্ট্যানলি

1
@ লুক স্ট্যানলে আমি সম্ভবত এই সপ্তাহান্তে উত্সটিকে গিথুবটিতে ফেলে দেব, তবুও কিছু ছোট ছোট পরিচ্ছন্নতা এবং আমি তার আগে যা করতে চাই তা পরিবর্তনগুলি বজায় রাখতে হবে, পাশাপাশি এটি বর্তমানে যে অপ্রয়োজনীয় jQuery নির্ভরতা থেকে মুক্তি পেয়েছে তা থেকে মুক্তি পাব।
নিক্লাস

43
সোর্স কোডটি এখন github.com/niklasvh/html2canvas এ পাওয়া যাবে , সেখানে html2canvas.hertzen.com ব্যবহারের স্ক্রিপ্টের কয়েকটি উদাহরণ । ত্রুটিগুলি স্থির করার জন্য এখনও প্রচুর, তাই আমি কোনও লাইভ পরিবেশে স্ক্রিপ্টটি ব্যবহার করার পরামর্শ দেব না।
নিক্লাস

2
এসভিজির পক্ষে এটি কার্যকর করার যে কোনও সমাধান একটি দুর্দান্ত সহায়তা হবে। এটি হাইচার্টস ডট কম নিয়ে কাজ করে না
জগদীপ

3
@ নিক্লাস আমি দেখছি আপনার উদাহরণটি একটি বাস্তব প্রকল্পে পরিণত হয়েছে। প্রকল্পের পরীক্ষামূলক প্রকৃতি সম্পর্কে আপনার সর্বাধিক আপত্তিকৃত মন্তব্য আপডেট করুন। প্রায় ৯০০
কমিটমেন্টের

70

আপনার ওয়েব অ্যাপ্লিকেশনটি এখন ক্লায়েন্টের পুরো ডেস্কটপের ব্যবহার করে একটি 'নেটিভ' স্ক্রিনশট নিতে পারে getUserMedia():

এই উদাহরণটি দেখুন:

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

ক্লায়েন্টকে ক্রোম ব্যবহার করতে হবে (আপাতত) এবং ক্রোম: // ফ্ল্যাগের আওতায় স্ক্রিন ক্যাপচার সহায়তা সক্ষম করতে হবে।


2
আমি কেবল কোনও স্ক্রিনশট নেওয়ার কোনও ডেমো খুঁজে পাচ্ছি না - সবকিছুই স্ক্রিন ভাগ করে নেওয়ার জন্য। এটি চেষ্টা করতে হবে।
jwl

8
@ এক্সমাইট, আপনি স্ক্রিন ক্যাপচার সমর্থন পতাকা টগল করে এটি অনুমতি দেবেন কিনা তা চয়ন করতে পারেন।
ম্যাট সিনক্লেয়ার

19
@ এক্সমাইট দয়া করে এটি মনে করবেন না। ওয়েব ব্রাউজারগুলি অনেক কিছু করতে সক্ষম হওয়া উচিত, তবে দুর্ভাগ্যক্রমে তারা তাদের প্রয়োগের সাথে সামঞ্জস্যপূর্ণ নয়। এটি একেবারে ঠিক আছে, যদি কোনও ব্রাউজারের এমন কার্যকারিতা থাকে, যতক্ষণ না ব্যবহারকারীকে বলা হচ্ছে। আপনার মনোযোগ ব্যতীত কেউ স্ক্রিনশট তৈরি করতে সক্ষম হবে না। তবে অত্যধিক ভয়ের ফলে খারাপ বাস্তবায়ন যেমন ক্লিপবোর্ড এপিআই, যা পুরোপুরি অক্ষম করা হয়েছে তার পরিবর্তে ওয়েবক্যাম, মিক্স, স্ক্রিনশট সামর্থ্য ইত্যাদির জন্য নিশ্চিতকরণ ডায়ালগ তৈরি করে
স্ট্যান


7
@ অগাস্টিনকৌটিনকে অবহেলা Navigator.getUserMedia()করা হয়েছে, তবে এর ঠিক নীচে "... দয়া করে নতুন নেভিগেটর.মিডিয়া ডিভাইস.সেট ইউজারমিডিয়া () " ব্যবহার করুন , এটি কেবলমাত্র একটি নতুন এপিআই দ্বারা প্রতিস্থাপিত হয়েছে।
অবতারণা

37

নিক্লাস যেমন উল্লেখ করেছেন আপনি ব্রাউজারে জেএস ব্যবহার করে স্ক্রিনশট নিতে html2 ক্যানভাস লাইব্রেরি ব্যবহার করতে পারেন । এই লাইব্রেরিটি ব্যবহার করে স্ক্রিনশট নেওয়ার উদাহরণ দিয়ে আমি তার উত্তরটি এই মুহুর্তে বাড়িয়ে দেব:

ইন report()ফাংশন onrenderedতথ্য কোনো URI হিসাবে ইমেজ পাওয়ার পর আপনি এটা ব্যবহারকারীকে দেন এবং তাকে মাউস দ্বারা "বাগ অঞ্চল" আঁকা এবং তারপর সার্ভারে একটি স্ক্রিনশট এবং অঞ্চলের স্থানাঙ্ক পাঠাতে অনুমতি দিতে পারেন।

ইন এই উদাহরণে async/await সংস্করণ তৈরি করা হয়েছিল: চমৎকার সঙ্গে makeScreenshot()ফাংশন

হালনাগাদ

সাধারণ উদাহরণ যা আপনাকে স্ক্রিনশট নিতে, অঞ্চল নির্বাচন করতে, বাগ বর্ণনা করতে এবং POST অনুরোধ ( এখানে jsfiddle ) প্রেরণ করতে দেয় (মূল ফাংশনটি হ'ল report())।


10
আপনি যদি মাইনাস পয়েন্ট দিতে চান, তবে ব্যাখ্যা দিয়ে মন্তব্যটিও ছেড়ে দিন
কামিল কিয়েসজেউস্কি

আমি মনে করি আপনি কেন অবনমিত হচ্ছেন তার কারণটি সম্ভবত এইচটিএমএল 2 ক্যানভাস লাইব্রেরি হ'ল তার গ্রন্থাগার, কোনও সরঞ্জাম নয় যা তিনি কেবল নির্দেশ করেছিলেন।
zfrisch

আপনি পোস্ট-প্রসেসিং প্রভাবগুলি (ব্লার ফিল্টার হিসাবে) ক্যাপচার করতে না চাইলে এটি ঠিক আছে।
vintproykt

সীমাবদ্ধতা স্ক্রিপ্ট ব্যবহার করে এমন সমস্ত চিত্রের প্রক্সিটির সহায়তা ছাড়াই সেগুলি পড়তে সক্ষম হওয়ার জন্য একই উত্সের অধীনে থাকা দরকার। একইভাবে, যদি পৃষ্ঠায় অন্যান্য ক্যানভাস উপাদান থাকে, যা ক্রস-অরিজিন সামগ্রীতে কলঙ্কিত হয়েছে তবে এগুলি নোংরা হয়ে যাবে এবং এইচটিএমএল 2 ক্যানভাস দ্বারা আর পঠনযোগ্য নয়।
অরবিন্দ

13

GetDisplayMedia API ব্যবহার করে ক্যানভাস বা জেপিগ ব্লব / অ্যারেবফার হিসাবে স্ক্রিনশট পান :

// 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 টি উত্সাহ ছিল, এটি সত্যই সহায়ক হিসাবে প্রমাণিত হয়েছিল!
জে দধনিয়া 22

দয়া করে এটি কিভাবে কাজ করে? আপনি কি আমার মতো নবাগতদের জন্য একটি ডেমো সরবরাহ করতে পারেন? Thx
kabrice

@ কেব্রাইস আমি একটি ডেমো যুক্ত করেছি। কেবলমাত্র Chrome কনসোলে কোডটি রেখে দিন। আপনার যদি পুরানো ব্রাউজারগুলির সমর্থন প্রয়োজন হয় তবে ব্যবহার করুন: babeljs.io/en/repl
নিকোলে মাখোনিন

7

এখানে ব্যবহার করে একটি উদাহরণ পান: 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}`));

স্ক্রিন ক্যাপচার এপিআই ডক্সও যাচাইয়ের জন্য মূল্যবান ।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.