DEV Community

Masui Masanori
Masui Masanori

Posted on

3 1

Save HTMLCanvasElements as images

Intro

This time, I want to save charts what are drawn by Chart.js as images.

To do this, I will try saving HTMLCanvasElement as an image.

In this time, I just use samples to use Chart.js.
I will try Chart.js next time.

Environments

  • Node.js ver.15.0.0

package.json

{
    "browserslist": [
        "last 2 version"
    ],
    "scripts": {
        "css": "npx postcss postcss -c postcss.config.js -d wwwroot/css -w"
    },
    "dependencies": {
        "autoprefixer": "^10.0.1",
        "chart.js": "^2.9.4",
        "postcss": "^8.1.2",
        "postcss-cli": "^8.1.0",
        "postcss-import": "^13.0.0",
        "precss": "^4.0.0",
        "ts-loader": "^8.0.6",
        "tsc": "^1.20150623.0",
        "typescript": "^4.0.3",
        "webpack": "^5.2.0",
        "webpack-cli": "^4.1.0"
    },
    "devDependencies": {
        "@types/chart.js": "^2.9.27",
        "whatwg-fetch": "^3.4.1"
    }
}
Enter fullscreen mode Exit fullscreen mode

Base projects

ChartPage.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <link rel="stylesheet" type="text/css" href="../css/chart_page.css" >
    </head>
    <body>
        <div id="outside">
            <h1>Chart</h1>
            <div class="chart_area">
                <canvas id="chart_1" class="chart_canvas"></canvas>
            </div>
        </div>
        <button onclick="Page.save()">Save</button>
        <script src="../js/chartPage.bundle.js"></script>
        <script>
            (function() {
                Page.init();
            })();
        </script>
    </body>
</html>
Enter fullscreen mode Exit fullscreen mode

chart_page.css

.chart_area{
    background-color: white;
    border: solid black 1px;
    display: flex;
    align-items: center;
    justify-content: center;
    height: 300px;
    width: 300px;
}
.chart_canvas {
}
Enter fullscreen mode Exit fullscreen mode

Draw chart samples

This time, I use the chart sample in Chart.js document.

chart.page.ts

import { SampleDrawer } from "./charts/sample-drawer";

export function init() {
    const target = document.getElementById('chart_1');
    if (target == null)
    {
        return;
    }
    const drawer = new SampleDrawer();
    drawer.draw(target as HTMLCanvasElement);
}
Enter fullscreen mode Exit fullscreen mode

sample-drawer.ts

import Chart from "chart.js";

export class SampleDrawer {
    public draw(canvas: HTMLCanvasElement) {
        const context = canvas.getContext('2d');
        if (context == null) {
            console.error('failed getting context');
            return;
        }
        this.drawSample(canvas);
    }
    private drawSample(canvas: HTMLCanvasElement) {
        const chart = new Chart(canvas, {
            // as same as the sample script at "Creating a Chart"
        });
    }
}
Enter fullscreen mode Exit fullscreen mode

Result

Alt Text

The chart size comes from the parent element's(<div class="chart_area">) width or height.
Though I change the canvas size, background-color, and etc. before instantiating the chart instance, they will be ignored.

Save as a image

The HTMLCanvasElement can generate Blob or Base64 string and I can use them to create images.
If I save image by a server application, I prefer using "toDataURL" and send Base64 string to the server.

But this time, I use "toBlob()" to download by TypeScript.

chart.page.ts

...
export function save() {
    const target = document.getElementById('chart_1');
    if (target == null)
    {
        console.error('chart_1 was not found');
        return;
    }
    const targetCanvas = target as HTMLCanvasElement;
    targetCanvas.toBlob((blob) => downloadImageBlob(blob), 'image/jpeg', 1);
}
function downloadImageBlob(blob: Blob|null) {
    if(blob == null){
        console.error('Failed getting image blob');
        return;
    }
    if (navigator.msSaveBlob == null) {
        if (navigator.msSaveBlob == null) {
        const linkElement = document.getElementById('download_link') as HTMLAnchorElement;
        linkElement.href = window.URL.createObjectURL(blob);
        linkElement.download = 'sample_file.jpg';
        linkElement.click();
        return;
    }
    // for IE
    window.navigator.msSaveBlob(blob, 'sample_file.jpg');
}
Enter fullscreen mode Exit fullscreen mode

ChartPage.html

...
        <button onclick="Page.save()">Save</button>
        <a id="download_link"></a>
        <script src="../js/polyfill/toBlob.js"></script>
        <script src="../js/chartPage.bundle.js"></script>
...
Enter fullscreen mode Exit fullscreen mode

For downloading created image, I add an AnchorElement.

And For IE, I add a polyfill file.

Result

Alt Text

Where did the background color come from?
It's because the chart didn't have background colors, and I saved as Jpeg image(no transparency).

So I have to set background color.
But it ignores canvas background color.

Set background color

To set background color, I use plugins of Chart.js.

sample-drawer.ts

    public draw(canvas: HTMLCanvasElement) {
        const context = canvas.getContext('2d');
        if (context == null) {
            return;
        }
        this.drawBackground();
        this.drawSample(canvas);
    }
    private drawBackground() {
        Chart.pluginService.register({
            beforeDraw: c => {
                const ctx = c.ctx;
                if (ctx == null){
                    return;
                }
                const canvas = c.canvas;
                if (canvas == null) {
                    return;
                }
                ctx.fillStyle = 'rgba(255, 255, 255, 1)';                
                ctx.fillRect(0, 0, canvas.width, canvas.height);
            }
        });
    }
...
Enter fullscreen mode Exit fullscreen mode

Result

Alt Text

Hide chart and save image

Can I only want to show an image what is created from a chart dynamically and hide the chart?

display:none; (Failed)

chart_page.css

#outside{
    display: none;
}
...
Enter fullscreen mode Exit fullscreen mode

I couldn't get the blob by "toBlob()".

visibility: hidden; (Success)

chart_page.css

#outside{
    visibility: hidden;
}
...
Enter fullscreen mode Exit fullscreen mode

Draw outside (Success)

#outside{
}
.chart_area{
...
    height: 1024px;
    width: 1024px;
    position: absolute;
    top: -2000px;
    left: -2000px;
}
...
Enter fullscreen mode Exit fullscreen mode

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

Top comments (0)

Billboard image

Create up to 10 Postgres Databases on Neon's free plan.

If you're starting a new project, Neon has got your databases covered. No credit cards. No trials. No getting in your way.

Try Neon for Free →

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay