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

Community
  • 1
  • 1
asagura
  • 141
  • 1
  • 1
  • 8

3 Answers3

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>
markE
  • 102,905
  • 11
  • 164
  • 176
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.

Mohsen
  • 64,437
  • 34
  • 159
  • 186
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>
mukund patel
  • 1,039
  • 10
  • 31
Toan
  • 79
  • 1
  • 4