0

Im trying to set up this site where the user input text will draw text into the speech bubble of the image being displayed. I have three issues:

  1. Users can input text to canvas, but the text drawn on canvas isn't updated when users delete characters

  2. The text isn't wrapping at all with user inputs but it wraps when I directly insert a string. I want the text to wrap with the user input

  3. Anytime a user starts typing into a new input box all of the text in the canvas gets erased.

I've looked for tutorials online and answers here but none of them are the exact solution that I need to get this working properly here. Any other suggestions would be helpful.

Thanks!

<html>
<input id="input-text" type="text" onkeyup="usertextChange(this.value)" 
maxlength="6" />
<input id="input-text2" type="text" onkeyup="usertextChange2(this.value)" 
maxlength="5" />
<input id="input-text3" type="text" onkeyup="usertextChange3(this.value)" 
maxlength="12" />
<input id="input-text4" type="text" onkeyup="usertextChange4(this.value)" 
maxlength="18" />
<div class="art-container">
<canvas id="canvas" width="576" height="576">
Canvas requires a browser that supports HTML5.

</canvas>
<img crossOrigin="Anonymous"  id="no-crying" 
src="https://cdn.glitch.com/4ed5f9d8-97ad-4c53-b855-3e8d508ba2f3%2FDVSN- 
FUTURE-NO-CRYIN-FINAL-NoText.jpg?v=1572122142300"/>

</div>
</html>

<style>
#input-text, #input-text2, #input-text3, #input-text4 {
width: 90%;
font-size: 18px;
height: 24px;
text-transform: uppercase;
padding: 0 8px;
background-color: transparent;
color: red;
border: 2px solid black;
outline: none;
border-radius: 4px;
margin-bottom: 12px;
margin-left: auto;
margin-right: auto;
font-weight: 500;
font-family: bubblegum;
}
#no-crying {
display: none;
}
</style>

<script>

var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var maxWidth = 90;
var lineHeight = 45;
var x = 35;
var y = 315;
var text = document.getElementById('input-text1').value;
var text2 = document.getElementById('input-text2').value;
var text3 = document.getElementById('input-text3').value;
var text4 = document.getElementById('input-text4').value;

function drawImage(text) {
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
var img = document.getElementById('no-crying');  
context.drawImage(img, 0, 0, canvas.width, canvas.height);
}


function wrapText(context, text, x, y, maxWidth, lineHeight) {
var words = text.split(' ');
var line = '';

for(var n = 0; n < words.length; n++) {
        var testLine = line + words[n] + ' ';
    var metrics = context.measureText(testLine);
    var testWidth = metrics.width;
      if (testWidth > maxWidth && n > 0) {
        context.fillText(line, x, y);
        line = words[n] + ' ';
        y += lineHeight;
      }
      else {
        line = testLine;
      }
    }
    context.fillText(line, x, y);
  }



  window.onload = function() {
    drawImage();
  }

  // USER IMPUT FUNCTIONS
        window.usertextChange = function(val){
    context.clearRect(0, 0, canvas.width, canvas.height);
          context.restore();
    drawImage();
    context.font = '26px Bubblegum';
    context.fillStyle = "#000000";
    context.fillText(val, 39, 315);
    context.save();
    wrapText(context, text, x, y, maxWidth, lineHeight);
        } 
  window.usertextChange2 = function(val){
          context.restore();
    context.font = '22px Bubblegum';
    context.fillStyle = "#000000";
    context.fillText(val, 45, 370);
    context.save();
    wrapText(context, text, x, y, maxWidth, lineHeight);
        } 
  window.usertextChange3 = function(val){
          context.restore();
    context.font = '26px Bubblegum';
    context.fillStyle = "#000000";
    context.fillText(val, 25, 420);
    context.save();
    wrapText(context, text, x, y, maxWidth, lineHeight);
        } 
  window.usertextChange4 = function(val){
    context.restore();
            context.font = '24px Bubblegum';
    context.fillStyle = "#000000";
    context.fillText(val, 48, 360);
    wrapText(context, text, x, y, maxWidth, lineHeight);
        } 


</script>

EDIT Im also looking to download the image after adding user input text. What is the best way to implement. Here is what i came up with based off recent responses

function init() {
text1 = '';
text2 = '';
text3 = '';
text4 = '';
backgroundImg = new Image();
backgroundImg.src = 'https://cdn.glitch.com/4ed5f9d8-97ad-4c53-b855- 
3e8d508ba2f3%2FDVSN-FUTURE-NO-CRYIN-FINAL-NoText.jpg?v=1572122142300';

backgroundImg.setAttribute('crossOrigin', 'anonymous');

function addLink() {
var link = document.createElement('a');
link.innerHTML = 'Download!';
link.addEventListener('click', function(e) {
link.href = canvas.toDataURL();
link.download = "salt-bae.png";
}, false); 
link.className = "instruction";
document.getElementById('input-container').appendChild(link);
}

window.onload = function() {
    drawImage();
    addLink();
}
TOX
  • 45
  • 1
  • 11

1 Answers1

1

Few comments:

You use var text = document.getElementById('input-text1').value; with the id input-text1 whereas in your HTML your id is input-text.

You define the function drawImage(text) with a text parameter that is never used. In this function, you create the canvas and a context variables again.

You insert in the DOM an image that you hide to draw it later on the canvas. I think that it's easier to create a new Image object.

The function wrapText() is a bit ambiguous. Does it format text? Does it display anything? You could rename it like displayWrapText() or getWrappedText().

Then, about your issues:

  1. The text is not updated when the user deletes characters because, except for the input #1, when the user types, it doesn't clear the bubble and it draws the new text over the previous one (it's getting darker). That's why you see no difference. Deletion works for the first input because you clear the bubble and draw the full text again.

  2. Tbh, I forgot about this issue when I tried, and now it's working well... In any case, provide an appropriate maxWidth to the wrapText() function: there are small and big bubbles. However, it doesn't wrap if the string doesn't contain any space.

  3. When the user types in the first input, it clears the canvas but display text again only inside the first bubble, so the others are displayed empty. You need to clear the canvas and to redraw the texts in all the corresponding bubbles.

What I did was to apply what I told you before, plus improve the event handling (see bubbling) since it was pretty much the same code for each input. To be able to redraw all the bubbles after every update, I store their value each time they are updated.

const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
const lineHeight = 30;
let backgroundImg;
let text1;
let text2;
let text3;
let text4;

init();

function init() {
  text1 = '';
  text2 = '';
  text3 = '';
  text4 = '';
  backgroundImg = new Image();
  backgroundImg.src = 'https://cdn.glitch.com/4ed5f9d8-97ad-4c53-b855-3e8d508ba2f3%2FDVSN-FUTURE-NO-CRYIN-FINAL-NoText.jpg?v=1572122142300';

  context.drawImage(backgroundImg, 0, 0, canvas.width, canvas.height);

  document.querySelector('#input-container').addEventListener('keyup', function(e) {
    const num = parseInt(e.target.getAttribute('data-bubble'), 10);
    const text = e.target.value;
    saveText(num, text);
    draw(canvas, context, backgroundImg);
  }, false);
}

function draw(canvas, context, backgroundImg) {
  context.clearRect(0, 0, canvas.width, canvas.height);
  context.drawImage(backgroundImg, 0, 0, canvas.width, canvas.height);
  drawBubble(canvas, context, getTextFrom(1), 39, 315, '26px Bubblegum', '#000000', 110, lineHeight); // bubble 1
 drawBubble(canvas, context, getTextFrom(2), 45, 370, '22px Bubblegum', '#000000', 70, lineHeight); // bubble 2
 drawBubble(canvas, context, getTextFrom(3), 20, 425, '26px Bubblegum', '#000000', 120, lineHeight); // bubble 3
 drawBubble(canvas, context, getTextFrom(4), 20, 515, '24px Bubblegum', '#000000', 120, lineHeight); // bubble 4
}

function getTextFrom(num) {
 switch(num) {
   case 1: return text1;
   case 2: return text2;
   case 3: return text3;
   case 4: return text4;
    default: '';
  }
}

function saveText(num, text) {
 switch(num) {
   case 1: text1 = text; break;
   case 2: text2 = text; break;
   case 3: text3 = text; break;
   case 4: text4 = text; break;
  }
}

function displayWrappedText(context, text, x, y, maxWidth, lineHeight) {
  var words = text.split(' ');
  var line = '';

  for (var n = 0; n < words.length; n++) {
    var testLine = line + words[n] + ' ';
    var metrics = context.measureText(testLine);
    var testWidth = metrics.width;
    if (testWidth > maxWidth && n > 0) {
      context.fillText(line, x, y);
      line = words[n] + ' ';
      y += lineHeight;
    } else {
      line = testLine;
    }
  }
  context.fillText(line, x, y);
}

function drawBubble(canvas, context, text, x, y, font, color, maxWidth, lineHeight) {
  context.font = font;
  context.fillStyle = color;
  //context.fillText(text, x, y);
  displayWrappedText(context, text, x, y, maxWidth, lineHeight);
}
#input-container > input {
  width: 90%;
  font-size: 18px;
  height: 24px;
  text-transform: uppercase;
  padding: 0 8px;
  background-color: transparent;
  color: red;
  border: 2px solid black;
  outline: none;
  border-radius: 4px;
  margin-bottom: 12px;
  margin-left: auto;
  margin-right: auto;
  font-weight: 500;
  font-family: bubblegum;
}
<div id="input-container">
  <input class="js-input-text" data-bubble="1" type="text" maxlength="6" />
  <input class="js-input-text" data-bubble="2" type="text" maxlength="5" />
  <input class="js-input-text" data-bubble="3" type="text" maxlength="12" />
  <input class="js-input-text" data-bubble="4" type="text" maxlength="18" />
</div>
<div class="art-container">
  <canvas id="canvas" width="576" height="576">
    Canvas requires a browser that supports HTML5.
  </canvas>
</div>
Barudar
  • 560
  • 4
  • 13
  • You can create another image: save the whole canvas to an image, with the image and the text: https://stackoverflow.com/questions/12796513/html5-canvas-to-png-file (look for the accepted answer) – Barudar Oct 28 '19 at 07:46
  • Im sorry, I was looking through the link you sent, and Im still not the best with .js could you help with how to go about this using the code we've been using ? – TOX Oct 28 '19 at 08:00
  • He implemented a `dlCanvas()` function. I can't test now, so try to use it with the provided download button – Barudar Oct 28 '19 at 09:02
  • yes, i got that part but the issue is that it keeps downloading the versions of the canvas without the typed text. How do I download the current state of the canvas at all times ? – TOX Oct 28 '19 at 09:17
  • I also noticed that for some reason the text only wraps when I use the spacebar, but I wanted it to wrap whether I enter a space or not – TOX Oct 28 '19 at 22:18
  • 1
    It's surprising, with his example, he downloads the current canvas without problem. To be able to wrap without spaces, you need to manually create an array of lines that you manually display under/over each other. – Barudar Oct 29 '19 at 01:18