Heim  >  Artikel  >  Web-Frontend  >  Beispiel für die gemeinsame Nutzung von JavaScript mit Implementierung der Screenshot-Funktion

Beispiel für die gemeinsame Nutzung von JavaScript mit Implementierung der Screenshot-Funktion

黄舟
黄舟Original
2018-05-10 16:13:412450Durchsuche

Ich finde die Idee, JS zum Erstellen von Screenshots zu verwenden, sehr lächerlich. Erstens verfügt JS nicht über die Berechtigung, die Screenshot-Funktion des Betriebssystems aufzurufen Nach einigem Hin und Her habe ich einige Ideen zum Implementierungscode der js-Screenshot-Funktion erhalten.

Ich habe kürzlich an der Entwicklung teilgenommen Da ich auf verwandten Seiten von NetEase Hearthstone Box bin und an einer Seite zum Teilen von Kartengruppen arbeite (Adresse: Hearthstone Box Kartengruppen-Sharing), besteht die Anforderung: Benutzer können diese Kartengruppe in Form von Bildern mit Freunden teilen. Der ursprüngliche Ansatz bestand darin, die Seite mithilfe des Servers in ein Bild umzuwandeln und dann die Bildadresse an das Frontend zurückzugeben. Nun, das ist ziemlich gut, und der Server kann die konvertierten Bilder auch zwischenspeichern, sodass die nächste Anfrage die Bildadresse direkt zurückgeben kann. Daran ist grundsätzlich nichts auszusetzen. Es tritt jedoch gelegentlich ein Problem auf, das nicht mit dem Seiteninhalt übereinstimmt, und manchmal fehlen Inhalte. Die PM-Schwester ist sehr unzufrieden und sagt, dass dieses Problem gelöst werden muss. Wie auch immer, die Schnittstelle zum Konvertieren von Seiten in Bilder läuft im Hintergrund, das geht mich nichts an! Gerade als ich insgeheim glücklich war, passierte etwas Tragisches. Ein Kollege im Hintergrund sagte, dass die Serverkonvertierung manchmal instabil sei, weil einige Inhalte auf der Seite asynchron geladen würden Asynchron gerenderte Inhalte können nicht abgefangen werden. Um es ganz klar auszudrücken: Er hat keine Möglichkeit, dieses Problem zu lösen. Wer hat dem Frontend gesagt, dass es asynchrones Rendering verwenden soll? Schließlich bat mich der Leiter, zu versuchen, JS direkt zum Erstellen von Screenshots zu verwenden, was nicht nur die Belastung des Servers verringern, sondern auch den oben genannten Fehler beheben würde.

Zuerst fand ich die Idee, JS zum Erstellen von Screenshots zu verwenden, sehr lächerlich (Schuld daran ist meine Unwissenheit, das Frontend hat sich in den letzten Jahren zu schnell entwickelt): Erstens tut es JS Sie haben keine Berechtigung, die Screenshot-Funktion des Betriebssystems aufzurufen, und zweitens stellt der Browser (BOM) auch keine entsprechende Screenshot-Schnittstelle zur Verfügung. Was soll ich tun? Was soll ich tun? Bei Fragen wenden Sie sich bitte an Google. Dann habe ich nach „JS html to png“ gesucht und dann Folgendes gefunden: „render-html-to-an-image“. Mir kommen langsam einige Ideen, in der Antwort wurde erwähnt, dass man DOM in Canvas konvertieren kann, nicht wahr? Es ist wieder Canvas! Ich konnte nicht anders, als aufgeregt zu sein. Es war wirklich schwierig, einen Ausweg aus den Bergen und Flüssen zu finden, aber es gab immer noch eine glänzende Zukunft in einem anderen Dorf! Dann habe ich nach „dom to canvas“ gesucht und bin auf das bekannte MDN-Dokument „Drawing_DOM_objects_into_a_canvas“ gestoßen. Dann begann ich, das Dokument ernsthaft zu lesen. Wie am Anfang des Dokuments erwähnt, können Sie DOM nicht in Canvas konvertieren, aber Sie können DOM in SVG konvertieren und dann das SVG in den Canvas zeichnen. Manche Leute fragen sich vielleicht: Warum müssen wir zuerst dom in SVG konvertieren? Dies kann daran liegen, dass SVG die XML-Darstellung verwendet und die Struktur mit der von DOM übereinstimmt.
Das Folgende ist die Schritt-für-Schritt-Anleitung aus dem offiziellen Dokument:

1. Der Medientyp von Blob muss „image/svg+xml“ sein

Ein SVG-Element ist erforderlich

3. Fügen Sie ein foreignObject -Element in das SVG-Element

ein. 4. Fügen Sie HTML, das der Spezifikation entspricht, in das ForeignObject-Element

ein. Das Konvertieren von Dom in Canvas ist genauso einfach wie im obigen Schritt. Das Folgende ist eine einfache Demo im Dokument:

<!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>

Kopieren Sie den Code und führen Sie ihn aus. Wow, es ist so cool, dass im Browser zwei Zeilen mit cooler Wortkunst angezeigt werden.

Nun, es stellt sich heraus, dass die Konvertierung von Dom in Canvas so einfach ist? Dann verwende ich document.body.innerHTML , um das gesamte DOM im Textkörper herauszunehmen und in das ForeignObject-Element einzufügen. Ist das nicht in Ordnung und die gesamte Seite wird erfasst?

Die Demo ist nur eine Hello World, aber die Dom-Struktur im eigentlichen Projekt ist viel komplizierter. Beispielsweise entsprechen externe Stylesheets, Bilder und einige Tags möglicherweise nicht der XML Spezifikation (z. B. Fehlendes schließendes Tag usw.). Hier ist ein einfaches Beispiel. .container verwendet keine Inline-Stile, sondern ist im Stil-Tag definiert. Nach der Konvertierung in ein Bild wird der Stil nicht wirksam.

<!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>

Da externe Stile nicht wirksam werden, können wir alle DOM-Elemente über JS durchlaufen und alle Stile über das element.style-Objekt zu Inline-Stilen hinzufügen. Diese Idee hört sich gut an, aber ich kann wirklich nicht die Funktion schreiben, die externe Stile in Inline-Stile umwandelt. Die Nachfrage ist gering und ich habe nicht viel Zeit zum Herumspielen, deshalb möchte ich nach fertigen Bibliotheken suchen. Also ging ich noch einmal zu Google. Glücklicherweise habe ich sofort eine Bibliothek namens html2canvas gefunden. Es ist eine großartige Bibliothek, sehr leistungsfähig, aber sehr einfach zu verwenden. Mit einer so einfachen Methode kann ich einen Screenshot meiner gesamten Seite erstellen:

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);
  });
 }

Es gibt derzeit ein weiteres Problem: Diese Methode fängt standardmäßig die gesamte Seite ab (das heißt, sie verwendet Ihre innerHeight und innerWidth als Grenzen und es wird viel Leerraum vorhanden sein). Es nimmt nur einen kleinen Teil der Seite ein, ich möchte nur den Deckteil. Tatsächlich ist es einfacher zu handhaben, wenn wir die Leinwand bereits haben und sie verarbeiten können. Die allgemeine Idee ist: 1. Konvertieren Sie das oben erhaltene Canvas-Objekt in einen Blob und fügen Sie ihn in ein img-Element ein. Zeichnen Sie dann img.src in die Leinwand. Zu diesem Zeitpunkt kann der Aufruf der Methode canvas.drawImage den gewünschten Inhalt abfangen. Die folgenden zwei Funktionen konvertieren Leinwand in Bild und umgekehrt.

// 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的原理。

Das obige ist der detaillierte Inhalt vonBeispiel für die gemeinsame Nutzung von JavaScript mit Implementierung der Screenshot-Funktion. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn