-1

I am using fillRect to draw rectangular in canvas. I want to add text I want it to be editable like edit box. Is there a way to achieve this in JavaScript?

I don't want fillText because I want the text to be editable on canvas.

Gizele
  • 9
  • 8
  • you can add a textfield above the canvas.or you can simulate it.when focus show a box. – Madhawa Priyashantha Nov 25 '16 at 17:58
  • @FastSnail i want it to be inside the canvas and the editable text inside the fillRect . – Gizele Nov 25 '16 at 19:02
  • There is no method for doing this in the canvas. Edit text boxes are very complicated interfaces, they must support a wide range of languages, UI interfaces, spell checkers, cut and paste, OS specific features, support additions for people with disabilities. The list is long. Use a standard textinput element and just render the results onto the canvas when there is change. – Blindman67 Nov 25 '16 at 19:11
  • @Blindman67 could you give example of how to do that – Gizele Nov 25 '16 at 22:39
  • https://stackoverflow.com/questions/21011931/i-am-trying-to-edit-text-on-canvas/21011975#21011975 –  Nov 26 '16 at 03:10

2 Answers2

1

For sure! (Although it's tricky)

Thankfully someone has already done it and posted it in a Github repository: https://github.com/goldfire/CanvasInput

It is under the MIT Licence, so make sure to abide by its conditions!

1

It can be done, but don't!!!

This is the most basic of examples. I do not recommend that anyone do this unless it is for their own use, and that they have strict control over the browser and the browser version, localisation, and more. This will break for more reasons then there are lines of code.

canvasTextBox defines all that is needed. It creates a HTMLInputElement and then listens to the keyup and input events.

An animation loop checks for any change in state and renders the text if needed, in sync with all other DOM rendering.

I have added a basic blinking cursor, but no selection of text, no insert, overwrite modes, no writing direction (left to right only), no spell checking, no no, and no.

I have added no focus checking, no way to turn it off, there is no mouse interaction, and no context menu.

To implement just a basic usable public version using this method would need well over 1000 lines of code, and months of testing. Even then it will not always work as there will always be some special case that is unknowable from within the JavaScript context.

Canvas textbox

Example works on Chrome beta on a Win10 machine, may work on other browsers/OS but I have not bothered to check.

var canvas = document.createElement("canvas");
canvas.width = 400;
canvas.height = 200;
document.body.appendChild(canvas);
var ctx = canvas.getContext("2d");

var w = canvas.width;
var h = canvas.height;
var cw = w / 2;  // center 
var ch = h / 2;
var globalTime; // 



var canvasTextBox = {
    x : 10,
    y : 10,
    h : 50,
    w : 300,
    ready : false,
    font : "40px arial",
    fontCol : "blue",
    selectColour : "blue",
    background : "#CCC",
    border : "black 2px", // this is not a CSS style and will break if you dont have colour and width
    create : function(){
        var text = document.createElement("input")
        text.type = "text";
        text.style.position = "absolute";
        var bounds  = canvas.getBoundingClientRect();
        text.style.top = (bounds.top + this.x + 2) +  "px";
        text.style.left = (bounds.left + this.y +2 )+ "px";
        text.style.zIndex = -100;
        document.body.appendChild(text);
        this.textState.element = text;
        
        // get some events
        this.textState.events = (function(event){
            this.changed = true;
            this.text = this.element.value;
        }).bind(this.textState);
        
        // add a blink thing
        this.textState.blink = (function(){
            this.cursorOn = !this.cursorOn;
            this.changed = true;
            setTimeout(this.blink,this.cursorRate);
        }).bind(this.textState);
        
        // listen for changes
        text.addEventListener("input",this.textState.events);
        text.addEventListener("keyup",this.textState.events);
        this.ready = true;
        
    },
    render : function(){
        var start,end;
        ctx.font = this.font;
        ctx.fillStyle = "#AAA";
        ctx.strokeStyle = this.border.split(" ")[0];
        ctx.lineWidth = this.border.split(" ")[1].replace("px","");
        ctx.fillRect(this.x,this.y,this.w,this.h);
        ctx.strokeRect(this.x,this.y,this.w,this.h);
        ctx.fillStyle = this.fontCol;
        start = 0;
        end = 0;
        if(this.textState.element.selectionStart !== undefined){
            start = this.textState.element.selectionStart;
        }
        var text = this.textState.text;
        var textStart = text.substr(0,start);
        var w = ctx.measureText(text).width;
        var wStart = ctx.measureText(textStart).width;
        var cursor = this.x + wStart;
        ctx.save();
        ctx.beginPath();
        ctx.rect(this.x,this.y,this.w,this.h);
        ctx.clip();
        if(w > this.w){
            cursor = this.x + this.w - w + wStart;
            if(cursor < this.x){
                ctx.fillText(this.textState.text,this.x+this.w-w + (this.x - cursor)+3,this.y + 40);
                cursor = this.x;
            }else{
                ctx.fillText(this.textState.text,this.x+this.w-w,this.y + 40);
            }
        }else{
            ctx.fillText(this.textState.text,this.x,this.y + 40);
        }
        if(this.textState.cursorOn){
            ctx.fillStyle = "red";
            ctx.fillRect(cursor,this.y,3,this.h);
        }
        ctx.restore();
    },
    textState : {
        text : "",
        cursor : 0,
        cursorOn : false,
        cursorRate : 250,
        changed : true,
        events : null,
        element : null,
    },
    update : function(){
        if(this.textState.changed){
            this.textState.changed = false;
            this.render();
        }
    },
    focus : function(){
        this.textState.element.focus();
        this.textState.blink();
    },
}

canvasTextBox.create();
canvasTextBox.focus();

// main update function
function update(timer){
    
    globalTime = timer;
    if(canvasTextBox.ready){
        canvasTextBox.update();
    }
    requestAnimationFrame(update);
}
requestAnimationFrame(update);
Blindman67
  • 51,134
  • 11
  • 73
  • 136