Draw text on top of rectangle - html

I'm trying to draw some text in the corner of a rectangle but I just started off drawing text on a rectangle before tackling the positioning. I can't seem to draw a rectangle, fill it with a color and then draw text over it. Even if i draw the text first, then rectangle, then fill in those orders, the rectangle just seems to overlap the text.
This code will show text and rectangles with no fill
context.beginPath();
for (var i = 0; i < 8; i++) {
context.font = "18pt Arial";
context.fillText("blah", 0, 0 + (i * 50));
context.rect(30, 0 + (i * 50), 50, 50);
}
context.lineWidth = 0.1;
context.strokeStyle = "black";
context.stroke();
This code will show me the text and filled in rectangles but the text seems to appear below the rectangles.
context.beginPath();
for (var i = 0; i < 8; i++) {
context.font = "18pt Arial";
context.fillText("blah", 0, 0 + (i * 50));
context.rect(30, 0 + (i * 50), 50, 50);
}
context.fillStyle = "#33cc00";
context.fill();
context.lineWidth = 0.1;
context.strokeStyle = "black";
context.stroke();
Any ideas what I'm doing wrong?

Every paint operation on an HTML5 Canvas draws on top of and (in general) obliterates whatever was underneath. If you want text to draw on top of the rectangles, you must call fillText() after you call fill() for the rects you have created.
It doesn't matter what order drawing commands are added to the path, it is when the fill() occurs that determines when the instantly-drying ink is applied. Since you do this after all your fillText() calls, the rectangles are drawn on top.
I would modify your code like this:
context.font = "18pt Arial";
context.strokeStyle = "#000";
context.lineWidth = 0.1;
for (var i=0; i<8; i++) {
context.fillStyle = "#3c0";
context.fillRect(30, 0 + (i * 50), 50, 50);
context.strokeRect(30, 0 + (i * 50), 50, 50);
context.fillStyle = "#fff";
context.fillText("blah", 0, 0 + (i * 50));
}
Alternatively, if you don't want to use fillRect()/strokeRect():
context.font = "18pt Arial";
context.strokeStyle = "#000";
context.lineWidth = 0.1;
for (var i=0; i<8; i++) {
context.beginPath();
context.fillStyle = "#3c0";
context.rect(30, 0 + (i * 50), 50, 50);
context.fill();
context.stroke();
context.fillStyle = "#fff";
context.fillText("blah", 0, 0 + (i * 50));
}

Related

Create pattern with canvas

I would like to create a pettern with canvas. The Picture which should be used should also be gernerated first. I already did something like this with this code:
document.addEventListener('DOMContentLoaded', function () {
async function draw() {
var canvas = document.getElementById('canvas1')
var ctx = canvas.getContext("2d");
var canvas = ctx.createImageData(500, 300);
ctx.fillStyle = "#7289DA";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Select the color of the stroke
ctx.strokeStyle = '#74037b';
// Draw a rectangle with the dimensions of the entire canvas
ctx.strokeRect(0, 0, canvas.width, canvas.height);
ctx.font = 'bold 70px sans-serif';
// Select the style that will be used to fill the text in
ctx.save();
ctx.rotate(1.7*Math.PI);
ctx.fillStyle = '#23272A';
ctx.fillText('Text', -70, 300);
ctx.restore();
// Actually fill the text with a solid color
}
draw();
});
<canvas id="canvas" width="1500" height="900">Beispiel für eine Kachelung eines Musters in Canvas.</canvas>
Now I want to create some kind of grid with it, it should look like this
How can I do that?
The best way would be using two for loops to go over the x and y values! You can surround the part that draws text with these loops and use the changing x and y values instead of hard-coded ones.
async function draw() {
var canvas = document.getElementById('canvas1')
var ctx = canvas.getContext("2d");
var canvas = ctx.createImageData(500, 300);
ctx.fillStyle = "#7289DA";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Select the color of the stroke
ctx.strokeStyle = '#74037b';
// Draw a rectangle with the dimensions of the entire canvas
ctx.strokeRect(0, 0, canvas.width, canvas.height);
ctx.font = 'bold 70px sans-serif';
ctx.fillStyle = '#23272A';
// Select the style that will be used to fill the text in
for (var x = 0; x < canvas.width; x += 100 ) { // 100 is the width
for (var y = 0; y < canvas.height; y += 70) { // 70 is the height
ctx.save();
ctx.translate(x, y); // offset the text
ctx.rotate(1.7*Math.PI);
ctx.fillText('Text', -70, 300);
ctx.restore();
// Actually fill the text with a solid color
}
}
}
The reason ctx.translate(x, y) is used instead of ctx.fillText('Text', x - 70, y + 300) is because using fillText would move the grid at an angle instead of just rotating the letters.

Pulse animation in canvas

I am trying to make various shapes have a pulse like effect in canvas and managed to do it with a circle,
function drawCircle() {
// color in the background
context.fillStyle = "#EEEEEE";
context.fillRect(0, 0, canvas.width, canvas.height);
// draw the circle
context.beginPath();
var radius = 25 + 20 * Math.abs(Math.cos(angle)); //radius of circle
context.arc(25, 25, radius, 0, Math.PI * 2, false); //position on canvas
context.closePath();
// color in the circle
context.fillStyle = "#006699";
context.fill();
//'pulse'
angle += Math.PI / 220;
requestAnimationFrame(drawCircle);
}
drawCircle();
but I'm not sure how to go about doing any other shape. What I have so far for my triangle is
function drawTriangle() {
// draw the triangle
context.beginPath();
context.moveTo(75, 50);
context.lineTo(100, 75);
context.lineTo(100, 25);
context.fill();
context.rect(215, 100, Math.PI * 2, false); //position on canvas
context.closePath();
// color in the triangle
context.fillStyle = "#3f007f";
context.fill();
//'pulse'
angle += Math.PI / 280;
requestAnimationFrame(drawTriangle);
}
drawTriangle();
Any insight would be appreciated.
This can be simply achieved by changing the scale of the context matrix.
All you need to find is the position of the scaling anchor of your shape so that you can translate the matrix to the correct position after the scale has been applied.
In following example, I'll use the center of the shape as scaling anchor, since it seems it is what you wanted.
The extended version of the matrix transformations would be
ctx.translate(anchorX, anchorY);
ctx.scale(scaleFactor, scaleFactor);
ctx.translate(-anchorX, -anchorY);
which in below example has been reduced to
ctx.setTransform(
scale, 0, 0,
scale, anchorX - (anchorX * scale), anchorY - (anchorY * scale)
);
var ctx = canvas.getContext('2d');
var angle = 0;
var scale = 1;
var img = new Image();
img.src = 'https://dl.dropboxusercontent.com/s/4e90e48s5vtmfbd/aaa.png';
anim();
function anim() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
updateScale();
drawCircle();
drawTriangle();
drawImage();
ctx.setTransform(1, 0, 0, 1, 0, 0);
requestAnimationFrame(anim);
}
function updateScale() {
angle += Math.PI / 220;
scale = 0.5 + Math.abs(Math.cos(angle));
}
function drawCircle() {
ctx.beginPath();
var cx = 75,
cy = 50,
radius = 25;
// for the circle, centerX and centerY are given
var anchorX = cx,
anchorY = cy;
// with these anchorX, anchorY and scale,
// we can determine where we need to translate our context once scaled
var scaledX = anchorX - (anchorX * scale),
scaledY = anchorY - (anchorY * scale);
// then we apply the matrix in one go
ctx.setTransform(scale, 0, 0, scale, scaledX, scaledY);
// and we draw normally
ctx.arc(cx, cy, radius, 0, Math.PI * 2);
ctx.fill();
}
function drawTriangle() {
ctx.beginPath();
// for the triangle, we need to find the position between minX and maxX,
// and between minY and maxY
var anchorX = 175 + (200 - 175) / 2,
anchorY = 25 + (75 - 25) / 2;
var scaledX = anchorX - (anchorX * scale),
scaledY = anchorY - (anchorY * scale);
ctx.setTransform(scale, 0, 0, scale, scaledX, scaledY);
ctx.moveTo(175, 50);
ctx.lineTo(200, 75);
ctx.lineTo(200, 25);
ctx.fill();
}
function drawImage() {
if (!img.naturalWidth) return;
// for rects, it's just pos + (length / 2)
var anchorX = 250 + img.naturalWidth / 2,
anchorY = 25 + img.naturalHeight / 2;
var scaledX = anchorX - (anchorX * scale),
scaledY = anchorY - (anchorY * scale);
ctx.setTransform(scale, 0, 0, scale, scaledX, scaledY);
ctx.drawImage(img, 250, 25);
}
<canvas id="canvas" width="500"></canvas>

HTML5 Canvas - cant apply source-atop on mask

Sorry I am new to Canvas and dont know how to google this out. Problem is that I cant draw on mask if previous layer (night sky) is present.
Here are the two snippets:
const canvas = document.querySelector('#board canvas');
const ctx = canvas.getContext('2d');
const { width: w, height: h } = canvas;
// first layer
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, w, h);
ctx.fillStyle = '#555';
let x, y, radius;
for (let i = 0; i < 550; i++) {
x = Math.random() * w;
y = Math.random() * h;
radius = Math.random() * 3;
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2, false);
ctx.fill();
}
// destination
ctx.font = 'bold 70pt monospace';
ctx.fillStyle = 'black';
ctx.fillText('FOO', 10, 60);
ctx.fillText('BAR', 10, 118);
ctx.fill();
// source
ctx.globalCompositeOperation = 'source-atop';
for (let i = 0; i < 6; i++) {
ctx.fillStyle = `hsl(${i * (250 / 6)}, 90%, 55%)`;
ctx.fillRect(0, i * 20, 200, 20);
}
<div id="board">
<canvas width="640" height="480"></canvas>
</div>
EXPECTED RESULT (but with the first layer - night sky):
const canvas = document.querySelector('#board canvas');
const ctx = canvas.getContext('2d');
const { width: w, height: h } = canvas;
// destination
ctx.font = 'bold 70pt monospace';
ctx.fillStyle = 'black';
ctx.fillText('FOO', 10, 60);
ctx.fillText('BAR', 10, 118);
ctx.fill();
// source
ctx.globalCompositeOperation = 'source-atop';
for (let i = 0; i < 6; i++) {
ctx.fillStyle = `hsl(${i * (250 / 6)}, 90%, 55%)`;
ctx.fillRect(0, i * 20, 200, 20);
}
<div id="board">
<canvas width="640" height="480"></canvas>
</div>
Compositing will affect the whole context.
source-atop mode will draw only where there were existing pixels (i.e only where alpha > 0).
When you draw your background, all the pixels of your context have alpha values set to 1.
This means that source-atop will not produce anything on your fully opaque image.
Once you understand these points, it's clear that you need to make your compositing alone.
It could be e.g on a different off-screen canvas that you would then draw back on the main canvas with ctx.drawImage(canvas, x, y).
const canvas = document.querySelector('#board canvas');
const ctx = canvas.getContext('2d');
const {
width: w,
height: h
} = canvas;
// background
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, w, h);
ctx.fillStyle = '#555';
let x, y, radius;
for (let i = 0; i < 550; i++) {
x = Math.random() * w;
y = Math.random() * h;
radius = Math.random() * 3;
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2, false);
ctx.fill();
}
// text compositing on an off-screen context
const ctx2 = Object.assign(document.createElement('canvas'), {
width: 200,
height: 120
}).getContext('2d');
// text
ctx2.font = 'bold 70pt monospace';
ctx2.fillStyle = 'black';
ctx2.fillText('FOO', 10, 60);
ctx2.fillText('BAR', 10, 118);
ctx2.globalCompositeOperation = 'source-atop';
// rainbow
for (let i = 0; i < 6; i++) {
ctx2.fillStyle = `hsl(${i * (250 / 6)}, 90%, 55%)`;
ctx2.fillRect(0, i * 20, 200, 20);
}
// now draw our off-screen canvas on the main one
ctx.drawImage(ctx2.canvas, 0, 0);
<div id="board">
<canvas width="640" height="480"></canvas>
</div>
Or, since this is the only compositing in your composition, you can also do it all on the same, but use an other compositing mode: destination-over.
This mode will draw behind the existing content, this means that you will have to actually draw your background after you made the compositing.
const canvas = document.querySelector('#board canvas');
const ctx = canvas.getContext('2d');
const {
width: w,
height: h
} = canvas;
//
// text compositing on a clear context
drawText();
// will draw only where the text has been drawn
ctx.globalCompositeOperation = 'source-atop';
drawRainbow();
// from here we will draw behind
ctx.globalCompositeOperation = 'destination-over';
// so we need to first draw the stars, otherwise they'll be behind
drawStars();
//And finally the sky black background
drawSky();
//... reset
ctx.globalCompositeOperation = 'source-over';
function drawSky() {
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, w, h);
}
function drawStars() {
ctx.fillStyle = '#555';
let x, y, radius;
for (let i = 0; i < 550; i++) {
x = Math.random() * w;
y = Math.random() * h;
radius = Math.random() * 3;
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2, false);
ctx.fill();
}
}
function drawText()  {
ctx.font = 'bold 70pt monospace';
ctx.fillStyle = 'black';
ctx.fillText('FOO', 10, 60);
ctx.fillText('BAR', 10, 118);
}
function drawRainbow() {
for (let i = 0; i < 6; i++) {
ctx.fillStyle = `hsl(${i * (250 / 6)}, 90%, 55%)`;
ctx.fillRect(0, i * 20, 200, 20);
}
}
<div id="board">
<canvas width="640" height="480"></canvas>
</div>

In canvas, how to draw 2 semi-transparent overlapping circles

I want to draw two circles on the gray rectangle in HTML canvas.
I attempted the following steps:
Fill rectangle gray
Change globalComposition lighter to mix two colors.
I want to mix only blue and red, not gray and gray rectangle.
You can easily achieve this property by fill style of the circles in rgba, where the last parameter will be alpha or opacity. The syntax will be something like this
circle.fillStyle = "rgba(255, 255, 255, 0.5)";
//can be used to fill as red object with opacity of 0.5
The complete code to achieve the desired effect will be something like this.
// JavaScript Code
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var centerleftX = canvas.width / 4;
var centerRightX = 3 * canvas.width / 4;
var centerY = canvas.height / 2;
var radius = 70;
context.beginPath();
context.rect(0, 0, 400, 200);
context.fillStyle = 'rgb(200,200,200)';
context.fill();
context.lineWidth = 7;
context.strokeStyle = 'black';
context.stroke();
context.beginPath();
context.arc(centerleftX + 50, centerY, radius, 0, 2 * Math.PI, false);
context.fillStyle = "rgba(0, 0,255,0.7)";
context.fill();
context.beginPath();
context.arc(centerRightX - 50, centerY, radius, 0, 2 * Math.PI, false);
context.fillStyle = "rgba(255, 0,0,0.7)";
context.fill();
<!-- HTML Code -->
<canvas id="myCanvas" width="400" height="200">

Animate a Fill Circle using Canvas

Basically I want to be able to Fill a Circle using canvas, but it animate to a certain percentage.
I.e only have the circle fill up 80% of the way.
My canvas knowledge isn't amazing, Here is an image i made in photoshop to display what i want.
I want the circle to start empty and then Fill up to say 70% of the circle.
Is this possible with Canvas, if so? can anyone shed some light on how to do it?
Here is a fiddle of what I've managed
http://jsfiddle.net/6Vm67/
var canvas = document.getElementById('Circle');
var context = canvas.getContext('2d');
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var radius = 80;
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.fillStyle = '#13a8a4';
context.fill();
context.lineWidth = 10;
context.strokeStyle = '#ffffff';
context.stroke();
Any help would be massively appreciated
Clipping regions make this very easy. All you have to do is make a circular clipping region and then fill a rectangle of some size to get a "partial circle" worth of fill. Here's an example:
var canvas = document.getElementById('Circle');
var context = canvas.getContext('2d');
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var radius = 80;
var full = radius*2;
var amount = 0;
var amountToIncrease = 10;
function draw() {
context.save();
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.clip(); // Make a clipping region out of this path
// instead of filling the arc, we fill a variable-sized rectangle
// that is clipped to the arc
context.fillStyle = '#13a8a4';
// We want the rectangle to get progressively taller starting from the bottom
// There are two ways to do this:
// 1. Change the Y value and height every time
// 2. Using a negative height
// I'm lazy, so we're going with 2
context.fillRect(centerX - radius, centerY + radius, radius * 2, -amount);
context.restore(); // reset clipping region
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.lineWidth = 10;
context.strokeStyle = '#000000';
context.stroke();
// Every time, raise amount by some value:
amount += amountToIncrease;
if (amount > full) amount = 0; // restart
}
draw();
// Every second we'll fill more;
setInterval(draw, 1000);
http://jsfiddle.net/simonsarris/pby9r/
This is a little more dynamic, object-oriented version, so you can configure the options as the circle radius, border width, colors, duration and step of animation, you can also animate the circle to a certain percentage. It was quite fun to write this.
<canvas id="Circle" width="300" height="300"></canvas>
<script>
function Animation( opt ) {
var context = opt.canvas.getContext("2d");
var handle = 0;
var current = 0;
var percent = 0;
this.start = function( percentage ) {
percent = percentage;
// start the interval
handle = setInterval( draw, opt.interval );
}
// fill the background color
context.fillStyle = opt.backcolor;
context.fillRect( 0, 0, opt.width, opt.height );
// draw a circle
context.arc( opt.width / 2, opt.height / 2, opt.radius, 0, 2 * Math.PI, false );
context.lineWidth = opt.linewidth;
context.strokeStyle = opt.circlecolor;
context.stroke();
function draw() {
// make a circular clipping region
context.beginPath();
context.arc( opt.width / 2, opt.height / 2, opt.radius-(opt.linewidth/2), 0, 2 * Math.PI, false );
context.clip();
// draw the current rectangle
var height = ((100-current)*opt.radius*2)/100 + (opt.height-(opt.radius*2))/2;
context.fillStyle = opt.fillcolor;
context.fillRect( 0, height, opt.width, opt.radius*2 );
// clear the interval when the animation is over
if ( current < percent ) current+=opt.step;
else clearInterval(handle);
}
}
// create the new object, add options, and start the animation with desired percentage
var canvas = document.getElementById("Circle");
new Animation({
'canvas': canvas,
'width': canvas.width,
'height': canvas.height,
'radius': 100,
'linewidth': 10,
'interval': 20,
'step': 1,
'backcolor': '#666',
'circlecolor': '#fff',
'fillcolor': '#339999'
}).start( 70 );
</script>