Fabric JS canvas not loading initially

I created a basic canvas in fabric js but when i run the app it fails to render the app but when i click a button that adds rectangle to the canvas it gets visible with a movable rectangle

This is the initial screen

this is the App.tsx

import Toolbox from "./components/Toolbox"
import { MenuIcons } from "./data/menuicons";
import Dcanvas from "./Dcanvas";
import * as fabric from "fabric"
import { useEffect, useState} from "react";

const App = () => {

  const [canvas, setCanvas] = useState<fabric.Canvas>();

  useEffect(() => {
    setCanvas(new fabric.Canvas("canvas", {
      height: 450,
      width: 800,
      backgroundColor: "white"
    })
    );
    canvas?.requestRenderAll();
  }, []);

  return (
    <div className="flex items-center fixed w-screen h-screen bg-gray-200 z-0">
      <Toolbox menu={MenuIcons} canvas={canvas} />
      <Dcanvas/>
    </div>
  )
}

export default App

This is the canvas

const Dcanvas = () => {
  return (
    <div>
      <canvas id="canvas"/>
    </div>
  )
}

export default Dcanvas

This is Toolbox.tsx

import { IconProps } from "../data/btypes"
import * as fabric from "fabric";

function Icon({ Name, size, title, action, canvas }: IconProps & { canvas: fabric.Canvas | undefined }) {
  return <div onClick={() => { action(canvas) }} className="cursor-pointer">
    <Name size={size} color="rgb(67,67,67)" title={title} />
  </div>
}


interface ToolProps {
  menu: IconProps[]
  canvas: fabric.Canvas | undefined
}

const Toolbox = ({ menu, canvas }: ToolProps) => {
  return (
    <div className="grid grid-cols-1 gap-7 p-3 rounded-md m-2 shadow-md w-fit h-fit bg-white">
      {
        menu.map((props) => {
          return <Icon {...props} key={props.title} canvas={canvas}/>
        })
      }
    </div>
  )
}

export default Toolbox

This is menuicons file

import { LuShapes } from "react-icons/lu";
import { FaPenNib } from "react-icons/fa";
import { IoMdCut } from "react-icons/io";
import { TbBlendMode } from "react-icons/tb";
import { MdOutlineColorLens } from "react-icons/md";
import { CgExport } from "react-icons/cg";
import { MdAnimation } from "react-icons/md";
import { PiHandGrabbing } from "react-icons/pi";
import { IconProps } from "./btypes";
import * as fabric from "fabric";


const icomenu: IconProps[] = [

    {
        Name: LuShapes,
        size: 25,
        title: "Draw shapes",
        action: (canvas: fabric.Canvas | undefined) => {
            console.log("called", canvas);
            const rect = new fabric.Rect({
                width: 100,
                height: 100,
                fill: "red",
                left: 100,
                top: 100,
            });
            canvas?.add(rect);
            canvas?.requestRenderAll();
        }
    },


    {
        Name: FaPenNib,
        size: 21,
        title: "Pen tool",
        action: () => { }
    },

    {
        Name: IoMdCut,
        size: 23,
        title: "Cut tool",
        action: () => { }
    },

    {
        Name: TbBlendMode,
        size: 24,
        title: "Blend mode",
        action: () => { }
    },

    {
        Name: MdOutlineColorLens,
        size: 25,
        title: "Color picker",
        action: () => { }
    },

    {
        Name: CgExport,
        size: 23,
        title: "Export",
        action: () => { }
    },

    {
        Name: MdAnimation,
        size: 23,
        title: "Animation",
        action: () => { }
    },

    {
        Name: PiHandGrabbing,
        size: 23,
        title: "Hand",
        action: () => { }
    }
];


export const MenuIcons = icomenu

This is the final screen

Preface: I am NOT a React fluent person. I just poke around Google here and there.

requestRenderAll wont do anything if a render request is already processing. For shiggles, what happens if you just plain tell it to renderAll?

The issue was the canvas was not available initially in DOM, so I solved it with a small workaround - I created a canvas directly under the App and created a reference and loaded the canvas when the reference got updated ( when the canvas became available in DOM ).

const canvasref = useRef<HTMLCanvasElement>(null);
  const [canvas, setCanvas] = useState<fabric.Canvas>();

  useEffect(() => {
    if (canvasref.current && !canvas)
      setCanvas(new fabric.Canvas(canvasref.current, {
        height: 450,
        width: 800,
        backgroundColor: "white"
      })
      );
    canvas?.requestRenderAll();
  }, [canvasref.current]);

  

  return (
    <div className="flex items-center fixed w-screen h-screen bg-gray-200 z-0">
      <Toolbox menu={MenuIcons} canvas={canvas} />
      <div>
        <canvas id="canvas" ref={canvasref} />
      </div>
    </div>
1 Like