Wrong rectangle size in canvas - html

I'm implementing a color picker. There is problem with the rendering. When I call c.fillRect(0, 0, 100, 80); the size of that rectangle is 103x42 px instead of 100x80. What is wrong here?
Also, rectangles are antialiased. Do I need offset the position by (0.5, 0.5) to avoid AA? I didn't use any kind of the coordinate system transformations.
colorSlider = function($e, color) {
this._$canvas = $('<canvas></canvas>');
this._c = this._$canvas[0].getContext('2d');
this._color = color || { r: 0, g: 0, b: 0 };
this._$canvas.width('310px');
this._$canvas.height('80px');
$e.append(this._$canvas);
this._render();
var me = this;
this._$canvas.mousedown(function(e) { me._mouseDown.call(me, e) });
this._$canvas.mouseup(function(e) { me._mouseUp.call(me, e) });
this._$canvas.mousemove(function(e) { me._mouseMove.call(me, e) });
this._dragChannel = 0;
}
colorSlider.prototype._pointInRect = function(x, y, rect) {
return x >= rect.x && x <= rect.x + rect.w && y >= rect.y && y <= rect.y + rect.h;
}
colorSlider.prototype._findTarget = function(event) {
var x = event.offsetX;
var y = event.offsetY;
console.log(x, y, this._rectR);
if (this._pointInRect(x, y, this._rectRThumb)) {
return { target: 1, value: x - this._rectR.x };
}
if (this._pointInRect(x, y, this._rectGThumb)) {
return { target: 2, value: x - this._rectG.x };
}
if (this._pointInRect(x, y, this._rectBThumb)) {
return { target: 3, value: x - this._rectB.x };
}
if (this._pointInRect(x, y, this._rectR)) {
return { target: 4, value: x - this._rectR.x };
}
if (this._pointInRect(x, y, this._rectG)) {
return { target: 5, value: x - this._rectG.x };
}
if (this._pointInRect(x, y, this._rectB)) {
return { target: 6, value: x - this._rectB.x };
}
return null;
}
colorSlider.prototype._mouseDown = function(event) {
this._dragChannel = 0;
var target = this._findTarget(event);
if (target) {
switch (target.target) {
case 1:
this._dragChannel = 1;
break;
case 2:
this._dragChannel = 2;
break;
case 3:
this._dragChannel = 3;
break;
case 4:
this._color.r = target.value;
break;
case 5:
this._color.g = target.value;
break;
case 6:
this._color.b = target.value;
break;
}
this._render();
}
};
colorSlider.prototype._mouseUp = function(event) {
//console.log('mouseUp');
};
colorSlider.prototype._mouseMove = function(event) {
//console.log('mouseMove', event);
};
colorSlider.prototype.padding = 4;
colorSlider.prototype._render = function() {
var padding = this.padding;
var thickness = 16;
var c = this._c;
var w = 255;
var h = this._$canvas.height();
c.clearRect(0, 0, this._$canvas.width(), this._$canvas.height());
var gradient = c.createLinearGradient(padding, 0, w, 0);
c.fillStyle = gradient;
gradient.addColorStop(0, this.colorToHex({ r: 0, g: this._color.g, b: this._color.b }));
gradient.addColorStop(1, this.colorToHex({ r: 255, g: this._color.g, b: this._color.b }));
c.fillRect(padding, padding, w, thickness);
c.lineWidth = 0;
c.fillRect(0, 0, 100, 80);
this._rectR = { x: padding, y: padding, w: w, h: thickness };
gradient = c.createLinearGradient(padding, 0, w, 0);
c.fillStyle = gradient;
gradient.addColorStop(0, this.colorToHex({ r: this._color.r, g: 0, b: this._color.b }));
gradient.addColorStop(1, this.colorToHex({ r: this._color.r, g: 255, b: this._color.b }));
c.fillRect(padding, padding + thickness + 2 * padding, w, thickness);
this._rectG = { x: padding, y: padding + thickness + 2 * padding, w: w, h: thickness };
gradient = c.createLinearGradient(padding, 0, w, 0);
c.fillStyle = gradient;
gradient.addColorStop(0, this.colorToHex({ r: this._color.r, g: this._color.g, b: 0 }));
gradient.addColorStop(1, this.colorToHex({ r: this._color.r, g: this._color.g, b: 255 }));
c.fillRect(padding, padding + 2 * (thickness + 2 * padding), w, thickness);
this._rectB = { x: padding, y: padding + 2 * (thickness + 2 * padding), w: w, h: thickness };
c.lineWidth = 2;
c.fillStyle = "white";
c.strokeStyle = "#888888";
this._rectRThumb = { x: padding + this._color.r - 2, y: padding / 2, w: 8, h: 20, r: 2 };
this.drawRoundedRectangle(c, this._rectRThumb);
this._rectGThumb = { x: padding + this._color.g - 2, y: padding / 2 + 2 * padding + thickness, w: 8, h: 20, r: 2 };
this.drawRoundedRectangle(c, this._rectGThumb);
this._rectBThumb = { x: padding + this._color.b - 2, y: padding / 2 + 2 * (2 * padding + thickness), w: 8, h: 20, r: 2 };
this.drawRoundedRectangle(c, this._rectBThumb);
};
colorSlider.prototype.colorToHex = function(color) {
var c = '#'
+ (color.r + 256).toString(16).substr(1, 2)
+ (color.g + 256).toString(16).substr(1, 2)
+ (color.b + 256).toString(16).substr(1, 2);
console.log(c);
return c;
};
// http://stackoverflow.com/questions/1255512/how-to-draw-a-rounded-rectangle-on-html-canvas
colorSlider.prototype.drawRoundedRectangle = function(c, rect) {
var x = rect.x;
var y = rect.y;
var width = rect.w;
var height = rect.h;
var radius = rect.r;
c.beginPath();
c.moveTo(x + radius, y);
c.lineTo(x + width - radius, y);
c.quadraticCurveTo(x + width, y, x + width, y + radius);
c.lineTo(x + width, y + height - radius);
c.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
c.lineTo(x + radius, y + height);
c.quadraticCurveTo(x, y + height, x, y + height - radius);
c.lineTo(x, y + radius);
c.quadraticCurveTo(x, y, x + radius, y);
c.closePath();
c.stroke();
c.fill();
};
index.html
<script>
$(function() {
$("#directionalLight,#ambientLight").each(function() {
new colorSlider($(this));
});
});
</script>
<body>
<div>Directional light</div>
<div id="directionalLight"></div>
<div>Ambient light</div>
<div id="ambientLight"></div>
</body>

The first thing to know is that a canvas element has intrinsic dimensions = number of pixels in the inside coordinate space (set by the width and height attributes and properties). It also has extrinsic dimensions (style.width and style.height) which is the number of pixels that the image takes within the webpage. The intrinsic pixels are scaled to fit the extrinsic space.
It's confusing because an img also has intrinsic and extrinsic dimensions, but the names of the properties are completely different from canvas. If you set width and height on an image, it's basically the same as setting style.width or style.height; they both set the extrinsic dimensions to scale the image within the page. Meanwhile, you can only get the intrinsic dimensions of an img using the new naturalWidth and naturalHeight (HTML5 browsers only) properties.
If the extrinsic dimensions are not set on both img and canvas, the image will be laid out at the same size as the intrinsic dimensions (i.e., scale factor would be 1).
Now, when you use jQuery, $(canvas).width('310px') is the same as $(canvas).css('310px'), which sets the extrinsic dimensions. You have to call $(canvas).prop('width', 310) or simply set canvas.width = 310 to set the intrinsic width.

Related

I am trying to set this HTML code to a chromebook background. Is this possible?

I am trying to set this HTML code to a Chromebook background. Is this possible? I also don't want to have to use any extensions.
<style>*{
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
html, body{
height: 100%;
margin: 0;
padding: 0;
background-color: #333;
overflow: hidden;
}
canvas{
background-color: #000;
}</style>
<div class="container">
<div class="row">
<canvas id="nokey" width="800" height="800">
</canvas>
</div>
</div>
<script>
var canvas = document.getElementById('nokey'),
can_w = parseInt(canvas.getAttribute('width')),
can_h = parseInt(canvas.getAttribute('height')),
ctx = canvas.getContext('2d');
// console.log(typeof can_w);
var ball = {
x: 0,
y: 0,
vx: 0,
vy: 0,
r: 0,
alpha: 1,
phase: 0
},
ball_color = {
r: 207,
g: 255,
b: 4
},
R = 2,
balls = [],
alpha_f = 0.03,
alpha_phase = 0,
// Line
link_line_width = 0.8,
dis_limit = 260,
add_mouse_point = true,
mouse_in = false,
mouse_ball = {
x: 0,
y: 0,
vx: 0,
vy: 0,
r: 0,
type: 'mouse'
};
// Random speed
function getRandomSpeed(pos){
var min = -1,
max = 1;
switch(pos){
case 'top':
return [randomNumFrom(min, max), randomNumFrom(0.1, max)];
break;
case 'right':
return [randomNumFrom(min, -0.1), randomNumFrom(min, max)];
break;
case 'bottom':
return [randomNumFrom(min, max), randomNumFrom(min, -0.1)];
break;
case 'left':
return [randomNumFrom(0.1, max), randomNumFrom(min, max)];
break;
default:
return;
break;
}
}
function randomArrayItem(arr){
return arr[Math.floor(Math.random() * arr.length)];
}
function randomNumFrom(min, max){
return Math.random()*(max - min) + min;
}
console.log(randomNumFrom(0, 10));
// Random Ball
function getRandomBall(){
var pos = randomArrayItem(['top', 'right', 'bottom', 'left']);
switch(pos){
case 'top':
return {
x: randomSidePos(can_w),
y: -R,
vx: getRandomSpeed('top')[0],
vy: getRandomSpeed('top')[1],
r: R,
alpha: 1,
phase: randomNumFrom(0, 10)
}
break;
case 'right':
return {
x: can_w + R,
y: randomSidePos(can_h),
vx: getRandomSpeed('right')[0],
vy: getRandomSpeed('right')[1],
r: R,
alpha: 1,
phase: randomNumFrom(0, 10)
}
break;
case 'bottom':
return {
x: randomSidePos(can_w),
y: can_h + R,
vx: getRandomSpeed('bottom')[0],
vy: getRandomSpeed('bottom')[1],
r: R,
alpha: 1,
phase: randomNumFrom(0, 10)
}
break;
case 'left':
return {
x: -R,
y: randomSidePos(can_h),
vx: getRandomSpeed('left')[0],
vy: getRandomSpeed('left')[1],
r: R,
alpha: 1,
phase: randomNumFrom(0, 10)
}
break;
}
}
function randomSidePos(length){
return Math.ceil(Math.random() * length);
}
// Draw Ball
function renderBalls(){
Array.prototype.forEach.call(balls, function(b){
if(!b.hasOwnProperty('type')){
ctx.fillStyle = 'rgba('+ball_color.r+','+ball_color.g+','+ball_color.b+','+b.alpha+')';
ctx.beginPath();
ctx.arc(b.x, b.y, R, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill();
}
});
}
// Update balls
function updateBalls(){
var new_balls = [];
Array.prototype.forEach.call(balls, function(b){
b.x += b.vx;
b.y += b.vy;
if(b.x > -(50) && b.x < (can_w+50) && b.y > -(50) && b.y < (can_h+50)){
new_balls.push(b);
}
// alpha change
b.phase += alpha_f;
b.alpha = Math.abs(Math.cos(b.phase));
// console.log(b.alpha);
});
balls = new_balls.slice(0);
}
// loop alpha
function loopAlphaInf(){
}
// Draw lines
function renderLines(){
var fraction, alpha;
for (var i = 0; i < balls.length; i++) {
for (var j = i + 1; j < balls.length; j++) {
fraction = getDisOf(balls[i], balls[j]) / dis_limit;
if(fraction < 1){
alpha = (1 - fraction).toString();
ctx.strokeStyle = 'rgba(150,150,150,'+alpha+')';
ctx.lineWidth = link_line_width;
ctx.beginPath();
ctx.moveTo(balls[i].x, balls[i].y);
ctx.lineTo(balls[j].x, balls[j].y);
ctx.stroke();
ctx.closePath();
}
}
}
}
// calculate distance between two points
function getDisOf(b1, b2){
var delta_x = Math.abs(b1.x - b2.x),
delta_y = Math.abs(b1.y - b2.y);
return Math.sqrt(delta_x*delta_x + delta_y*delta_y);
}
// add balls if there a little balls
function addBallIfy(){
if(balls.length < 20){
balls.push(getRandomBall());
}
}
// Render
function render(){
ctx.clearRect(0, 0, can_w, can_h);
renderBalls();
renderLines();
updateBalls();
addBallIfy();
window.requestAnimationFrame(render);
}
// Init Balls
function initBalls(num){
for(var i = 1; i <= num; i++){
balls.push({
x: randomSidePos(can_w),
y: randomSidePos(can_h),
vx: getRandomSpeed('top')[0],
vy: getRandomSpeed('top')[1],
r: R,
alpha: 1,
phase: randomNumFrom(0, 10)
});
}
}
// Init Canvas
function initCanvas(){
canvas.setAttribute('width', window.innerWidth);
canvas.setAttribute('height', window.innerHeight);
can_w = parseInt(canvas.getAttribute('width'));
can_h = parseInt(canvas.getAttribute('height'));
}
window.addEventListener('resize', function(e){
console.log('Window Resize...');
initCanvas();
});
function goMovie(){
initCanvas();
initBalls(20);
window.requestAnimationFrame(render);
}
goMovie();
// Mouse effect
canvas.addEventListener('mouseenter', function(){
console.log('mouseenter');
mouse_in = true;
balls.push(mouse_ball);
});
canvas.addEventListener('mouseleave', function(){
console.log('mouseleave');
mouse_in = false;
var new_balls = [];
Array.prototype.forEach.call(balls, function(b){
if(!b.hasOwnProperty('type')){
new_balls.push(b);
}
});
balls = new_balls.slice(0);
});
canvas.addEventListener('mousemove', function(e){
var e = e || window.event;
mouse_ball.x = e.pageX;
mouse_ball.y = e.pageY;
// console.log(mouse_ball);
});
</script>
Since you said, "Chromebook." There's no native support for it as you're asking; without extension.
According to Google's Chromebook Help, they state only .png & .jpg backgrounds.
If you haven't yet, download an image (.png or .jpg) from the web that
you’d like as your wallpaper.
Also, referencing the crosexperts website, it states:
there’s no native support for moving wallpapers, we had to create an
engine — and editor — to make this possible. But, amazingly, it is
possible.
Chrome OS doesn’t support Live Wallpapers in any way.

find coordinates to draw square inside circle

How to compute the starting co-ordinates to draw a square inside a cirle?
Function Draws the circular spectrum .
Now help me to find the starting coordinates to draw the rectangle inside the circle
Gradient.prototype.renderSpectrum = function() {
var radius = this.width / 2;
var toRad = (2 * Math.PI) / 360;
var step = 1 / radius;
this.ctx.clearRect(0, 0, this.width, this.height);
for(var i = 0; i < 360; i += step) {
var rad = i * toRad;
this.ctx.strokeStyle = 'hsl(' + i + ', 100%, 50%)';
this.ctx.beginPath();
this.ctx.moveTo(radius, radius);
this.ctx.lineTo(radius + radius * Math.cos(rad), radius + radius * Math.sin(rad));
this.ctx.stroke();
}
this.ctx.fillStyle = 'rgb(255, 255, 255)';
this.ctx.beginPath();
this.ctx.arc(radius, radius, radius * 0.8, 0, Math.PI * 2, true);
this.ctx.closePath();
return this.ctx.fill();
}
Function to draw the square
Gradient.prototype.renderGradient = function() {
var color, colors, gradient, index, xy, _i, _len, _ref, _ref1;
xy = arguments[0], colors = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
gradient = (_ref = this.ctx).createLinearGradient.apply(_ref, [0, 0].concat(__slice.call(xy)));
gradient.addColorStop(0, (_ref1 = colors.shift()) != null ? _ref1.toString() : void 0);
for (index = _i = 0, _len = colors.length; _i < _len; index = ++_i) {
color = colors[index];
gradient.addColorStop(index + 1 / colors.length, color.toString());
}
this.ctx.fillStyle = gradient;
this.renderSpectrum();
return this.ctx.fillRect(?, ?, this.width * 0.8, this.height * 0.8);
};
To fit a square inside a circle you can use something like this (adopt as needed):
Live example
/**
* ctx - context
* cx/cy - center of circle
* radius - radius of circle
*/
function squareInCircle(ctx, cx, cy, radius) {
var side = Math.sqrt(radius * radius * 2), // calc side length of square
half = side * 0.5; // position offset
ctx.strokeRect(cx - half, cy - half, side, side);
}
Just replace strokeRect() with fillRect().
Which will result in this (circle added for reference):
Adopting it for general usage:
function getSquareInCircle(cx, cy, radius) {
var side = Math.sqrt(radius * radius * 2), // calc side length of square
half = side * 0.5; // position offset
return {
x: cx - half,
y: cy - half,
w: side,
h: side
}
}
Then in your method:
Gradient.prototype.renderGradient = function() {
var color, colors, gradient, index, xy, _i, _len, _ref, _ref1;
xy = arguments[0], colors = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
gradient = (_ref = this.ctx).createLinearGradient.apply(_ref, [0, 0].concat(__slice.call(xy)));
gradient.addColorStop(0, (_ref1 = colors.shift()) != null ? _ref1.toString() : void 0);
for (index = _i = 0, _len = colors.length; _i < _len; index = ++_i) {
color = colors[index];
gradient.addColorStop(index + 1 / colors.length, color.toString());
}
this.ctx.fillStyle = gradient;
this.renderSpectrum();
// supply the proper position/radius here:
var square = getSquareInCircle(centerX, centerY, radius);
return this.ctx.fillRect(square.x, square.y, square.w, square.h);
};
Here is how I computed the co ordinates to draw square inside a circle
1)Get the coordinates of a inner circle at 135 degree
using the formula
x = rad + rad * Math.cos(135 * ( 2 Math.PI / 360);
y = rad - rad * Math.sin(135 * ( 2 Math.PI / 360);
2) then pyhthogoram therom to find the width if the square
width = Math.sqrt(rad * rad / 2);

How to make a pill shape in Canvas? (rounded corner rect basically)

Playing with a breakout clone and wanted to make rounded corner powerups.
Can someone point me in the right direction?
Thanks!
A simple way is to use quadraticCurveTo to smooth out corners
let roundRect = (ctx, x0, y0, x1, y1, r, color) => {
var w = x1 - x0;
var h = y1 - y0;
if (r > w/2) r = w/2;
if (r > h/2) r = h/2;
ctx.beginPath();
ctx.moveTo(x1 - r, y0);
ctx.quadraticCurveTo(x1, y0, x1, y0 + r);
ctx.lineTo(x1, y1-r);
ctx.quadraticCurveTo(x1, y1, x1 - r, y1);
ctx.lineTo(x0 + r, y1);
ctx.quadraticCurveTo(x0, y1, x0, y1 - r);
ctx.lineTo(x0, y0 + r);
ctx.quadraticCurveTo(x0, y0, x0 + r, y0);
ctx.closePath();
ctx.fillStyle = color;
ctx.fill();
},
ctx = document.getElementById("canvas").getContext("2d");
roundRect(ctx, 50, 50, 150, 100, 5, "#F00");
roundRect(ctx, 50, 110, 100, 160, 10, "#00F");
roundRect(ctx, 53, 113, 97, 157, 7, "#0F0");
<canvas id="canvas" width="200" height="180"></canvas>
You can do what's shown on this article by Juan Mendes:
HTML:
<canvas id="rounded-rect" width="600" height="600">
<!-- Insert fallback content here -->
</canvas>​
JavaScript:
CanvasRenderingContext2D.prototype.roundRect = function(x, y, width, height, radius, fill, stroke) {
if (typeof stroke == "undefined") {
stroke = true;
}
if (typeof radius === "undefined") {
radius = 5;
}
this.beginPath();
this.moveTo(x + radius, y);
this.lineTo(x + width - radius, y);
this.quadraticCurveTo(x + width, y, x + width, y + radius);
this.lineTo(x + width, y + height - radius);
this.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
this.lineTo(x + radius, y + height);
this.quadraticCurveTo(x, y + height, x, y + height - radius);
this.lineTo(x, y + radius);
this.quadraticCurveTo(x, y, x + radius, y);
this.closePath();
if (stroke) {
this.stroke(stroke);
}
if (fill) {
this.fill(fill);
}
};
// Now you can just call
var ctx = document.getElementById("rounded-rect").getContext("2d");
// Manipulate it again
ctx.strokeStyle = "#2d6";
ctx.fillStyle = "#abc";
ctx.roundRect(100, 200, 200, 100, 50, true);
As you can see on this JsFiddle

is it possible to draw text decoration (underline, etc.) with HTML5 Canvas Text API?

I am using the HTML5 canvas API to display some string (canvas.fillText), and I was wondering whether text-decoration (like underline, strikethrough, etc.) was something possible with the canvas API. Unfortunately, I found nothing about this.
The only solution I found was to manually do the decoration using the canvas drawing API (I mean, explicitly drawing a horizontal line, for example, to mimic the 'underline' decoration).
Is this possible using the canvas text API?
It won't work with a built-in method, but here is a simplified function I used successfully based on reading, "HTML5 Canvas: Text underline workaround" on the ScriptStock website.
var underline = function(ctx, text, x, y, size, color, thickness ,offset){
var width = ctx.measureText(text).width;
switch(ctx.textAlign){
case "center":
x -= (width/2); break;
case "right":
x -= width; break;
}
y += size+offset;
ctx.beginPath();
ctx.strokeStyle = color;
ctx.lineWidth = thickness;
ctx.moveTo(x,y);
ctx.lineTo(x+width,y);
ctx.stroke();
}
You can do this by using measureText and fillRect like so:
ctx.fillText(text, xPos, yPos);
let { width } = ctx.measureText("Hello World");
ctx.fillRect(xPos, yPos, width, 2);
The only difficult part about this approach is there is no way to obtain the height use measureText. Otherwise, you could use that as your Y coordinate when drawing your fillRect.
Your Y position will only depend on the height of your text and how close you'd like the underline.
Demo in Stack Snippets
// get canvas / context
var can = document.getElementById('my-canvas');
var ctx = can.getContext('2d')
let xPos=10, yPos=15;
let text = "Hello World"
ctx.fillText(text, xPos, yPos);
let { width } = ctx.measureText("Hello World");
ctx.fillRect(xPos, yPos, width, 2);
<canvas id="my-canvas" width="250" height="150"></canvas>
I created an alternative version of Mulhoon's code. I also take into account the text baseline.
const underline = (ctx, text, x, y) => {
let metrics = measureText(ctx, text)
let fontSize = Math.floor(metrics.actualHeight * 1.4) // 140% the height
switch (ctx.textAlign) {
case "center" : x -= (metrics.width / 2) ; break
case "right" : x -= metrics.width ; break
}
switch (ctx.textBaseline) {
case "top" : y += (fontSize) ; break
case "middle" : y += (fontSize / 2) ; break
}
ctx.save()
ctx.beginPath()
ctx.strokeStyle = ctx.fillStyle
ctx.lineWidth = Math.ceil(fontSize * 0.08)
ctx.moveTo(x, y)
ctx.lineTo(x + metrics.width, y)
ctx.stroke()
ctx.restore()
}
Full Example
const triggerEvent = (el, eventName) => {
var event = document.createEvent('HTMLEvents')
event.initEvent(eventName, true, false)
el.dispatchEvent(event)
}
const measureText = (ctx, text) => {
let metrics = ctx.measureText(text)
return {
width: Math.floor(metrics.width),
height: Math.floor(metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent),
actualHeight: Math.floor(metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent)
}
}
const underline = (ctx, text, x, y) => {
let metrics = measureText(ctx, text)
let fontSize = Math.floor(metrics.actualHeight * 1.4) // 140% the height
switch (ctx.textAlign) {
case "center" : x -= (metrics.width / 2) ; break
case "right" : x -= metrics.width ; break
}
switch (ctx.textBaseline) {
case "top" : y += (fontSize) ; break
case "middle" : y += (fontSize / 2) ; break
}
ctx.save()
ctx.beginPath()
ctx.strokeStyle = ctx.fillStyle
ctx.lineWidth = Math.ceil(fontSize * 0.08)
ctx.moveTo(x, y)
ctx.lineTo(x + metrics.width, y)
ctx.stroke()
ctx.restore()
}
const getOrigin = (ctx) => ({
x : Math.floor(ctx.canvas.width / 2),
y : Math.floor(ctx.canvas.height / 2)
})
const redraw = (ctx, sampleText, fontSize) => {
let origin = getOrigin(ctx)
ctx.font = fontSize + 'px Arial'
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
renderText(ctx, sampleText, origin.x, origin.y, 'Yellow', 'left', 'top')
renderText(ctx, sampleText, origin.x, origin.y, 'SkyBlue ', 'right', 'bottom')
renderText(ctx, sampleText, origin.x, origin.y, 'Tomato', 'left', 'bottom')
renderText(ctx, sampleText, origin.x, origin.y, 'Chartreuse ', 'right', 'top')
renderText(ctx, sampleText, origin.x, origin.y, 'Black', 'center', 'middle')
}
const renderText = (ctx, text, x, y, fillStyle, textAlign, textBaseLine) => {
ctx.fillStyle = fillStyle
ctx.textAlign = textAlign
ctx.textBaseline = textBaseLine
ctx.fillText(text, x, y)
underline(ctx, text, x, y)
}
const sampleText = 'Hello World'
const fontSizes = [ 8, 12, 16, 24, 32 ]
document.addEventListener('DOMContentLoaded', () => {
let ctx = document.querySelector('#demo').getContext('2d')
let sel = document.querySelector('select[name="font-size"]')
fontSizes.forEach(fontSize => sel.appendChild(new Option(fontSize, fontSize)))
sel.addEventListener('change', (e) => redraw(ctx, sampleText, sel.value))
sel.value = fontSizes[fontSizes.length - 1]
triggerEvent(sel, 'change')
})
canvas { border: thin solid grey }
label { font-weight: bold }
label::after { content: ": " }
<canvas id="demo" width="360" height="120"></canvas>
<form>
<label for="font-size-select">Font Size</label>
<select id="font-size-select" name="font-size"></select>
</form>
To add an underline to your canvas text, simply add underline characters at the same (x,y) position as your text.
e.g. you want to underline abc
context.fillText("abc",x,y);
context.fillText ("___",x,y);
Similarly for strike through, you would use the "-" character rather than underline.
I'm sorry to say that the answer is 'no'. There are no 'text-decoration' or similar styles available in the text methods of the HTML Canvas Context.

How to draw a rounded rectangle using HTML Canvas?

HTML Canvas provides methods for drawing rectangles, fillRect() and strokeRect(), but I can't find a method for making rectangles with rounded corners. How can I do that?
Nowadays you can just use context.roundRect. See further details on Kaiido's answer
var ctx = document.getElementById("rounded-rect").getContext("2d");
ctx.beginPath();
// Draw using 5px for border radius on all sides
// stroke it but no fill
ctx.roundRect(5, 5, 50, 50, 5);
ctx.stroke();
// To change the color on the rectangle, just manipulate the context
ctx.strokeStyle = "rgb(255, 0, 0)";
ctx.fillStyle = "rgba(255, 255, 0, .5)";
ctx.beginPath();
ctx.roundRect(100, 5, 100, 100, 20);
ctx.stroke();
ctx.fill();
// Manipulate it again
ctx.strokeStyle = "#0f0";
ctx.fillStyle = "#ddd";
// Different radii for each corner, top-left clockwise to bottom-left
ctx.beginPath();
ctx.roundRect(300, 5, 200, 100, [50,0,25,0]);
ctx.fill();
ctx.stroke();
<canvas id="rounded-rect" width="500" height="200">
<!-- Insert fallback content here -->
</canvas>
Old answer:
I needed to do the same thing and created a method to do it.
/**
* Draws a rounded rectangle using the current state of the canvas.
* If you omit the last three params, it will draw a rectangle
* outline with a 5 pixel border radius
* #param {CanvasRenderingContext2D} ctx
* #param {Number} x The top left x coordinate
* #param {Number} y The top left y coordinate
* #param {Number} width The width of the rectangle
* #param {Number} height The height of the rectangle
* #param {Number} [radius = 5] The corner radius; It can also be an object
* to specify different radii for corners
* #param {Number} [radius.tl = 0] Top left
* #param {Number} [radius.tr = 0] Top right
* #param {Number} [radius.br = 0] Bottom right
* #param {Number} [radius.bl = 0] Bottom left
* #param {Boolean} [fill = false] Whether to fill the rectangle.
* #param {Boolean} [stroke = true] Whether to stroke the rectangle.
*/
function roundRect(
ctx,
x,
y,
width,
height,
radius = 5,
fill = false,
stroke = true
) {
if (typeof radius === 'number') {
radius = {tl: radius, tr: radius, br: radius, bl: radius};
} else {
radius = {...{tl: 0, tr: 0, br: 0, bl: 0}, ...radius};
}
ctx.beginPath();
ctx.moveTo(x + radius.tl, y);
ctx.lineTo(x + width - radius.tr, y);
ctx.quadraticCurveTo(x + width, y, x + width, y + radius.tr);
ctx.lineTo(x + width, y + height - radius.br);
ctx.quadraticCurveTo(x + width, y + height, x + width - radius.br, y + height);
ctx.lineTo(x + radius.bl, y + height);
ctx.quadraticCurveTo(x, y + height, x, y + height - radius.bl);
ctx.lineTo(x, y + radius.tl);
ctx.quadraticCurveTo(x, y, x + radius.tl, y);
ctx.closePath();
if (fill) {
ctx.fill();
}
if (stroke) {
ctx.stroke();
}
}
// Now you can just call
var ctx = document.getElementById("rounded-rect").getContext("2d");
// Draw using default border radius,
// stroke it but no fill (function's default values)
roundRect(ctx, 5, 5, 50, 50);
// To change the color on the rectangle, just manipulate the context
ctx.strokeStyle = "rgb(255, 0, 0)";
ctx.fillStyle = "rgba(255, 255, 0, .5)";
roundRect(ctx, 100, 5, 100, 100, 20, true);
// Manipulate it again
ctx.strokeStyle = "#0f0";
ctx.fillStyle = "#ddd";
// Different radii for each corner, others default to 0
roundRect(ctx, 300, 5, 200, 100, {
tl: 50,
br: 25
}, true);
<canvas id="rounded-rect" width="500" height="200">
<!-- Insert fallback content here -->
</canvas>
Different radii per corner provided by Corgalore
See http://js-bits.blogspot.com/2010/07/canvas-rounded-corner-rectangles.html
for further explanation
I started with #jhoff's solution, but rewrote it to use width/height parameters, and using arcTo makes it quite a bit more terse:
CanvasRenderingContext2D.prototype.roundRect = function (x, y, w, h, r) {
if (w < 2 * r) r = w / 2;
if (h < 2 * r) r = h / 2;
this.beginPath();
this.moveTo(x+r, y);
this.arcTo(x+w, y, x+w, y+h, r);
this.arcTo(x+w, y+h, x, y+h, r);
this.arcTo(x, y+h, x, y, r);
this.arcTo(x, y, x+w, y, r);
this.closePath();
return this;
}
Also returning the context so you can chain a little. E.g.:
ctx.roundRect(35, 10, 225, 110, 20).stroke(); //or .fill() for a filled rect
The HTML5 canvas doesn't provide a method to draw a rectangle with rounded corners.
How about using the lineTo() and arc() methods?
You can also use the quadraticCurveTo() method instead of the arc() method.
Juan, I made a slight improvement to your method to allow for changing each rectangle corner radius individually:
/**
* Draws a rounded rectangle using the current state of the canvas.
* If you omit the last three params, it will draw a rectangle
* outline with a 5 pixel border radius
* #param {Number} x The top left x coordinate
* #param {Number} y The top left y coordinate
* #param {Number} width The width of the rectangle
* #param {Number} height The height of the rectangle
* #param {Object} radius All corner radii. Defaults to 0,0,0,0;
* #param {Boolean} fill Whether to fill the rectangle. Defaults to false.
* #param {Boolean} stroke Whether to stroke the rectangle. Defaults to true.
*/
CanvasRenderingContext2D.prototype.roundRect = function (x, y, width, height, radius, fill, stroke) {
var cornerRadius = { upperLeft: 0, upperRight: 0, lowerLeft: 0, lowerRight: 0 };
if (typeof stroke == "undefined") {
stroke = true;
}
if (typeof radius === "object") {
for (var side in radius) {
cornerRadius[side] = radius[side];
}
}
this.beginPath();
this.moveTo(x + cornerRadius.upperLeft, y);
this.lineTo(x + width - cornerRadius.upperRight, y);
this.quadraticCurveTo(x + width, y, x + width, y + cornerRadius.upperRight);
this.lineTo(x + width, y + height - cornerRadius.lowerRight);
this.quadraticCurveTo(x + width, y + height, x + width - cornerRadius.lowerRight, y + height);
this.lineTo(x + cornerRadius.lowerLeft, y + height);
this.quadraticCurveTo(x, y + height, x, y + height - cornerRadius.lowerLeft);
this.lineTo(x, y + cornerRadius.upperLeft);
this.quadraticCurveTo(x, y, x + cornerRadius.upperLeft, y);
this.closePath();
if (stroke) {
this.stroke();
}
if (fill) {
this.fill();
}
}
Use it like this:
var canvas = document.getElementById("canvas");
var c = canvas.getContext("2d");
c.fillStyle = "blue";
c.roundRect(50, 100, 50, 100, {upperLeft:10,upperRight:10}, true, true);
Good news everyone!
roundRect(x, y, width, height, radii); is now officially part of the Canvas 2D API.
It is exposed on CanvasRenderingContext2D, Path2D and OffscreenCanvasRenderingContext2D objects.
Its radii parameter is an Array which contains either
a single float, representing the radius to use for all four corners,
two floats, for the top-left + bottom-right and top-right + bottom-left corners respectively,
three floats, for the top-left, top-right + bottom-left and bottom-right respectively,
or four floats, one per corner,
OR the same combinations, but with a DOMPointInit object, representing the x-radius and y-radius of each corner.
Currently, only Chrome has an implementation available, but you can find a polyfill I made, in this repo.
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
ctx.roundRect(20,20,80,80,[new DOMPoint(60,80), new DOMPoint(110,100)]);
ctx.strokeStyle = "green";
ctx.stroke();
const path = new Path2D();
path.roundRect(120,30,60,90,[0,25,new DOMPoint(60,80), new DOMPoint(110,100)]);
ctx.fillStyle = "purple";
ctx.fill(path);
// and a simple one
ctx.beginPath();
ctx.roundRect(200,20,80,80,[10]);
ctx.fillStyle = "orange";
ctx.fill();
<script src="https://cdn.jsdelivr.net/gh/Kaiido/roundRect#main/roundRect.js"></script>
<canvas></canvas>
This code creates a 100-pixel square, with rounded corners of 30 pixels.
var canvas = document.createElement("canvas");
document.body.appendChild(canvas);
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(100,100);
ctx.arcTo(0,100,0,0,30);
ctx.arcTo(0,0,100,0,30);
ctx.arcTo(100,0,100,100,30);
ctx.arcTo(100,100,0,100,30);
ctx.fill();
The drawPolygon function below can be used to draw any polygon with rounded corners.
See it running here.
function drawPolygon(ctx, pts, radius) {
if (radius > 0) {
pts = getRoundedPoints(pts, radius);
}
var i, pt, len = pts.length;
ctx.beginPath();
for (i = 0; i < len; i++) {
pt = pts[i];
if (i == 0) {
ctx.moveTo(pt[0], pt[1]);
} else {
ctx.lineTo(pt[0], pt[1]);
}
if (radius > 0) {
ctx.quadraticCurveTo(pt[2], pt[3], pt[4], pt[5]);
}
}
ctx.closePath();
}
function getRoundedPoints(pts, radius) {
var i1, i2, i3, p1, p2, p3, prevPt, nextPt,
len = pts.length,
res = new Array(len);
for (i2 = 0; i2 < len; i2++) {
i1 = i2-1;
i3 = i2+1;
if (i1 < 0) {
i1 = len - 1;
}
if (i3 == len) {
i3 = 0;
}
p1 = pts[i1];
p2 = pts[i2];
p3 = pts[i3];
prevPt = getRoundedPoint(p1[0], p1[1], p2[0], p2[1], radius, false);
nextPt = getRoundedPoint(p2[0], p2[1], p3[0], p3[1], radius, true);
res[i2] = [prevPt[0], prevPt[1], p2[0], p2[1], nextPt[0], nextPt[1]];
}
return res;
};
function getRoundedPoint(x1, y1, x2, y2, radius, first) {
var total = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)),
idx = first ? radius / total : (total - radius) / total;
return [x1 + (idx * (x2 - x1)), y1 + (idx * (y2 - y1))];
};
The function receives an array with the polygon points, like this:
var canvas = document.getElementById("cv");
var ctx = canvas.getContext("2d");
ctx.strokeStyle = "#000000";
ctx.lineWidth = 5;
drawPolygon(ctx, [[20, 20],
[120, 20],
[120, 120],
[ 20, 120]], 10);
ctx.stroke();
This is a port and a more generic version of a solution posted here.
Here's one I wrote... uses arcs instead of quadratic curves for better control over radius. Also, it leaves the stroking and filling up to you
/* Canvas 2d context - roundRect
*
* Accepts 5 parameters:
the start_x,
start_y points,
the end_x,
end_y points,
the radius of the corners
*
* No return value
*/
CanvasRenderingContext2D.prototype.roundRect = function(sx,sy,ex,ey,r) {
var r2d = Math.PI/180;
if( ( ex - sx ) - ( 2 * r ) < 0 ) { r = ( ( ex - sx ) / 2 ); } //ensure that the radius isn't too large for x
if( ( ey - sy ) - ( 2 * r ) < 0 ) { r = ( ( ey - sy ) / 2 ); } //ensure that the radius isn't too large for y
this.beginPath();
this.moveTo(sx+r,sy);
this.lineTo(ex-r,sy);
this.arc(ex-r,sy+r,r,r2d*270,r2d*360,false);
this.lineTo(ex,ey-r);
this.arc(ex-r,ey-r,r,r2d*0,r2d*90,false);
this.lineTo(sx+r,ey);
this.arc(sx+r,ey-r,r,r2d*90,r2d*180,false);
this.lineTo(sx,sy+r);
this.arc(sx+r,sy+r,r,r2d*180,r2d*270,false);
this.closePath();
}
Here is an example:
var _e = document.getElementById('#my_canvas');
var _cxt = _e.getContext("2d");
_cxt.roundRect(35,10,260,120,20);
_cxt.strokeStyle = "#000";
_cxt.stroke();
So this is based out of using lineJoin="round" and with the proper proportions, mathematics and logic I have been able to make this function, this is not perfect but hope it helps. If you want to make each corner have a different radius take a look at: https://p5js.org/reference/#/p5/rect
Here ya go:
CanvasRenderingContext2D.prototype.roundRect = function (x,y,width,height,radius) {
radius = Math.min(Math.max(width-1,1),Math.max(height-1,1),radius);
var rectX = x;
var rectY = y;
var rectWidth = width;
var rectHeight = height;
var cornerRadius = radius;
this.lineJoin = "round";
this.lineWidth = cornerRadius;
this.strokeRect(rectX+(cornerRadius/2), rectY+(cornerRadius/2), rectWidth-cornerRadius, rectHeight-cornerRadius);
this.fillRect(rectX+(cornerRadius/2), rectY+(cornerRadius/2), rectWidth-cornerRadius, rectHeight-cornerRadius);
this.stroke();
this.fill();
}
CanvasRenderingContext2D.prototype.roundRect = function (x,y,width,height,radius) {
radius = Math.min(Math.max(width-1,1),Math.max(height-1,1),radius);
var rectX = x;
var rectY = y;
var rectWidth = width;
var rectHeight = height;
var cornerRadius = radius;
this.lineJoin = "round";
this.lineWidth = cornerRadius;
this.strokeRect(rectX+(cornerRadius/2), rectY+(cornerRadius/2), rectWidth-cornerRadius, rectHeight-cornerRadius);
this.fillRect(rectX+(cornerRadius/2), rectY+(cornerRadius/2), rectWidth-cornerRadius, rectHeight-cornerRadius);
this.stroke();
this.fill();
}
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext('2d');
function yop() {
ctx.clearRect(0,0,1000,1000)
ctx.fillStyle = "#ff0000";
ctx.strokeStyle = "#ff0000"; ctx.roundRect(Number(document.getElementById("myRange1").value),Number(document.getElementById("myRange2").value),Number(document.getElementById("myRange3").value),Number(document.getElementById("myRange4").value),Number(document.getElementById("myRange5").value));
requestAnimationFrame(yop);
}
requestAnimationFrame(yop);
<input type="range" min="0" max="1000" value="10" class="slider" id="myRange1"><input type="range" min="0" max="1000" value="10" class="slider" id="myRange2"><input type="range" min="0" max="1000" value="200" class="slider" id="myRange3"><input type="range" min="0" max="1000" value="100" class="slider" id="myRange4"><input type="range" min="1" max="1000" value="50" class="slider" id="myRange5">
<canvas id="myCanvas" width="1000" height="1000">
</canvas>
Here's a solution using the lineJoin property to round the corners. It works if you just need a solid shape, but not so much if you need a thin border that's smaller than the border radius.
function roundedRect(ctx, options) {
ctx.strokeStyle = options.color;
ctx.fillStyle = options.color;
ctx.lineJoin = "round";
ctx.lineWidth = options.radius;
ctx.strokeRect(
options.x+(options.radius*.5),
options.y+(options.radius*.5),
options.width-options.radius,
options.height-options.radius
);
ctx.fillRect(
options.x+(options.radius*.5),
options.y+(options.radius*.5),
options.width-options.radius,
options.height-options.radius
);
ctx.stroke();
ctx.fill();
}
const canvas = document.getElementsByTagName("canvas")[0];
const ctx = canvas.getContext("2d");
roundedRect(ctx, {
x: 10,
y: 10,
width: 200,
height: 100,
radius: 35,
color: "red"
});
<canvas></canvas>
Opera, ffs.
if (window["CanvasRenderingContext2D"]) {
/** #expose */
CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r) {
if (w < 2*r) r = w/2;
if (h < 2*r) r = h/2;
this.beginPath();
if (r < 1) {
this.rect(x, y, w, h);
} else {
if (window["opera"]) {
this.moveTo(x+r, y);
this.arcTo(x+r, y, x, y+r, r);
this.lineTo(x, y+h-r);
this.arcTo(x, y+h-r, x+r, y+h, r);
this.lineTo(x+w-r, y+h);
this.arcTo(x+w-r, y+h, x+w, y+h-r, r);
this.lineTo(x+w, y+r);
this.arcTo(x+w, y+r, x+w-r, y, r);
} else {
this.moveTo(x+r, y);
this.arcTo(x+w, y, x+w, y+h, r);
this.arcTo(x+w, y+h, x, y+h, r);
this.arcTo(x, y+h, x, y, r);
this.arcTo(x, y, x+w, y, r);
}
}
this.closePath();
};
/** #expose */
CanvasRenderingContext2D.prototype.fillRoundRect = function(x, y, w, h, r) {
this.roundRect(x, y, w, h, r);
this.fill();
};
/** #expose */
CanvasRenderingContext2D.prototype.strokeRoundRect = function(x, y, w, h, r) {
this.roundRect(x, y, w, h, r);
this.stroke();
};
}
Since Opera is going WebKit, this should also remain valid in the legacy case.
To make the function more consistent with the normal means of using a canvas context, the canvas context class can be extended to include a 'fillRoundedRect' method -- that can be called in the same way fillRect is called:
var canv = document.createElement("canvas");
var cctx = canv.getContext("2d");
// If thie canvasContext class doesn't have a fillRoundedRect, extend it now
if (!cctx.constructor.prototype.fillRoundedRect) {
// Extend the canvaseContext class with a fillRoundedRect method
cctx.constructor.prototype.fillRoundedRect =
function (xx,yy, ww,hh, rad, fill, stroke) {
if (typeof(rad) == "undefined") rad = 5;
this.beginPath();
this.moveTo(xx+rad, yy);
this.arcTo(xx+ww, yy, xx+ww, yy+hh, rad);
this.arcTo(xx+ww, yy+hh, xx, yy+hh, rad);
this.arcTo(xx, yy+hh, xx, yy, rad);
this.arcTo(xx, yy, xx+ww, yy, rad);
if (stroke) this.stroke(); // Default to no stroke
if (fill || typeof(fill)=="undefined") this.fill(); // Default to fill
}; // end of fillRoundedRect method
}
The code checks to see if the prototype for the constructor for the canvas context object contains a 'fillRoundedRect' property and adds one -- the first time around. It is invoked in the same manner as the fillRect method:
ctx.fillStyle = "#eef"; ctx.strokeStyle = "#ddf";
// ctx.fillRect(10,10, 200,100);
ctx.fillRoundedRect(10,10, 200,100, 5);
The method uses the arcTo method as Grumdring did. In the method, this is a reference to the ctx object. The stroke argument defaults to false if undefined. The fill argument defaults to fill the rectangle if undefined.
(Tested on Firefox, I don't know if all implementations permit extension in this manner.)
Method 1: Using path-drawing methods
The most straightforward method of doing this with HTML Canvas is by using the path-drawing methods of ctx:
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
function roundedRect(ctx, x, y, width, height, radius) {
ctx.beginPath();
ctx.moveTo(x + radius, y);
ctx.lineTo(x + width - radius, y);
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
ctx.lineTo(x + width, y + height - radius);
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
ctx.lineTo(x + radius, y + height);
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
ctx.lineTo(x, y + radius);
ctx.quadraticCurveTo(x, y, x + radius, y);
ctx.closePath();
}
ctx.fillStyle = "red";
roundedRect(ctx, 10, 10, 100, 100, 20);
ctx.fill();
<canvas id="canvas">
<!-- Fallback content -->
</canvas>
Method 2: Using Path2D
You can also draw rounded rectangles in HTML Canvas by using the Path2D interface:
Example 1
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
function roundedRect(x, y, width, height, radius) {
return new Path2D(`M ${x + radius} ${y} H ${x + width - radius} a ${radius} ${radius} 0 0 1 ${radius} ${radius} V ${y + height - radius} a ${radius} ${radius} 0 0 1 ${-radius} ${radius} H ${x + radius} a ${radius} ${radius} 0 0 1 ${-radius} ${-radius} V ${y + radius} a ${radius} ${radius} 0 0 1 ${radius} ${-radius}`);
}
ctx.fillStyle = "blue";
ctx.fill(roundedRect(10, 10, 100, 100, 20));
<canvas id="canvas">
<!-- Fallback content -->
</canvas>
Example 2
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
function roundedRect(x, y, width, height, radius) {
let path = new Path2D();
path.moveTo(x + radius, y);
path.lineTo(x + width - radius, y);
path.quadraticCurveTo(x + width, y, x + width, y + radius);
path.lineTo(x + width, y + height - radius);
path.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
path.lineTo(x + radius, y + height);
path.quadraticCurveTo(x, y + height, x, y + height - radius);
path.lineTo(x, y + radius);
path.quadraticCurveTo(x, y, x + radius, y);
path.closePath();
return path;
}
ctx.fillStyle = "green";
ctx.fill(roundedRect(10, 10, 100, 100, 20));
<canvas id="canvas">
<!-- Fallback content -->
</canvas>
try to add this line , when you want to get rounded corners : ctx.lineCap = "round";
NONE of the other answers can handle the following 3 cases correctly:
if ((width >= radius x 2) && (height <= radius * 2))
if ((width <= radius x 2) && (height >= radius * 2))
if ((width <= radius x 2) && (height <= radius * 2))
If any of these cases happen, you will not get a correctly drawn rectangle
My Solution handles ANY radius and ANY Width and Height dynamically, and should be the default answer
function roundRect(ctx, x, y, width, height, radius) {
/*
* Draws a rounded rectangle using the current state of the canvas.
*/
let w = width;
let h = height;
let r = radius;
ctx.stroke()
ctx.fill()
ctx.beginPath();
// Configure the roundedness of the rectangles corners
if ((w >= r * 2) && (h >= r * 2)) {
// Handles width and height larger than diameter
// Keep radius fixed
ctx.moveTo(x + r, y); // tr start
ctx.lineTo(x + w - r, y); // tr
ctx.quadraticCurveTo(x + w, y, x + w, y + r); //tr
ctx.lineTo(x + w, y + h - r); // br
ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h); // br
ctx.lineTo(x + r, y + h); // bl
ctx.quadraticCurveTo(x, y + h, x, y + h - r); // bl
ctx.lineTo(x, y + r); // tl
ctx.quadraticCurveTo(x, y, x + r, y); // tl
} else if ((w < r * 2) && (h > r * 2)) {
// Handles width lower than diameter
// Radius must dynamically change as half of width
r = w / 2;
ctx.moveTo(x + w, y + h - r); // br start
ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h); // br curve
ctx.quadraticCurveTo(x, y + h, x, y + h - r) // bl curve
ctx.lineTo(x, y + r); // line
ctx.quadraticCurveTo(x, y, x + r, y); // tl
ctx.quadraticCurveTo(x + w, y, x + w, y + r); // tl
ctx.lineTo(x + w, y + h - r); // line
} else if ((w > r * 2) && (h < r * 2)) {
// Handles height lower than diameter
// Radius must dynamically change as half of height
r = h / 2;
ctx.moveTo(x + w - r, y + h); // br start
ctx.quadraticCurveTo(x + w, y + h, x + w, y + r); // br curve
ctx.quadraticCurveTo(x + w, y, x + w - r, y); // tr curve
ctx.lineTo(x + r, y); // line between tr tl
ctx.quadraticCurveTo(x, y, x, y + r); // tl curve
ctx.quadraticCurveTo(x, y + h, x + r, y + h); // bl curve
} else if ((w < 2 * r) && (h < 2 * r)) {
// Handles width and height lower than diameter
ctx.moveTo(x + w / 2, y + h);
ctx.quadraticCurveTo(x + w, y + h, x + w, y + h / 2); // bl curve
ctx.quadraticCurveTo(x + w, y, x + w / 2, y); // tr curve
ctx.quadraticCurveTo(x, y, x, y + h / 2); // tl curve
ctx.quadraticCurveTo(x, y + h, x + w / 2, y + h); // bl curve
}
ctx.closePath();
}