So I trying to get build an arc with text on it as rounded rectangle in context 2d but the text is going under it and is getting hidden, also, the fill style for arc is getting overridden by the fill style for text. Here is what I am getting and what I want:
Here is the js fiddle with the code : https://jsfiddle.net/abhishek_soni/vzs6dLy9/19/
Here is the same code :
Html code :
<canvas></canvas>
Js code :
var roundRect = function(ctx, x, y, w, h, r) {
var x2 = x + w;
var y2 = y + h;
if (w < 2 * r) r = w / 2;
if (h < 2 * r) r = h / 2;
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.arcTo(x2, y, x2, y2, r);
ctx.arcTo(x2, y2, x, y2, r);
ctx.arcTo(x, y2, x, y, r);
ctx.arcTo(x, y, x2, y, r);
};
var canvas = document.querySelector('canvas');
var context = canvas.getContext('2d');
context.save();
roundRect(context, 0, 0, 50, 60, 20);
context.fillStyle = 'red'; // This should work, but it doesn't
context.fill();
context.restore();
context.fillStyle = 'green';
context.fillText('Hey', 50, 80) // if I change this to 50 it hides behind the arc, which I don't want.
context.textAlign = "center";
context.fill();
context.save();
roundRect(context, 100, 100, 50, 90, 20);
context.fillStyle = 'red'; // This should work, but it doesn't
context.fill();
context.restore();
context.fillStyle = 'green';
context.fillText('Hey', 90, 100) // if I change this to 50 it hides behind the arc, which I don't want.
context.textAlign = "center";
context.fill();
context.save();
roundRect(context, 300, 100, 50, 120, 20);
context.fillStyle = 'red'; // This should work, but it doesn't
context.fill();
context.restore();
context.fillStyle = 'green';
context.fillText('Hey', 90, 130) // if I change this to 50 it hides behind the arc, which I don't want.
context.textAlign = "center";
context.fill();
The problem is you are calling fill()
after you create the text which you don't need. That fill()
is being applied to the shape drawn previously. The purpose of methods like fillText()
and fillRect()
are to allow you to draw without the need to call fill()
after.
Don't call fill after the text and it works.
Next to align your text in the center of the shape I would calculate the center and place the text there and then use context.textAlign = 'center';
to center it.
Here's an example
var canvas = document.querySelector('canvas');
var context = canvas.getContext('2d');
canvas.width = innerWidth;
canvas.height = innerHeight;
let center = {}
var roundRect = function(ctx, x, y, w, h, r) {
var x2 = x + w;
var y2 = y + h;
center = {x: x + w/2, y: y + h/2}
if (w < 2 * r) r = w / 2;
if (h < 2 * r) r = h / 2;
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.arcTo(x2, y, x2, y2, r);
ctx.arcTo(x2, y2, x, y2, r);
ctx.arcTo(x, y2, x, y, r);
ctx.arcTo(x, y, x2, y, r);
ctx.closePath();
};
context.fillStyle = 'red';
roundRect(context, 0, 0, 50, 60, 20);
context.fill();
context.fillStyle = 'green';
context.textAlign = 'center';
context.fillText('Hey', center.x, center.y);
context.textAlign = "center";
context.fillStyle = 'red';
roundRect(context, 100, 100, 50, 90, 20);
context.fill();
context.fillStyle = 'green';
context.textAlign = 'center';
context.fillText('Hey', center.x, center.y)
context.textAlign = "center";
context.fillStyle = 'red';
roundRect(context, 300, 100, 50, 120, 20);
context.fill();
context.fillStyle = 'green';
context.textAlign = 'center';
context.fillText('Hey', center.x, center.y);
context.textAlign = "center";
<canvas id="canvas"></canvas>
I'm also going to add an example using a class to do this. I prefer this over the other method. If you don't need different color shapes you can get rid of the color argument. And if you want different color text you can add one for that.
var canvas = document.querySelector("canvas");
var ctx = canvas.getContext("2d");
canvas.width = innerWidth;
canvas.height = innerHeight;
class roundRect {
constructor(x, y, w, h, r, c) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.x2 = this.x + this.w;
this.y2 = this.y + this.h;
this.center = { x: x + w / 2, y: y + h / 2 };
this.r = r;
this.color = c;
}
drawShape() {
if (this.w < 2 * this.r) this.r = this.w / 2;
if (this.h < 2 * this.r) this.r = this.h / 2;
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.moveTo(this.x + this.r, this.y);
ctx.arcTo(this.x2, this.y, this.x2, this.y2, this.r);
ctx.arcTo(this.x2, this.y2, this.x, this.y2, this.r);
ctx.arcTo(this.x, this.y2, this.x, this.y, this.r);
ctx.arcTo(this.x, this.y, this.x2, this.y, this.r);
ctx.closePath();
ctx.fill();
}
drawText() {
ctx.fillStyle = 'green';
ctx.textAlign = "center";
ctx.textBaseline = 'middle';
ctx.fillText("Hey", this.center.x, this.center.y);
}
}
let rect1 = new roundRect(0, 0, 50, 60, 20, 'red', 'Text')
let rect2 = new roundRect(60, 0, 50, 80, 20, 'lightblue', 'Text')
let rect3 = new roundRect(120, 0, 50, 100, 20, 'lightgreen', 'Text')
rect1.drawShape();
rect1.drawText();
rect2.drawShape();
rect2.drawText();
rect3.drawShape();
rect3.drawText();
<canvas id="canvas"></canvas>
EDIT:
To rotate your text use the following.
var canvas = document.querySelector('canvas');
var context = canvas.getContext('2d');
canvas.width = innerWidth;
canvas.height = innerHeight;
let center = {}
var roundRect = function(ctx, x, y, w, h, r) {
var x2 = x + w;
var y2 = y + h;
center = {x: x + w/2, y: y + h/2}
if (w < 2 * r) r = w / 2;
if (h < 2 * r) r = h / 2;
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.arcTo(x2, y, x2, y2, r);
ctx.arcTo(x2, y2, x, y2, r);
ctx.arcTo(x, y2, x, y, r);
ctx.arcTo(x, y, x2, y, r);
ctx.closePath();
};
context.fillStyle = 'red';
roundRect(context, 0, 0, 50, 60, 20);
context.fill();
context.fillStyle = 'green';
context.textAlign = 'center';
context.fillText('Hey', center.x, center.y);
context.textAlign = "center";
context.fillStyle = 'red';
roundRect(context, 100, 100, 50, 90, 20);
context.fill();
context.fillStyle = 'green';
context.textAlign = 'center';
context.textBaseline = 'middle';
context.save() //save before you transform something
context.translate(center.x, center.y) //use this to position
context.rotate(degToRad(90)) //rotate here using degrees
context.fillText('Hey', 0, 0) //draw this a (0, 0)
context.fillStyle = 'lightblue'; //delete this
context.fillRect(0, 0, canvas.width, canvas.height) //and this
context.restore() //restore when done with all objects you want affected
context.textAlign = "center";
context.fillStyle = 'red';
roundRect(context, 300, 100, 50, 120, 20);
context.fill();
context.fillStyle = 'green';
context.textAlign = 'center';
context.fillText('Hey', center.x, center.y);
context.textAlign = "center";
function degToRad(deg) {
return deg * (Math.PI/180)
}
<canvas id="canvas"></canvas>