Maison  >  Article  >  interface Web  >  Exemple de partage de JavaScript implémentant la fonction de capture d'écran

Exemple de partage de JavaScript implémentant la fonction de capture d'écran

黄舟
黄舟original
2018-05-10 16:13:412419parcourir

Je pense que l'idée d'utiliser JS pour prendre des captures d'écran est très ridicule. Tout d'abord, JS n'a pas la permission d'appeler la fonction de capture d'écran du système d'exploitation. Deuxièmement, le navigateur (BOM) ne fournit pas de fonction. interface de capture d'écran pertinente. Après quelques réflexions, j'ai eu quelques idées. Ce qui suit est une brève introduction au code d'implémentation de la fonction de capture d'écran js à travers un exemple de code

J'ai récemment participé au développement de. pages associées de NetEase Hearthstone Box et je travaille sur une page de partage de groupe de cartes (Adresse : Partage de groupe de cartes Hearthstone Box), il y a une exigence : les utilisateurs peuvent partager ce groupe de cartes avec des amis sous forme d'images. L'approche originale consistait à utiliser le serveur pour convertir la page en image, puis à renvoyer l'adresse de l'image au front-end. Eh bien, c'est plutôt bien, et le serveur peut également mettre en cache les images converties, et la requête suivante peut renvoyer directement l'adresse de l'image. Il n’y a rien de mal à cela en principe. Cependant, un problème survient : les images converties en arrière-plan sont parfois incohérentes avec le contenu de la page, et parfois du contenu manque. La sœur PM est très mécontente et dit que ce problème doit être résolu. Quoi qu’il en soit, l’interface de conversion des pages en images se fait en arrière-plan, ce ne sont pas mes affaires ! Juste au moment où j'étais secrètement heureux, quelque chose de tragique s'est produit. Un collègue en arrière-plan a déclaré que, parce que certains contenus de la page sont chargés de manière asynchrone (par exemple, le code QR en bas est généré via Canvas), la conversion du serveur est parfois instable. Le contenu rendu de manière asynchrone ne peut pas être intercepté. Pour le dire franchement, il n'a aucun moyen de résoudre ce problème. Changeons le front-end. Qui a dit au front-end d'utiliser le rendu asynchrone ? Enfin, le leader m'a demandé d'essayer si je pouvais utiliser directement JS pour prendre des captures d'écran, ce qui non seulement réduirait la pression sur le serveur, mais résoudrait également le bug ci-dessus.

Au début, je trouvais l'idée d'utiliser JS pour faire des captures d'écran très ridicule (c'est la faute de mon ignorance, le front-end s'est développé trop vite ces dernières années) : Tout d'abord, JS le fait ne pas avoir l'autorisation d'appeler la fonction de capture d'écran du système d'exploitation, et deuxièmement, le navigateur (BOM) ne fournit pas non plus d'interface de capture d'écran pertinente. Que dois-je faire ? Si vous avez des questions, veuillez contacter Google. Ensuite, j'ai recherché : JS html vers png, puis j'ai trouvé ceci : render-html-to-an-image. Je commence à avoir quelques idées. Quelqu'un dans la réponse a mentionné que vous pouvez convertir le DOM en canevas, hein ! C'est encore Canvas ! Je ne pouvais pas m'empêcher d'être excité. C'était vraiment difficile de trouver un moyen de sortir des montagnes et des rivières, mais il y avait encore un brillant avenir dans un autre village ! Ensuite, j'ai cherché dom to canvas et suis arrivé au document mdn bien connu Drawing_DOM_objects_into_a_canvas. Puis j'ai commencé à lire le document sérieusement. Comme mentionné au début du document, vous ne pouvez pas convertir le DOM en canevas, mais vous pouvez convertir le DOM en SVG, puis dessiner le SVG dans le canevas. Certaines personnes peuvent se demander pourquoi devons-nous d’abord convertir dom en svg ? Cela peut être dû au fait que SVG utilise une représentation XML et que la structure est cohérente avec DOM.
Ce qui suit est le tutoriel étape par étape du document officiel :

1. Le type de média de Blob doit être "image/svg+xml"

2. 🎜>

3. Insérez un élément

dans l'élément svg foreignObject

4. Mettez du HTML conforme à la spécification dans l'élément ForeignObject

La conversion de dom en canevas est aussi simple. comme l'étape ci-dessus. Ce qui suit est une démo simple donnée dans le document :

<!doctype html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Document</title>
</head>
<body>
<canvas id="canvas" style="border:2px solid black;" width="200" height="200">
</canvas>
<script>
 var canvas = document.getElementById(&#39;canvas&#39;);
 var ctx = canvas.getContext(&#39;2d&#39;);
 var data = &#39;<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">&#39; +
  &#39;<foreignObject width="100%" height="100%">&#39; +
  &#39;<p xmlns="http://www.w3.org/1999/xhtml" style="font-size:40px">&#39; +
  &#39;<em>I</em> like &#39; +
  &#39;<span style="color:white; text-shadow:0 0 2px blue;">&#39; +
  &#39;cheese</span>&#39; +
  &#39;</p>&#39; +
  &#39;</foreignObject>&#39; +
  &#39;</svg>&#39;;
 var DOMURL = window.URL || window.webkitURL || window;
 var img = new Image();
 var svg = new Blob([data], {type: &#39;image/svg+xml&#39;});
 var url = DOMURL.createObjectURL(svg);
 img.onload = function() {
  ctx.drawImage(img, 0, 0);
  DOMURL.revokeObjectURL(url);
 }
 img.src = url;
</script>
</body>
</html>
Copiez le code et exécutez-le, c'est trop cool. Deux lignes d'art de mots sympas apparaissent sur le navigateur !

Eh bien, il s'avère que convertir un dom en toile est si simple ? Ensuite, j'utilise

pour supprimer tout le DOM du corps et le mettre dans l'élément ForeignObject, n'est-ce pas OK et la page entière est capturée ? document.body.innerHTML

La démo n'est qu'un Hello World, mais la structure Dom dans le projet réel est beaucoup plus compliquée que cela. Par exemple, les feuilles de style externes, les images et certaines balises peuvent ne pas être conformes au XML. spécification (telle que balise de fermeture manquante, etc.). Voici un exemple simple. .container n'utilise pas de styles en ligne, mais est défini dans la balise de style. Une fois convertie en image, le style ne prend pas effet.

<!doctype html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Document</title>
 <style>
  .container {
   color: red;
  }
 </style>
</head>
<body>
<p class="container" >
 Hello World!
</p>
<canvas id="canvas" style="border:2px solid black;" width=200" height="200">
</canvas>
<script>
 var canvas = document.getElementById(&#39;canvas&#39;);
 var ctx = canvas.getContext(&#39;2d&#39;);
 var data = &#39;<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">&#39; +
  &#39;<foreignObject width="100%" height="100%">&#39; +
  &#39;<p xmlns="http://www.w3.org/1999/xhtml" style="font-size:40px">&#39; +
  document.querySelector(&#39;.container&#39;).innerHTML +
  &#39;</p>&#39; +
  &#39;</foreignObject>&#39; +
  &#39;</svg>&#39;;
 var DOMURL = window.URL || window.webkitURL || window;
 var img = new Image();
 var svg = new Blob([data], {type: &#39;image/svg+xml&#39;});
 var url = DOMURL.createObjectURL(svg);
 img.onload = function() {
  ctx.drawImage(img, 0, 0);
  DOMURL.revokeObjectURL(url);
 }
 img.src = url;
</script>
</body>
</html>
Étant donné que les styles externes ne prennent pas effet, nous pouvons parcourir tous les éléments DOM via JS et ajouter tous les styles aux styles en ligne via l'objet element.style. Cette idée semble bonne, mais je ne peux vraiment pas écrire la fonction qui convertit les styles externes en styles en ligne. La demande est forte et je n'ai pas beaucoup de temps pour m'amuser, je souhaite donc rechercher des bibliothèques prêtes à l'emploi. Je suis donc retourné sur Google. Heureusement, j'ai tout de suite trouvé une bibliothèque appelée html2canvas. C'est une super bibliothèque, très puissante, mais très simple à utiliser. Avec une méthode aussi simple, je peux faire une capture d'écran de ma page entière :

Il y a toujours un problème, c'est-à-dire que cette méthode intercepte la page entière par défaut (c'est-à-dire qu'avec votre innerHeight et innerWidth comme limites, il y aura beaucoup d'espace blanc), mais mon deck est juste Cela prend un petite partie de la page, je veux seulement la partie deck. En fait, c'est plus facile à manipuler si on a déjà la toile, on peut la traiter. L'idée générale est la suivante : 1. Convertir l'objet canvas obtenu ci-dessus en un Blob et le mettre dans un élément img. Dessinez ensuite img.src dans le canevas. À ce stade, l’appel de la méthode
function convertHtml2Canvas() {
  html2canvas(document.body, {
   allowTaint: false,
   taintTest: true
  }).then(function(canvas) {
   document.body.appendChild(canvas);
  }).catch(function(e) {
   console.error(&#39;error&#39;, e);
  });
 }
peut intercepter le contenu souhaité. Les deux fonctions suivantes convertissent le canevas en image et vice versa.

// Converts canvas to an image
 function convertCanvasToImage(canvas) {
  var image = new Image();
  image.src = canvas.toDataURL("image/png", 0.1);
  return image;
 }
 // Converts image to canvas; returns new canvas element
 function convertImageToCanvas(image, startX, startY, width, height) {
  var canvas = document.createElement("canvas");
  canvas.width = width;
  canvas.height = height;
  canvas.getContext("2d").drawImage(image, startX, startY, width, height, 0, 0, width, height);
  return canvas;
 }

然后,再把我们上面的写的 convertHtml2Canvas 改成下面的:

function convertHtml2Canvas() {
  html2canvas(document.body, {
   allowTaint: false,
   taintTest: true
  }).then(function(canvas) {
   var img = convertCanvasToImage(canvas);
   document.body.appendChild(img);
   img.onload = function() {
    img.onload = null;
    canvas = convertImageToCanvas(img, 0, 0, 384, 696);
    img.src = convertCanvasToImage(canvas).src;
    $(img).css({
     display: &#39;block&#39;,
     position: &#39;absolute&#39;,
     top: 0,
     left: 400 + &#39;px&#39;
    });
   }
  }).catch(function(e) {
   console.error(&#39;error&#39;, e);
  });
 }

这时候就可以把它的页面的某部分内容进行截取下来了。效果如卡组分享测试页面。页面左边部分是DOM结构的,右边部分是则是使用html2canvas转换出来的图片。长得一模一样,毫无毛病哈。

关于JS页面截图的就写到这里啦,因为也只刚刚接触,很多东西也理解得不到位,欢迎各大神指点。后面会深入学习一下html2canvas的源码,进一步理解dom to canvas的原理。

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn