Recently, I participated in the development of the related page of NetEase Hearthstone Box. I am doing the deck sharing page (address: Hearthstone Box deck sharing), and there is a requirement: users can share this deck with friends in the form of pictures. The initial approach was to use the server to convert the page into an image and then return the image address to the front end. Well, this is also good, and the server can also cache the converted pictures, and you can directly return the image address for the next request. It is in principle free. However, the problem arises. The pictures and page content converted in the background are occasionally inconsistent, and sometimes some content is missing. Sister PM is very upset and said that this problem must be solved. Anyway, the interface for converting the page into a picture is done in the background, which is my business! Just when I was secretly happy, a sad thing happened. A colleague in the background said that because some of the content in the page is loaded asynchronously (for example, the QR code at the bottom is generated through canvas), the server conversion is unstable, and sometimes the asynchronous rendering content cannot be intercepted. To put it bluntly, he has no way to solve this problem. Let’s change the front-end. Who said that the front-end uses asynchronous rendering? Finally, the Leader asked me to try whether I could directly use JS to take screenshots, which can not only relieve the pressure on the server, but also solve the above bugs.
At the beginning, I thought the idea of using JS screenshots was very absurd (I blamed me for being ignorant, the front-end has developed too fast in recent years): First of all, JS does not have permission to call the screenshot function of the operating system, and secondly, the browser (BOM) does not provide relevant screenshot interfaces. What should I do and what should I do? If you have something to do, look for Google. Then I searched: JS html to png, and then I found this: render-html-to-an-image. I started to have a idea. Someone mentioned in the answer that I could convert dom to canvas, yes! Canvas again! I couldn't help but get excited. It was really because the mountains and rivers were full of no way out, and there was another village with dark willows and flowers! Then search for dom to canvas and come to the well-known mdn document Drawing_DOM_objects_into_a_canvas. Then I began to read the document in real life. The document begins with the statement that you cannot convert dom to canvas, but you can convert dom to svg, and then draw svg into canvas. Some people may ask, why do you need to convert dom to svg first? This may be because svg uses xml representation, structure and dom.
The following is the official document step by step tutorial:
The media type must be "image/svg+xml
"
2. Requires an svg element
3. Insert a svg element foreignObject
element
4. Put the specification-compliant html into the foreignObject element
It's that simple to convert dom to canvas, just the above steps. Here is a simple demo given by the document:
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <canvas style="border:2px solid black;" width="200" height="200"> </canvas> <script> var canvas = ('canvas'); var ctx = ('2d'); var data = '<svg xmlns="http:///2000/svg" width="200" height="200">' + '<foreignObject width="100%" height="100%">' + '<div xmlns="http:///1999/xhtml" style="font-size:40px">' + '<em>I</em> like ' + '<span style="color:white; text-shadow:0 0 2px blue;">' + 'cheese</span>' + '</div>' + '</foreignObject>' + '</svg>'; var DOMURL = || || window; var img = new Image(); var svg = new Blob([data], {type: 'image/svg+xml'}); var url = (svg); = function() { (img, 0, 0); (url); } = url; </script> </body> </html>
Copy the code and run it, wow, it’s so cool, two super cool lines of artistic words appear on the browser!
Well, it turns out that it is so easy to convert dom to canvas? Then I'll pass
Take out all the doms in the body and put them in the foreignObject element. Wouldn't it be OK and cut off the entire page?
demo is just a Hello World, but the Dom structure in actual projects is much more complicated than this. For example, external style sheets, pictures are introduced, and some tags may not comply with the XML specification (such as the lack of closed tags, etc.). As a simple example, .container does not use inline styles, but is defined in the style tag. The font is red, and the style does not take effect after being converted into a picture.
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style> .container { color: red; } </style> </head> <body> <div class="container" > Hello World! </div> <canvas style="border:2px solid black;" width=200" height="200"> </canvas> <script> var canvas = ('canvas'); var ctx = ('2d'); var data = '<svg xmlns="http:///2000/svg" width="200" height="200">' + '<foreignObject width="100%" height="100%">' + '<div xmlns="http:///1999/xhtml" style="font-size:40px">' + ('.container').innerHTML + '</div>' + '</foreignObject>' + '</svg>'; var DOMURL = || || window; var img = new Image(); var svg = new Blob([data], {type: 'image/svg+xml'}); var url = (svg); = function() { (img, 0, 0); (url); } = url; </script> </body> </html>
Since the external style does not take effect, we can traverse all dom elements through JS and add all styles to the in-line styles through objects. This idea sounds good, but I really can't write this function that converts external styles into inline styles. The demand is relatively tight, and there is not much time to mess around, so I want to find out if there is any ready-made library. So I went to google again. Fortunately, I found a library called html2canvas all at once. It was a great library. It was very powerful, but the usage was very simple. With such a simple method, I can take a screenshot of my entire page:
function convertHtml2Canvas() { html2canvas(, { allowTaint: false, taintTest: true }).then(function(canvas) { (canvas); }).catch(function(e) { ('error', e); }); }
There is another problem at present, that is, this method is to cut off the entire page by default (that is, it will take your innerHeight and innerWidth as the boundary, and there will be a lot of blank spaces). However, my deck only accounts for a small part of the page, and I only want the deck part. In fact, it's easy to have canvas already, we can deal with it. The general idea is: 1. Convert the canvas object obtained above into a blob and put it in an img element. Then draw it into canvas. Called at this timeThe method can intercept what we want. The following two functions are to convert canvas into image and in turn convert image into canvas.
// Converts canvas to an image function convertCanvasToImage(canvas) { var image = new Image(); = ("image/png", 0.1); return image; } // Converts image to canvas; returns new canvas element function convertImageToCanvas(image, startX, startY, width, height) { var canvas = ("canvas"); = width; = height; ("2d").drawImage(image, startX, startY, width, height, 0, 0, width, height); return canvas; }
Then, change the convertHtml2Canvas we wrote above to the following:
function convertHtml2Canvas() { html2canvas(, { allowTaint: false, taintTest: true }).then(function(canvas) { var img = convertCanvasToImage(canvas); (img); = function() { = null; canvas = convertImageToCanvas(img, 0, 0, 384, 696); = convertCanvasToImage(canvas).src; $(img).css({ display: 'block', position: 'absolute', top: 0, left: 400 + 'px' }); } }).catch(function(e) { ('error', e); }); }
At this time, you can cut off some of the contents of its page. The effect is like the deck sharing test page. The left part of the page is DOM structure, and the right part is an image converted using html2canvas. Looks exactly the same, without any problems.
This is all for the screenshots of JS pages, because I have only just come into contact with them and I have not understood many things properly. I welcome the guidance of all the masters. Later, I will learn more about the source code of html2canvas and further understand the principle of dom to canvas.
Summarize
The above is the implementation code of the JavaScript screenshot function introduced to you by the editor. I hope it will be helpful to you. If you have any questions, please leave me a message and the editor will reply to you in time. Thank you very much for your support for my website!