0

I'm trying to save my canvas using .toDataURL() but I keep getting the error mentioned in the title.

The canvas and context are initialized as follows:

const canvas = useRef();
let ctx = null;

This is the main function that previews the canvas based on user input:

const onFinish = () => {
    ctx?.clearRect(0, 0, canvas.width, canvas.height); //to reset canvas after input update
    const canvasEle = canvas.current;
    canvasEle.width = canvasEle.clientWidth;
    canvasEle.height = canvasEle.clientHeight;

    // get context of the canvas
    ctx = canvasEle.getContext("2d");


    //function that writes text on an image
    writeText({
      text: textA,
      x: 350,
      y: 100,
      text2: textB,
      x2: 350,
      y2: 200,
    });

//code breaks here
var dataURL = canvas?.toDataURL();
console.log(dataURL);
};

The canvas element is called as follows:

return(
<>

      <div className="App">
        <h3>Design Ecard</h3>
        <canvas ref={canvas}></canvas>
      </div>
</>

)

Update Tried using canvas.current.toDataURL() but that returned the URL of an empty canvas. I can also save the image properly by simply right-clicking and selecting Save Image

Update 2 The writeText function

const writeText = async (info) => {
    const { text, x, y, text2, x2, y2 } = info;
    var imageObj = new Image();
    imageObj.onload = function () {
      ctx?.drawImage(imageObj, 10, 10);
      ctx?.beginPath();
      ctx.font = fontSize + "px " + fontFamily;
      ctx.textAlign = textAlign;
      ctx.textBaseline = "top";
      ctx.fillStyle = colorCode;
      ctx.fillText(text, x, y);
      ctx.fillText(text2, x2, y2);
      ctx.stroke();
    };
    imageObj.src = bgImage;
    imageObj.width = "600px";
    imageObj.height = "550px";
};
Fahad K
  • 11
  • 3

1 Answers1

0

useRef gives you a plain object whose reference is stable. If you put something in the ref with ref={canvas}, that value doesn't replace the ref, but gets assigned to a property of the ref.

var dataURL = canvas?.toDataURL();

should be

const dataURL = canvas.current?.toDataURL();

I'd also suggest changing the name of the ref from canvas to canvasRef to avoid confusing yourself - it's a ref that holds the canvas, not the canvas itself.

You also need to wait for the image to load before extracting from the canvas. Either call toDataURL inside writeText, or have writeText return a Promise.

const writeText = (info) => new Promise((resolve) => {
    const { text, x, y, text2, x2, y2 } = info;
    var imageObj = new Image();
    imageObj.onload = function () {
        ctx?.drawImage(imageObj, 10, 10);
        ctx?.beginPath();
        ctx.font = fontSize + "px " + fontFamily;
        ctx.textAlign = textAlign;
        ctx.textBaseline = "top";
        ctx.fillStyle = colorCode;
        ctx.fillText(text, x, y);
        ctx.fillText(text2, x2, y2);
        ctx.stroke();
        resolve();
    };
    imageObj.src = bgImage;
    imageObj.width = "600px";
    imageObj.height = "550px";
});
writeText({
  // ...
})
  .then(() => {
    var dataURL = canvas?.toDataURL();
    console.log(dataURL);
  });
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • I tried that, but as I mentioned in the update above, using `canvas.current?.toDataURL()` gives a URL to the blank canvas – Fahad K Feb 10 '22 at 06:58
  • Sounds like `writeText` isn't finishing before you're calling `toDataURL` - is it asynchronous? – CertainPerformance Feb 10 '22 at 07:10
  • It wasn't originally, but I did add `async` to the header, and still got the blank canvas. Going to add the `writeText` function in an edit above – Fahad K Feb 10 '22 at 07:39
  • It definitely is asynchronous. Adding `async` to the function won't help, you need to wait for the image to load before calling `toDataURL`, otherwise the image's data won't be on the canvas at the time you call it – CertainPerformance Feb 10 '22 at 07:59
  • I made the changes you suggested, but that caused this error `Uncaught (in promise) DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.` – Fahad K Feb 10 '22 at 08:18
  • Sounds like your image is on another domain from the one the canvas is on. Put it on the same domain so you don't run into security restrictions, or set up the server it's on to permit cross-domain requests. https://stackoverflow.com/a/22716873 – CertainPerformance Feb 10 '22 at 08:23