如何使用fabric.js和React将Canvas导出为图像

问题描述 投票:0回答:1

我在react应用程序中使用fabric.js。我尝试的场景是我想将整个画布导出为图像,但以下是我遇到的问题

  1. 按导出按钮后画布正在重置。
  2. 缩放或平移后,我无法导出视口之外的内容。我只能导出在屏幕上可见的内容。但是我需要导出放置在画布上的整个对象,无论缩放和平移如何。

下面是我的代码

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;
javascript reactjs canvas fabricjs
1个回答
0
投票

我通过更新handleExport函数并添加useEffect来根据缩放值更新Canvas的尺寸来解决所提出的问题。

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;
© www.soinside.com 2019 - 2024. All rights reserved.