3

I'm trying to create something similar to http://www.listhings.com where you can edit the text within the canvas.

I've read the other post HTML5 Canvas Text Edit . But I don't want to edit the text outside of the canvas. I want to edit the text within the canvas.

I'd appreciate if anyone can point me in the right direction.
Thanks

3 Answers 3

7

First, Mohsen correctly points out that when you do context.fillText you are actually "painting a picture of letters" on the canvas. It's not like a word processor!

You can capture the key events on the window and then write the keystrokes out to your canvas.

Here is code and a Fiddle: http://jsfiddle.net/m1erickson/7tXd4/

This example ONLY types lowercase a-z (no capitals, spaces, backspaces, etc)

You will probably want to make more enhancements like these:

Here's code just to get you started:

<!doctype html> <html> <head> <link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css --> <script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script> <style> body{ background-color: ivory; padding:20px; } canvas{border:1px solid red;} </style> <script> $(function(){ var canvas=document.getElementById("canvas"); var ctx=canvas.getContext("2d"); ctx.font="18px Arial"; var keyHistory=""; window.addEventListener("keyup", keyUpHandler, true); function addletter(letter){ keyHistory+=letter; ctx.clearRect(0,0,300,300); ctx.fillText(keyHistory,20,20); } function keyUpHandler(event){ var letters="abcdefghijklmnopqrstuvwxyz"; var key=event.keyCode; if(key>64 && key<91){ var letter=letters.substring(key-64,key-65); addletter(letter); } } }); // end $(function(){}); </script> </head> <body> <p>First click in the red canvas below</p><br/> <p>Then type any lowercase letters from a-z</p><br/> <canvas id="canvas" width=300 height=100></canvas> </body> </html> 
Sign up to request clarification or add additional context in comments.

4 Comments

@albertxing: Nice! Thanks for sharing. I especially like the "code typing" you use as an intro.
Thanks, I appreciate your feedback. How come I can't @mention you?
There is more better solution for keys handling with not only english letters: function keyPressHandler(event) { addletter(String.fromCharCode(event.charCode)); }
Correction for the above comment: String.fromCharCode(event.keyCode)
3

fillText() do not create an object or text node that you can edit afterwards. It will fill text on canvas, that means it will leave pixels on canvas.

You can use Canvas libraries like http://kineticjs.com/ to have a single layer for that text so you can erase the layer and retype the text in. Kinect allows you to bind values to layers so you can save the text you have in the layer in a value binded to the layer.

Comments

0

Try this solution https://jsfiddle.net/tabvn/zjyoexf1/

<!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <title>Canvas Input Element</title> </head> <body> <canvas id="draw" width="500" height="500"></canvas> <script type="text/javascript"> var canvas = document.getElementById("draw"); var ctx = canvas.getContext("2d"); canvas.width = window.innerWidth; canvas.height = window.innerHeight; ctx.beginPath(); function Input(text = "", options = {}){ this.text = text; this.options = Object.assign({width: 250, height: 40, font: "17px Arial", borderWidth: 1, borderColor: "#ccc", padding: 5}, options); this.position = {x: 10, y: 10}; this.isFocus = false; this.focusIndex = text.length; this.isCommandKey = false; this.selected = false; this.render = function(){ ctx.clearRect(this.position.x, this.position.y, this.options.width, this.options.height); ctx.font = this.options.font; ctx.lineWidth = this.options.borderWidth; ctx.strokeStyle = this.options.borderColor; if(this.isFocus){ ctx.strokeStyle = "#000"; } ctx.rect(this.position.x, this.position.y, this.options.width, this.options.height); ctx.stroke(); // write text var str = ""; for(var i = 0; i < this.text.length; i++){ if(!this.selected && this.isFocus && this.focusIndex === i){ str += "|"; } str += this.text[i]; } if(!this.selected && this.isFocus && this.focusIndex === this.text.length){ str += "|"; } if(this.selected){ var _width = ctx.measureText(this.text).width; ctx.fillStyle = 'rgba(0,0,0,0.5)'; ctx.fillRect(this.position.x + this.options.padding, this.position.y + this.options.padding, _width, parseInt(this.options.font, 17)); } ctx.fillStyle = "#000"; ctx.fillText(str, this.position.x + this.options.padding, this.position.y + (this.options.height / 2) + this.options.padding); } this.handleOnClick = function(e){ let clientX = e.clientX; let clientY = e.clientY; if(clientX <= this.position.x + this.options.width && clientX >= this.position.x && clientY <= this.position.y + this.options.height && clientY >= this.position.y){ if(!this.isFocus){ this.isFocus = true; this.focusIndex = this.text.length; this.render(); } }else{ if(this.isFocus){ this.selected = false; this.isFocus = false; this.render(); } } } this.handleOnKeyUp = function(e){ this.isCommandKey = false; this.render(); } this.handleOnKeyDown = function(e){ if(e.key === "Meta" || e.key === "Control"){ this.isCommandKey = true; } if(this.isFocus){ e.preventDefault(); } if(this.isCommandKey && e.key === "a"){ this.selected = true; this.render(); return } if(this.isFocus && e.key === "Backspace"){ if(this.selected){ this.focusIndex = 0; this.text = ""; this.selected = false; this.render(); } var str = ""; for(var i =0; i < this.text.length; i++){ if(i !== this.focusIndex - 1){ str += this.text[i]; } } this.text = str; this.focusIndex --; if(this.focusIndex <0){ this.focusIndex = 0; } this.render(); } if(this.isFocus && e.key === "ArrowLeft"){ this.focusIndex --; if(this.focusIndex < 0){ this.focusIndex = 0; } this.render(); } if(this.isFocus && e.key === "ArrowRight"){ this.focusIndex ++; if(this.focusIndex > this.text.length){ this.focusIndex = this.text.length; } this.render(); } if(!this.isCommandKey && this.isFocus && (e.keyCode == 32 || (e.keyCode >= 65))){ this.text += e.key; this.focusIndex = this.text.length; this.render(); } } } var input = new Input("I 'm an input"); input.render(); window.addEventListener("click", function(event){ input.handleOnClick(event); }); window.addEventListener("keydown", function(event){ input.handleOnKeyDown(event); }); window.addEventListener("keyup", function(event){ input.handleOnKeyUp(event); }); </script> </body> </html> 

4 Comments

Welcome to SO, please add provide some explanation to your answer that simply describe how your solution solves the problem and avoid posting code only answers.
:) i noted that
@Toan, is it allowed to reuse your code example above in an CC0 way?
@Simeon it's ok

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.