skip to Main Content

I am using fabric.js in react application . The scenario i was trying is i want to export the Entire canvas as image but below are the issues im getting

  1. Canvas is resetting after pressing export button.
  2. After zoomed or panned the i am unable to export the content which is outside viewport . I am able to export only which is viewable on screen .But i need to export entire object which are placed on the canvas irrespective of zoomed and pan .

below is my code

import React, { useEffect, useRef, useState } from 'react';
import { fabric } from 'fabric';

function CanvasComponent() {
  const canvasRef = useRef(null);
  const [fabricCanvas, setFabricCanvas] = useState(null);
  const [zoom, setZoom] = useState(1);

  useEffect(() => {
    const initCanvas = new fabric.Canvas(canvasRef.current);
    setFabricCanvas(initCanvas);

    // Set initial canvas size based on window
    const updateCanvasSize = () => {
      if (initCanvas) {
        const width = window.innerWidth;
        const height = window.innerHeight;
        initCanvas.setWidth(width);
        initCanvas.setHeight(height);
        initCanvas.renderAll();
      }
    };
    updateCanvasSize();

    // Add random objects
    const rect = new fabric.Rect({
      left: 100,
      top: 100,
      fill: 'red',
      width: 60,
      height: 70
    });

    const circle = new fabric.Circle({
      left: 200,
      top: 200,
      fill: 'green',
      radius: 50
    });

    initCanvas.add(rect, circle);

    // Add event listener for window resize
    window.addEventListener('resize', updateCanvasSize);

    // Clean up the event listener on component unmount
    return () => {
      window.removeEventListener('resize', updateCanvasSize);
    };

  }, []);


  useEffect(() => {
    if (fabricCanvas) {
      fabricCanvas.setZoom(zoom);
      fabricCanvas.renderAll();
    }
  }, [zoom, fabricCanvas]);

  const handleExport = () => {
    if (fabricCanvas) {
      // Store the current content of the canvas
      const originalData = fabricCanvas.toDataURL();

      // Fill the canvas with a white background
      const ctx = fabricCanvas.getContext('2d');
      ctx.fillStyle = 'white';
      ctx.fillRect(0, 0, fabricCanvas.width, fabricCanvas.height);

      // Draw the original content on top of the white background
      const img = new Image();
      img.onload = () => {
        ctx.drawImage(img, 0, 0);

        // Now export the canvas with the white background
        const dataURL = fabricCanvas.toDataURL('image/png');
        const link = document.createElement('a');
        link.href = dataURL;
        let file_name = (Math.random() + 1).toString(36).substring(7);
        link.download = `canvas_export_${file_name}.png`;
        link.click();

        // Restore the original content of the canvas
        fabricCanvas.clear();
        fabricCanvas.setBackgroundImage(originalData, fabricCanvas.renderAll.bind(fabricCanvas), {
          originX: 'left',
          originY: 'top'
        });
      };
      img.src = originalData;
    }
  };

  return (
    <div>
      <input 
        type="range" 
        min="0.1" 
        max="2" 
        step="0.1" 
        value={zoom} 
        onChange={(e) => setZoom(parseFloat(e.target.value))}
      />
      <button onClick={handleExport}>Export Canvas</button>
      <canvas ref={canvasRef}></canvas>
    </div>
  );
}

export default CanvasComponent;

2

Answers


  1. I solved the presented issues by updating handleExport function and adding useEffect to update the dimensions of the Canvas based on zooming value.

    import React, { useEffect, useRef, useState } from "react";
    import { fabric } from "fabric";
    
    function CanvasComponent() {
      const canvasRef = useRef(null);
      const [fabricCanvas, setFabricCanvas] = useState(null);
      const [zoom, setZoom] = useState(1);
      const [dimensions, setDimentions] = useState({ width: 0, height: 0 });
      useEffect(() => {
        const initCanvas = new fabric.Canvas(canvasRef.current);
        setFabricCanvas(initCanvas);
    
        // Set initial canvas size based on window
        const updateCanvasSize = () => {
          if (initCanvas) {
            const width = window.innerWidth;
            const height = window.innerHeight;
            setDimentions({ width, height });
            initCanvas.setWidth(width);
            initCanvas.setHeight(height);
            initCanvas.renderAll();
          }
        };
        updateCanvasSize();
    
        // Add random objects
        const rect = new fabric.Rect({
          left: 100,
          top: 100,
          fill: "red",
          width: 60,
          height: 70,
        });
    
        const circle = new fabric.Circle({
          left: 200,
          top: 200,
          fill: "green",
          radius: 50,
        });
    
        initCanvas.add(rect, circle);
    
        // Add event listener for window resize
        window.addEventListener("resize", updateCanvasSize);
    
        // Clean up the event listener on component unmount
        return () => {
          window.removeEventListener("resize", updateCanvasSize);
        };
      }, []);
    
      useEffect(() => {
        if (fabricCanvas) {
          fabricCanvas.setZoom(zoom);
          fabricCanvas.renderAll();
        }
      }, [zoom, fabricCanvas]);
    
      //Update the dimensions of the Canvas based on zooming value
      useEffect(() => {
        if (fabricCanvas) {
          const newFabricCanvas = fabricCanvas;
          if (zoom > 1) {
            newFabricCanvas.setHeight(dimensions.height * zoom);
            newFabricCanvas.setWidth(dimensions.width * zoom);
          } else {
            newFabricCanvas.setHeight(dimensions.height);
            newFabricCanvas.setWidth(dimensions.width);
          }
          setFabricCanvas(newFabricCanvas);
        }
      }, [fabricCanvas, zoom]);
    
      const handleExport = () => {
        if (fabricCanvas) {
          // Change the color of the current background
          fabricCanvas.backgroundColor = "white";
          // Export the canvas with the white background
          const link = document.createElement("a");
          link.href = fabricCanvas.toDataURL({
            format: "png",
            quality: 0.8,
          });
          let file_name = (Math.random() + 1).toString(36).substring(7);
          link.download = `canvas_export_${file_name}.png`;
          link.click();
        }
      };
    
      return (
        <div>
          <input
            type="range"
            min="0.1"
            max="2"
            step="0.1"
            value={zoom}
            onChange={(e) => setZoom(parseFloat(e.target.value))}
          />
          <button onClick={handleExport}>Export Canvas</button>
          <canvas ref={canvasRef}></canvas>
        </div>
      );
    }
    
    export default CanvasComponent;
    
    Login or Signup to reply.
  2. First, it selects the canvas element using document.getElementsByTagName('canvas').

    The getElementsByTagName function returns a collection of elements with the given tag name, so using [0] at the end ensures we target the first canvas element.

    The toDataURL() method is then called on the canvas to convert it into a data URL string representing the image.

    Next, we create a new window using window.open(‘about:blank’) and assign it to the w variable. This new window will be used to display the image.
    A new Image object is created and assigned to the image variable.
    The src property of the Image object is set to the canvas data URL obtained earlier.

    We use setTimeout to delay the execution of the next step by 0 milliseconds. This ensures that the image has finished loading before attempting to write it to the new window.

    Finally, within the setTimeout function, we write the HTML representation of the image (image.outerHTML) to the document of the new window (w.document.write(...)). This causes the image to display in the new window once it has finished loading.

    By following this approach, you can export a canvas element as an image in JavaScript.

    Example:

    let data = document.getElementsByTagName('canvas')?.[0]?.toDataURL();
    let w = window.open('about:blank');
    let image = new Image();
    image.src = data;
    setTimeout(function(){
      w.document.write(image.outerHTML);
    }, 0);
    

    it will open new tab where you can see canvas as an image
    to simply download you can use anchor tag as you used.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search