Intro
I work at a startup company that develops several web applications. The app's code is older than my developer experience. If it is working, I rarely touch it. π (Of course, I will change it if necessary.)
One of the apps I am developing has a function to capture a screen and save it as a jpeg. When I got a new task, which was implementing a table to the component, the style collapsed. π I need to figure out a way to keep it in style.
The original code uses html2canvas
to make screenshots. As I said earlier, if it works, there is no need to change it. π To begin with, I start searching how to capture a table with html2canvas
.
TL;DR
html-to-image
The Problem
This is a simple code for capturing an element using html2canvas
and file-saver
.
import { saveAs } from "file-saver";
import html2canvas from "html2canvas";
const element = document.querySelector("#capture") as HTMLElement;
html2canvas(element, {
scale: 1, // default is 2
}).then((canvas) => {
saveAs(canvas.toDataURL("image/jpeg"), "capture.jpeg");
});
This works fine for most of the elements but not for the table.
This is what we get. The outside border width is 1px, but the inside borders are 2px.
You might say... "Make the outer border 2px to match the inside". Of course, that's a solution too, but this wasn't my personal project. I have a design for it and I do not have the power to change the design... π«
* I found some of the stack overflow talks about border-collapse: collapse;
. This only eliminate nested border look. So this wasn't my solution.
Delete Overlapping Borders
One common solution I found on the Internet is reducing overlaps.
Most of the browsers automatically reduce overlapping borders. Therefore, when I see my code on a browser, there are no double borders. (Open the developer tool and check the cell styles. It will show that only the left and bottom borders are active even though I added borders for all sides)
But the capture won't reduce automatically. So... need to style it manually.
I added a top border for thead
, and a left border and a bottom border for cells. And a right border for the last cell. (I think if the border does not overlap, anything is fine... but if I am wrong, please tell me π)
So this is the final result, all borders are the same width π
Complex Table
It worked fine until I encountered merged cells and striped rows. When I use html2canvas
, the text in merged cells disappears. Additionally, the borders of the merged cells are not displaying correctly... π (I didn't remove overlapping borders for this example)
Use dom-to-image
I made the decision to search for a new library instead of modifying the styles (don't ask me why π«£). While researching, I came across dom-to-image
. Although it hasn't updated since 2017, but.... I decided to give it a try.
import { saveAs } from "file-saver";
import domtoimage from "dom-to-image";
let element = document.getElementById("capture") as HTMLElement;
domtoimage
.toBlob(element, {
width: element.clientWidth,
height: element.clientHeight,
})
.then((blob: Blob) => saveAs(blob, "capture.jpeg"))
.catch((error: Error) => {
console.error("Fail to save", error);
});
And you know what? It works perfectly. π No overlapped borders or disappearing text. π₯³
After a brief moment of excitement, the project manager is hesitant to use a library that hasn't been updated in a long time. I get it, but... but... ππ« π
Use dom-to-image-more
The project manager suggested using dom-to-image-more which is a fork of the dom-to-image with some important fixes. As of writing this article, it seems that it is still being updated.
Just change the import and reuse other part.
import { saveAs } from "file-saver";
import domtoimage from "dom-to-image-more";
let element = document.getElementById("capture") as HTMLElement;
domtoimage
.toBlob(element)
.then((blob: Blob) => saveAs(blob, "capture.jpeg"));
And result is...
What...π«’ I did not expect this result.
The text inside the cell did not disappear, but the border and spacing were changed. I might be able to set print styles for the element. I asked my project manager if he wanted me to change the style and make it work, but he said no. Therefore, we decided to skip using this library.
Use html-to-image
Luckily I found another library through a medium post by Code to Coin.
In the article, he was comparing the performance time of the 2 libraries.
The average difference in performance was 86.3985 ms. This means that on average,
html-to-image
was almost 71 times faster thanhtml2canvas
!
I was having time issues as well, so if this works fine, I might replace all html2canvas laterπ€.
import { saveAs } from "file-saver";
import * as htmlToImage from "html-to-image";
let element = document.getElementById("capture") as HTMLElement;
htmlToImage
.toBlob(element, {
canvasWidth: element.clientWidth / 2,
canvasHeight: element.clientHeight / 2,
})
.then((blob: Blob) => saveAs(blob, "capture.jpeg"));
I use toBlob
and added canvasWidth
and canvasHeight
to match with other results. (There is a toPng
, toJpeg
, toSvg
functions available)
And the result is this π
It looks fine. There is no difference from the dom-to-image
result.
The last release was a year ago, but my manager is okay with it, so finally I could finish my task. π
Wrap up
I ended up replace html2canvas
with html-to-image
so that I don't have to change existing table styles. However, if it's not a complex table, I think I'd reduce the overlapping borders.
If I had more time to experiment with styles, I might have found another way, or maybe I could fork the library and do something? If you know a better way or a different library, please let me know. π€©
* Find the code for this article on GitHub.
References