1

I can really use some help, my goal is to add few lines to the canvas in different places, it means to use the function drawText few times, for some reason if I don't use drawText inside the onload of drawImage the text does not rendering I am really stuck and can use some help, my main target it to make website that can edit pictures and make memes (e.g.: https://imgflip.com/memegenerator)and adding text line is what i am getting stuck on because I don't understand how to render new text line while save the old one, every time i start new line its like the image rendering all over again and start from the beginning.

// Function to load image on the canvas
function drawImg(url) {
    gCurrLineIdx = getCurrLineIdx();
    let currLine = gMeme.lines[gCurrLineIdx];
    const img = new Image();

    img.src = url;
    img.onload = () => {
        gCtx.drawImage(img, 0, 0, gElCanvas.width, gElCanvas.height);
        //If you dont call drawText the text does not render to the canvas
        drawText(currLine.txt, currLine.size, currLine.fontColor, currLine.strokeColor, currLine.align, currLine.font, gElCanvas.width / 2, currLine.y);
    };
}

// Function to add text on the canvas
function drawText(text = '', fontSize = 20, fontColor = 'white', strokeColor = 'black', align = 'center', font = "ariel", x = gElCanvas.width / 2, y = 20) {
    gCtx.strokeStyle = strokeColor;
    gCtx.fillStyle = fontColor;
    gCtx.font = `${fontSize}px ${font}`;
    gCtx.textAlign = align;
    gCtx.fillText(text, x, y);
    gCtx.strokeText(text, x, y);
}

//Function to add new text line on the canvas.
function onAddTextLine() {
    let textInput = document.getElementById('text-input');
    textInput.value = '';

    addTextLine();
    gCurrLineIdx = getCurrLineIdx();
    var currLine = gMeme.lines[gCurrLineIdx];
    drawText(currLine.txt, currLine.size, currLine.fontColor, currLine.strokeColor, currLine.align, currLine.font, gElCanvas.width / 2, currLine.y);
}
Yoshi
  • 54,081
  • 14
  • 89
  • 103
Ream Lasry
  • 11
  • 1
  • My guess would be that the text *is* rendered, but then, when the image is loaded, it's rendered *on top* of the text. – Yoshi Feb 12 '21 at 11:45
  • @Yoshi I thought about it the problem is when i try to add a new text line it just delete the old line I wrote and does not write the new link only the image in the background – Ream Lasry Feb 12 '21 at 11:54
  • 1
    The problem might be that you expect *too much* of the canvas element. Ultimately it's just a bunch of pixels. You're responsible to keep/create your own data model. – Yoshi Feb 12 '21 at 11:55
  • @Yoshi so what does it means that draw text must be called inside drawImage and to add new text line i need save my canvas in data and re render it to the canvas? – Ream Lasry Feb 12 '21 at 12:03
  • In general I'd say yes. You want to have a representation of your model that exist without the canvas. And on every change to that model (e.g. new background image, new text, text changes, ...), you re-paint it to a fresh canvas. Though, this render process needs to handle async operations (like with the backround image). For this you could use simple promises (e.g. async/await). To speed things up, you could use multiple canvas that get merged at a last step. – Yoshi Feb 12 '21 at 14:26

1 Answers1

0

The question I see here is:

how to render new text line while save the old one

Looks like in your code you are drawing things independently, below is a different approach the class Meme has a few functions to add text and image but every time anything change the entire canvas is cleared (clearRect) and every element is re-drawn, the magic is in the this.draw(), that is the call to the common function that does all the drawing.

To keep my sample small I hard-coded many of the things you had as parameters, it should be easy for you to reintroduce them if you need

class Meme {
  constructor(ctx, width, height) {
    this.ctx = ctx;
    this.ctx.font = '80px Arial';
    this.ctx.fillStyle = "blue"
    this.ctx.textAlign = "center"
    this.width = width;
    this.height = height;
  }

  addHeadText(text) {
    this.htext = text;
    this.draw();
  }

  addFootText(text) {
    this.ftext = text;
    this.draw();
  }

  addImage(image_src) {
    this.image = new Image();
    this.image.src = image_src;
    this.image.onload = () => {
      this.draw()
    };
  }

  draw() {
    this.ctx.beginPath();
    this.ctx.clearRect(0, 0, this.width, this.height)
    if (this.image) {
      this.ctx.drawImage(this.image, 0, 0, this.width, this.height);
    }
    if (this.htext) {
      this.ctx.textBaseline = "top";
      this.ctx.fillText(this.htext, this.width / 2, 0);
    }
    if (this.ftext) {
      this.ctx.textBaseline = "bottom";
      this.ctx.fillText(this.ftext, this.width / 2, this.height);
    }
    this.ctx.closePath();
  }
}

const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');

meme = new Meme(ctx, canvas.width, canvas.height)
meme.addHeadText("Hello");
meme.addFootText("World");
meme.addImage("https://i.stack.imgur.com/UFBxY.png");

document.getElementById('htext').onkeyup = (event) => {
  meme.addHeadText(event.srcElement.value);
};
document.getElementById('ftext').onkeyup = (event) => {
  meme.addFootText(event.srcElement.value);
};
Head Text:<input type="text" id="htext" name="ftext" style="width: 140px;" value="Hello"><br>
Foot Text:<input type="text" id="ftext" name="ftext" style="width: 140px;" value="World"><br>

<canvas id="c" width=280 height=380 style="border:2px solid red;"></canvas>
Helder Sepulveda
  • 15,500
  • 4
  • 29
  • 56