web-dev-qa-db-fra.com

Utilisation de HTML5 / Canvas / JavaScript pour effectuer des captures d'écran dans le navigateur

"Signaler un bogue" ou "Outil de commentaires" de Google vous permet de sélectionner une zone de la fenêtre de votre navigateur pour créer une capture d'écran soumise avec vos commentaires sur un bogue.

Google Feedback Tool Screenshot Capture d'écran de Jason Small, posté dans un question dupliquée .

Comment font-ils cela? L'API de commentaires JavaScript de Google est chargée à partir de ici et leur vue d'ensemble du module de commentaires illustrera la fonctionnalité de capture d'écran.

870
joelvh

JavaScript peut lire le DOM et en donner une représentation assez précise avec canvas. J'ai travaillé sur un script qui convertit le HTML en image canvas. Nous avons décidé aujourd'hui de le mettre en œuvre en envoyant des retours comme vous l'avez décrit.

Le script vous permet de créer des formulaires de commentaires comprenant une capture d'écran, créée sur le navigateur du client, avec le formulaire. La capture d'écran est basée sur le DOM et, en tant que telle, elle peut ne pas correspondre à la représentation réelle à 100%, car elle ne crée pas une capture d'écran mais génère la capture d'écran en fonction des informations disponibles sur la page.

Il ne nécessite aucun rendu du serveur , car toute l'image est créée sur le navigateur du client. Le script HTML2Canvas lui-même est encore dans un état très expérimental, car il n'analyse pas autant d'attributs CSS3 que je le souhaiterais et ne prend pas en charge le chargement d'images CORS, même si un proxy était disponible.

Compatibilité des navigateurs encore assez limitée (non pas parce qu'il est impossible d'en prendre plus, mais pas encore eu le temps de le faire davantage).

Pour plus d'informations, regardez les exemples ici:

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

edit Le script html2canvas est maintenant disponible séparément ici et quelques exemples ici .

edit 2 Une autre confirmation que Google utilise une méthode très similaire (en fait, sur la base de la documentation, la seule différence majeure est leur méthode asynchrone de déplacement/dessin ) peut être trouvé dans cette présentation d'Elliott Sprehn de l'équipe Google Feedback: http://www.elliottsprehn.com/preso/fluentconf/

1100
Niklas

Votre application Web peut maintenant prendre une capture d'écran 'native' de l'intégralité du bureau du client en utilisant getUserMedia():

Regardez cet exemple:

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

Le client devra utiliser chrome (pour l'instant) et activer le support de capture d'écran sous chrome: // flags.

64
Matt Sinclair

Comme Niklas a mentionné , vous pouvez utiliser la bibliothèque html2canvas pour prendre une capture d'écran en utilisant JS dans le navigateur. J'étendrai sa réponse sur ce point en donnant un exemple de prise de capture d'écran utilisant cette bibliothèque:

function report() {
  let region = document.querySelector("body"); // whole screen
  html2canvas(region, {
    onrendered: function(canvas) {
      let pngUrl = canvas.toDataURL(); // png in dataURL format
      let img = document.querySelector(".screen");
      img.src = pngUrl; 

      // here you can allow user to set bug-region
      // and send it with 'pngUrl' to server
    },
  });
}
.container {
  margin-top: 10px;
  border: solid 1px black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js"></script>
<div>Screenshot tester</div>
<button onclick="report()">Take screenshot</button>

<div class="container">
  <img width="75%" class="screen">
</div>

Dans report(), dans onrendered après avoir obtenu l'image en tant qu'URI de données, vous pouvez la montrer à l'utilisateur et lui permettre de dessiner une "région de bogue" à la souris, puis d'envoyer une capture d'écran et les coordonnées de la région au serveur.

Dans cet exempleasync/await la version a été faite: avec Nice makeScreenshot() fonction .

MISE À JOUR

Exemple simple qui vous permet de prendre une capture d'écran, de sélectionner une région, de décrire un bogue et d'envoyer la demande POST ( ici jsfiddle ) (la fonction principale est report()).

async function report() {
    let screenshot = await makeScreenshot(); // png dataUrl
    let img = q(".screen");
    img.src = screenshot; 
    
    let c = q(".bug-container");
    c.classList.remove('hide')
        
    let box = await getBox();    
    c.classList.add('hide');

    send(screenshot,box); // sed post request  with bug image, region and description
    alert('To see POST requset with image go to: chrome console > network tab');
}

// ----- Helper functions

let q = s => document.querySelector(s); // query selector helper
window.report = report; // bind report be visible in fiddle html

async function  makeScreenshot(selector="body") 
{
  return new Promise((resolve, reject) => {  
    let node = document.querySelector(selector);
    
    html2canvas(node, { onrendered: (canvas) => {
        let pngUrl = canvas.toDataURL();      
        resolve(pngUrl);
    }});  
  });
}

async function getBox(box) {
  return new Promise((resolve, reject) => {
     let b = q(".bug");
     let r = q(".region");
     let scr = q(".screen");
     let send = q(".send");
     let start=0;
     let sx,sy,ex,ey=-1;
     r.style.width=0;
     r.style.height=0;
     
     let drawBox= () => {
         r.style.left   = (ex > 0 ? sx : sx+ex ) +'px'; 
         r.style.top    = (ey > 0 ? sy : sy+ey) +'px';
         r.style.width  = Math.abs(ex) +'px';
         r.style.height = Math.abs(ey) +'px'; 
     }
     
     
     
     //console.log({b,r, scr});
     b.addEventListener("click", e=>{
       if(start==0) {
         sx=e.pageX;
         sy=e.pageY;
         ex=0;
         ey=0;
         drawBox();
       }
       start=(start+1)%3;               
     });
     
     b.addEventListener("mousemove", e=>{
       //console.log(e)
       if(start==1) {
           ex=e.pageX-sx;
           ey=e.pageY-sy
           drawBox(); 
       }
     });
     
     send.addEventListener("click", e=>{
       start=0;
       let a=100/75 //zoom out img 75%       
       resolve({
          x:Math.floor(((ex > 0 ? sx : sx+ex )-scr.offsetLeft)*a),
          y:Math.floor(((ey > 0 ? sy : sy+ey )-b.offsetTop)*a),
          width:Math.floor(Math.abs(ex)*a),
          height:Math.floor(Math.abs(ex)*a),
          desc: q('.bug-desc').value
          });
          
     });
  });
}

function send(image,box) {

    let formData = new FormData();
    let req = new XMLHttpRequest();
    
    formData.append("box", JSON.stringify(box)); 
    formData.append("screenshot", image);     
    
    req.open("POST", '/upload/screenshot');
    req.send(formData);
}
.bug-container { background: rgb(255,0,0,0.1); margin-top:20px; text-align: center; }
.send { border-radius:5px; padding:10px; background: green; cursor: pointer; }
.region { position: absolute; background: rgba(255,0,0,0.4); }
.example { height: 100px; background: yellow; }
.bug { margin-top: 10px; cursor: crosshair; }
.hide { display: none; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js"></script>
<body>
<div>Screenshot tester</div>
<button onclick="report()">Report bug</button>

<div class="example">Lorem ipsum</div>

<div class="bug-container hide">
  <div>Select bug region</div>
  <div class="bug">    
    <img width="75%" class="screen" >
    <div class="region"></div> 
  </div>
  <div>
    <textarea class="bug-desc">Describe bug here...</textarea>
  </div>
  <div class="send">SEND BUG</div>
</div>

</body>
29
Kamil Kiełczewski

Voici un exemple utilisant: 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}`));

La API de capture d'écran docs mérite également une visite.

0
JSON C11