Recharts component to PNG
Asked Answered
D

7

10

I currently have a Recharts component that I would like to export as a PNG file.

<LineChart
  id="currentChart"
  ref={(chart) => (this.currentChart = chart)}
  width={this.state.width}
  height={this.state.height}
  data={this.testData}
  margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
>
  <XAxis dataKey="name" />
  <YAxis />
  <CartesianGrid strokeDasharray="3 3" />
  <Tooltip />
  <Legend />
  <Line type="monotone" dataKey="pv" stroke="#8884d8" activeDot={{ r: 8 }} />
  <Line type="monotone" dataKey="uv" stroke="#82ca9d" />
</LineChart>;

but I'm unsure if this is directly supported by the library.

I have an idea that involves using a canvas and a 2D rendering context to get me close to a solution, as outlined on MDN

However, I'm not sure of a generic way to render an HTML element (or React Component) as a canvas to implement this solution.

I might be going about this all wrong, and I would appreciate the correction!

Domela answered 13/7, 2017 at 16:8 Comment(0)
D
11

I was able to solve my problem by delving into the Recharts component. Recharts renders as an SVG under a wrapper so all I had to do was convert properly to save as both HTML or SVG

// Exports the graph as embedded JS or PNG
exportChart(asSVG) {

    // A Recharts component is rendered as a div that contains namely an SVG
    // which holds the chart. We can access this SVG by calling upon the first child/
    let chartSVG = ReactDOM.findDOMNode(this.currentChart).children[0];

    if (asSVG) {
        let svgURL = new XMLSerializer().serializeToString(chartSVG);
        let svgBlob = new Blob([svgURL], {type: "image/svg+xml;charset=utf-8"});
        FileSaver.saveAs(svgBlob, this.state.uuid + ".svg");
    } else {
        let svgBlob = new Blob([chartSVG.outerHTML], {type: "text/html;charset=utf-8"});
        FileSaver.saveAs(svgBlob, this.state.uuid + ".html");
    }
}

I am using FileSaver.js for the save prompt.

Domela answered 13/7, 2017 at 20:26 Comment(5)
Hi, what is currentChart here?Ragtime
Hi, @Ragtime ref={(chart) => this.currentChart = chart}Playlet
Where do you convert the svg to png?Christianize
Is this possible to add a codepen, jsfiddle to have a full picture of current example?Perverted
Any way to convert the svg to pngOuster
K
11

This function takes SVG element on input and transforms to image/png data:

export const svgToPng = (svg, width, height) => {

    return new Promise((resolve, reject) => {

        let canvas = document.createElement('canvas');
        canvas.width = width;
        canvas.height = height;
        let ctx = canvas.getContext('2d');

        // Set background to white
        ctx.fillStyle = '#ffffff';
        ctx.fillRect(0, 0, width, height);

        let xml = new XMLSerializer().serializeToString(svg);
        let dataUrl = 'data:image/svg+xml;utf8,' + encodeURIComponent(xml);
        let img = new Image(width, height);

        img.onload = () => {
            ctx.drawImage(img, 0, 0);
            let imageData = canvas.toDataURL('image/png', 1.0);
            resolve(imageData)
        }

        img.onerror = () => reject();

        img.src = dataUrl;
    });
};

And how to access the Recharts SVG element? This code snippet allows you to render any Chart outside of your current visible DOM and use it's SVG:

const exportChart = () => {

    // Output image size
    const WIDTH = 900;
    const HEIGHT = 250;

    const convertChart = async (ref) => {

        if (ref && ref.container) {
            let svg = ref.container.children[0];
            let pngData = await svgToPng(svg, WIDTH, HEIGHT);
            console.log('Do what you need with PNG', pngData);
        }
    };

    const chart = <LineChart data={...} width={WIDTH} height={HEIGHT}
        ref={ref => convertChart(ref)} />;

    // Render chart component into helper div
    const helperDiv = document.createElement('tmp');
    ReactDOM.render(chart, helperDiv);
}
Kinnard answered 20/5, 2019 at 14:33 Comment(2)
I can't create a blob using the pngData, any help on that? I need the blob so that I can download the png imageGuiana
@Guiana I believe, you can try to modify the solution by downloading the pdf image from canvas. See here: #8127123Kinnard
B
3

@brammitch created a package for this (inspired by answers here):

https://github.com/brammitch/recharts-to-png

Binominal answered 9/9, 2020 at 12:3 Comment(0)
R
3

The written answer helped me a lot. Many thanks for that. Nevertheless, I was missing an "out of the box" solution for the download as png, which I would like to make up for here. Even if it's too late, maybe it will help someone else.

handleExportChart = () => {

        let chartSVG = ReactDOM.findDOMNode(this.currentChart).children[0];
        const width = chartSVG.clientWidth;
        const height = chartSVG.clientHeight;
        let svgURL = new XMLSerializer().serializeToString(chartSVG);
        let svgBlob = new Blob([svgURL], { type: "image/svg+xml;charset=utf-8" });
        let URL = window.URL || window.webkitURL || window;
        let blobURL = URL.createObjectURL(svgBlob);

        let image = new Image();
        image.onload = () => {
            let canvas = document.createElement('canvas');
            canvas.width = width;
            canvas.height = height;
            let context = canvas.getContext('2d');
            context.drawImage(image, 0, 0, context.canvas.width, context.canvas.height);
            let png = canvas.toDataURL('image/png', 1.0);
            FileSaver.saveAs(png, "Test.png");
        };

        image.src = blobURL;
    };

Radioscope answered 12/1, 2021 at 20:52 Comment(0)
Q
0

This is an old post but this might help someone

let pngData = await getPngData(this.ref);
FileSaver.saveAs(pngData, filename);

using recharts-to-png and file-saver npm modules

Quiz answered 19/1, 2021 at 18:31 Comment(0)
P
0
        <PolarRadiusAxis axisLine={true} tick={true} tickCount={6} />
        <Radar
          name="Mike"
          dataKey="weight"
          stroke="#8884d8"
          fill="#8884d8"
          fillOpacity={0.6}
        />

      </RadarChart>
    </ResponsiveContainer>
 <button onClick={handleDownload}>Save Chart</button>

The Chart component receives the data prop, which represents the data to be displayed in the Recharts chart.

The handleDownload function is triggered when the "Download Chart" button is clicked.

Inside the handleDownload function:

The chart container element is obtained using document.getElementsByClassName("recharts-wrapper")[0]. The SVG content of the chart is extracted by selecting the first SVG element within the chart container. The SVG content is serialized into a string using XMLSerializer().serializeToString(chartSvg). An image element is created, and a blob is created from the SVG string using new Blob([chartXml], { type: "image/svg+xml;charset=utf-8" }). A URL for the blob is created using URL.createObjectURL(svgBlob). An onload event handler is attached to the image element, which ensures that the image is fully loaded before performing further actions. Inside the onload event handler: A canvas element is created with the same dimensions as the chart container. The image is drawn onto the canvas using context.drawImage(image, 0, 0). The canvas is converted to a data URL using canvas.toDataURL("image/png"). A temporary link element is created with the data URL as its href and "chart.png" as the download attribute value. The link element is clicked programmatically using link.click(), triggering the download of the binary file. The Chart component renders a responsive container with a Recharts LineChart inside it. The chart is configured with the provided data, and various components such as Line, CartesianGrid, XAxis, YAxis, Tooltip, and Legend are added as needed.

The rendered chart is enclosed within a element with a fixed height of 300px.

A "Download Chart" button is rendered below the chart, and the handleDownload function is attached to its onClick event.

Pointblank answered 6/6, 2023 at 10:57 Comment(0)
L
0
    const chartRef = useRef<HTMLDivElement>(null);

   const downloadChartAsImage = () => {
      if (chartRef.current) {
         const svgElement = chartRef.current.querySelector('svg');
         if (svgElement) {
            const svgData = new XMLSerializer().serializeToString(svgElement);
            const canvas = document.createElement('canvas');
            const ctx = canvas.getContext('2d');
            const img = new Image();

            img.onload = function () {
               canvas.width = img.width;
               canvas.height = img.height;
               ctx?.drawImage(img, 0, 0);

               const a = document.createElement('a');
               a.href = canvas.toDataURL('image/png');
               a.download = 'chart.png';
               document.body.appendChild(a);
               a.click();
               document.body.removeChild(a);
            };

            img.src = 'data:image/svg+xml;base64,' + btoa(svgData);
         }
      }
   };
<ChartContainer config={chartConfig} className="min-h-[200px] w-full" ref={chartRef}>
Lysin answered 21/7 at 14:33 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Indonesian

© 2022 - 2024 — McMap. All rights reserved.