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.
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.
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!
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);