javascriptreactjsimagefile-uploadhtml5-canvas

Add text to image using canvas


I am trying to convert an image to a canvas, and than add a text to the image and convert it back to an image, but I am having issues doing it. The text is NEVER added to the final picture.

At the end of the function I upload the image to the server but the picture uploaded doen't have the text on it, just the picture.

I have tried many different ways and the result is the same, can someone tell me what am I doing wrong please?

  async function saveConfiguration() {
    try {
      const {
        left: caseLeft,
        top: caseTop,
        width,
        height,
      } = phoneCaseRef.current!.getBoundingClientRect();
  
      const { left: containerLeft, top: containerTop } =
        containerRef.current!.getBoundingClientRect();
  
      const leftOffset = caseLeft - containerLeft;
      const topOffset = caseTop - containerTop;
  
      const actualX = renderedPosition.x - leftOffset;
      const actualY = renderedPosition.y - topOffset;
  
      //create canvas
      const canvas = document.createElement("canvas");
      canvas.width = width;
      canvas.height = height;
      const ctx = canvas.getContext("2d");
  
      const userImage = new Image();
      userImage.crossOrigin = "anonymous";
      userImage.src = imageUrl;
      await new Promise((resolve) => (userImage.onload = resolve));
  
      //add image
      ctx?.drawImage(
        userImage,
        actualX,
        actualY,
        renderedDimension.width,
        renderedDimension.height
      );
  
  
      // add text
      ctx.font = `${fontSizeCustom}px Arial`;
      ctx.fillStyle = "rgba(255,255,255,.5)";
      ctx.textBaseline = "top";
      ctx.fillStyle = "red"; // 
      ctx.fillText(customText, fontPosition.x, fontPosition.y);
  
  
  
      const base64 = canvas.toDataURL();
      const base64Data = base64.split(",")[1];
  
      //function convert base64 to blob
      const blob = base64ToBlob(base64Data, "image/png");
      const file = new File([blob], "filename.png", { type: "image/png" });
  
      //upload iamge to server UPLOADTHINGS
      await startUpload([file], { configId });
    } catch (err) {
      console.log("error al subir imagen");
    }
  }
  

Function 

  function base64ToBlob(base64: string, mimeType: string) {
    const byteCharacters = atob(base64);
    const byteNumbers = new Array(byteCharacters.length);
    for (let i = 0; i < byteCharacters.length; i++) {
      byteNumbers[i] = byteCharacters.charCodeAt(i);
    }
    const byteArray = new Uint8Array(byteNumbers);
    return new Blob([byteArray], { type: mimeType });
  }

I tried using the function but doent work. Just want to add the text to the final image.


Solution

  • After faking the data for some of the variable that you did not include, writing an upload function, and removing the base64 code, your code seemed to work just fine.

    The only reason I can think of that it did not work for you, is that your canvas had a 0 height or width.

    I would recommend that you append the canvas to the DOM for debugging purposes. i.e. You can see if the text is actually being drawn to the canvas (it was).

    You can get a blob directly from the canvas, which is a file like object (files are based on blobs). So no need to get a Data URL and no need to for the base64 conversion.

    You may not even need to convert the blob to a File.

    Stackoverflow will not let the save dialog box pop up in this code snippet, but if you run it, you can see that the text is added. Also if you run the code locally on your computer, you can see it save the image WITH the text overlaid.

    saveConfiguration()
    
    async function saveConfiguration() {
      const fontSizeCustom = 20
      const fontPosition = {x:30, y:40}
      let customText = "Something to say!"
      let imageUrl = "https://images.dog.ceo/breeds/beagle/1271553739_Milo.jpg"
      let phoneCaseRef = {}
      let containerRef = {}
      let renderedPosition = {x:0, y:0} // NO idea what this is suppose to be
      let renderedDimension = {width:140, height:100} // NO idea what this is suppose to be
      phoneCaseRef.current = document.querySelector("#PhoneCaseID")
      containerRef.current = document.querySelector("#ContainerID")
    
      try {
        const {
          left: caseLeft,
          top: caseTop,
          width,
          height,
        } = phoneCaseRef.current.getBoundingClientRect();
    
        const { left: containerLeft, top: containerTop } =
          containerRef.current.getBoundingClientRect();
    
        const leftOffset = caseLeft - containerLeft;
        const topOffset = caseTop - containerTop;
    
        const actualX = renderedPosition.x - leftOffset;
        const actualY = renderedPosition.y - topOffset;
    
        //create canvas
        const canvas = document.createElement("canvas");
        canvas.width = width;
        canvas.height = height;
        const ctx = canvas.getContext("2d");
    
        // For testing purposes, show the canvas on the screen
        containerRef.current.appendChild( canvas )
        // ctx.fillRect(20,20,100,100)
    
        const userImage = new Image();
        userImage.crossOrigin = "anonymous";
        userImage.src = imageUrl;
        await new Promise((resolve) => (userImage.onload = resolve));
    
        //add image
        ctx.drawImage(
          userImage,
          actualX,
          actualY,
          renderedDimension.width,
          renderedDimension.height
        );
    
    
        // add text
        ctx.font = `${fontSizeCustom}px Arial`;
        ctx.fillStyle = "rgba(255,255,255,.5)";
        ctx.textBaseline = "top";
        ctx.fillStyle = "red"; //
        ctx.fillText(customText, fontPosition.x, fontPosition.y);
    
        const blob = canvas.toBlob( blob => {
          if( blob ) {
            // Convert blob to a File if needed
            // const file = new File([blob], "filename.png", { type: "image/png" });
            //upload iamge to server UPLOADTHINGS
            // await startUpload([file], { configId });
            saveBlob( "filename.png", blob )
          }
          else console.error( `canvas.toBlob() failed` );
        });
    
      } catch (err) {
        console.log("error al subir imagen");
      }
    }
    
    
    
    function saveBlob( defaultFileName, blob ) {
        let objectURL = URL.createObjectURL( blob );
        let aElement = document.createElement( "a" );  // Create a <a> tag (hyperlink)
    
        console.log(`Saving blob --- Save Dialog box would pop up here if run locally.` )
        
        aElement.download = defaultFileName;
        aElement.href = objectURL;
    
        aElement.click( );  // Trigger the save dialog
    
        URL.revokeObjectURL( objectURL );
    }
    #PhoneCaseID {
      border: solid 2px purple;
      padding: 20px;
    }
    #ContainerID {
      border: solid 2px grey;
      width: 200px;
      height: 150px;
    }
            <div id="PhoneCaseID">
              <div id="ContainerID">
                <!-- canvas will get appended here -->
              </div>
            </div>