Recently, I got a job to download html as an image. Even though it was easy to find related libraries, it was kind of tricky to take a full capture of an element that has a scroll.
I will use my landing page in this post. (GITHUB and DEMO)
First of all, download the packages.
npm i downloadjs html2canvas
or
yarn add downloadjs html2canvas
And if you're using typescript, you must download type package.
npm i -D @types/downloadjs
or
yarn add -D @types/downloadjs
html2canvas: Drawing HTML Element in Canvas
downloadjs: Download Library
Whole HTML
I added a list item in Navbar.
I will implement the download feature on the click event of the list item.
import downloadjs from 'downloadjs';
import html2canvas from 'html2canvas';
// ...
const handleCaptureClick = async () => {
const canvas = await html2canvas(document.body);
const dataURL = canvas.toDataURL('image/png');
downloadjs(dataURL, 'download.png', 'image/png');
};
// ...
<li>
<a href="#" onClick={handleCaptureClick}>
Capture
</a>
</li>
That's it.
Now, click the capture text and you will get that image.
It's very simple.
Specific Element
What if you want to capture a specific element?
There is nothing too difficult, just change the target document.body
to an element you want.
Let's say, we're going to capture Pricing Table section.
[Pricing Table]
// ...
const Prices: React.FC = () => {
// I added the class name 'pricing-table' to get the table element easily.
return (
<div className={cx('prices-section') + ' pricing-table'}>
<SectionTitle>Pricing Table</SectionTitle>
<div className={cx('plans')}>
{plans.map((plan, planIdx) => (
<PlanCard
key={planIdx}
href={plan.href}
title={plan.title}
price={plan.price}
perDate={plan.perDate}
features={plan.features}
/>
))}
</div>
</div>
);
};
[Navbar]
// ...
const handleCaptureClick = async () => {
const pricingTableElmt =
document.querySelector<HTMLElement>('.pricing-table');
if (!pricingTableElmt) return;
const canvas = await html2canvas(pricingTableElmt);
const dataURL = canvas.toDataURL('image/png');
downloadjs(dataURL, 'download.png', 'image/png');
};
// ...
I just replaced document.body
to document.querySelector('pricing-table)
.
It worked well.
Specific Element (Scrolling Capture)
How about capturing an element that has a scroll?
I changed height
of Pricing Table to 200px
.
The scroll appears.
How does it work with the previous code?
The image is cropped.
But this is how it actually works because that's what we can see at this moment.
For capturing a whole element without a scroll, I use some tricks.
const handleCaptureClick = async () => {
const pricingTableElmt =
document.querySelector<HTMLElement>('.pricing-table');
if (!pricingTableElmt) return;
const copiedPricingTableElmt = pricingTableElmt.cloneNode(
true
) as HTMLElement;
copiedPricingTableElmt.style.position = 'fixed';
copiedPricingTableElmt.style.right = '100%';
copiedPricingTableElmt.style.height = 'auto';
document.body.append(copiedPricingTableElmt);
const canvas = await html2canvas(copiedPricingTableElmt);
copiedPricingTableElmt.remove();
const dataURL = canvas.toDataURL('image/png');
downloadjs(dataURL, 'download.png', 'image/png');
};
I cloned the element then set position
to fixed
and right
to 100%
.
Now, you won't be able to see it. Even if it is appended to body
.
Then I adjusted the size. For this case, it's enough to change height
to auto
.
Don't forget to remove the copied element after calling html2canvas
.
Here, you see the final result.
I hope this will be helpful for someone.
Happy Coding!