Related
So I have wanted to make an ocean in the night with a canvas. I got the Wave part down but I want them to be filled. So that they are not just a lign anymore but a solid block locking like waves. I really like how they look right now but if you have any tweeks that could make them look even better feel free to share them!
Thanks for the Help in Advance!
var c = document.getElementById("screen2");
var ctx = c.getContext("2d");
var cw = c.width = window.innerWidth;
var ch = c.height = window.innerHeight;
var cx = cw / 2,
cy = ch / 2;
var rad = Math.PI / 180;
var w = window.innerWidth;
var h = 100;
var amplitude = h;
var frequency = .01;
var phi = 0;
var frames = 0;
var stopped = true;
//ctx.strokeStyle = "Cornsilk";
ctx.lineWidth = 4;
//first wave
function Draw() {
frames++
phi = frames / 88;
ctx.clearRect(0, 0, cw, ch);
ctx.beginPath();
ctx.moveTo(0, ch);
for (var x = 0; x < w; x++) {
y = Math.sin(x * frequency + phi) * amplitude/2 + amplitude /2;
//y = Math.cos(x * frequency + phi) * amplitude / 2 + amplitude / 2;
ctx.lineTo(x, y + ch -110); // setting it to the bottom of the page 100= lift
}
ctx.lineTo(w, ch);
ctx.lineTo(0, ch);
ctx.stroke();
requestId = window.requestAnimationFrame(Draw);
}
requestId = window.requestAnimationFrame(Draw);
//second wave
function Draw2() {
frames++
phi = frames / 72 ;
ctx.beginPath();
ctx.moveTo(0, ch);
for (var x = 0; x < w; x++) {
y = Math.sin(x * frequency + phi) * amplitude/4 + amplitude/4;
//y = Math.cos(x * frequency + phi) * amplitude / 2 + amplitude / 2;
ctx.lineTo(x, y + ch -110); // setting it to the bottom of the page 100= lift
}
ctx.lineTo(w, ch);
ctx.lineTo(0, ch);
ctx.stroke();
requestId = window.requestAnimationFrame(Draw2);
}
requestId = window.requestAnimationFrame(Draw2);
canvas {
display:block;
margin:0 auto;
width:100%;
height:100vh;
}
<canvas id="screen2"></canvas>
First and foremost what I really would change is using requestAnimationFrame two times as it's completely redundant. You can group your drawing operations in a single function and just execute this.
As #mousetail hinted a simple 'fix' would be using .fill() instead of .stroke(). The problem is that you would have two completely solid shapes of the same colour making it look like there's just a single wave. So the next step would be adding two different colours. As that might still look a little boring I'd use two linear gradients - a lighter for the foreground and a darker for the background wave.
To make it look even more interesting I'd move the waves up & down over time.
var c = document.getElementById("screen2");
var ctx = c.getContext("2d");
var cw = c.width = window.innerWidth;
var ch = c.height = window.innerHeight;
var cx = cw / 2,
cy = ch / 2;
var rad = Math.PI / 180;
var w = window.innerWidth;
var h = 100;
var amplitude = h;
var frequency = .01;
var phi = 0;
var frames = 0;
var stopped = true;
var gradientA = ctx.createLinearGradient(0, ch / 2, 0, ch);
gradientA.addColorStop(0, "#29c3d3");
gradientA.addColorStop(1, "#15656e");
var gradientB = ctx.createLinearGradient(0, ch / 2, 0, ch);
gradientB.addColorStop(0, "#55dee5");
gradientB.addColorStop(1, "#399499");
ctx.lineWidth = 4;
var step = 0;
function drawWaves() {
frames++;
phi = frames / 88;
ctx.clearRect(0, 0, cw, ch);
ctx.beginPath();
ctx.moveTo(0, ch);
for (var x = 0; x < w; x++) {
y = Math.sin(x * frequency + phi) * amplitude / 2 + amplitude / 2;
//y = Math.cos(x * frequency + phi) * amplitude / 2 + amplitude / 2;
ctx.lineTo(x, y + ch - 110 + Math.sin(step / 2) * 10); // setting it to the bottom of the page 100= lift
}
ctx.lineTo(w, ch);
ctx.lineTo(0, ch);
ctx.fillStyle = gradientA;
ctx.fill();
frames++;
phi = frames / 72;
ctx.beginPath();
ctx.moveTo(0, ch);
for (var x = 0; x < w; x++) {
y = Math.sin(x * frequency + phi) * amplitude / 4 + amplitude / 4;
//y = Math.cos(x * frequency + phi) * amplitude / 2 + amplitude / 2;
ctx.lineTo(x, y + ch - 110 + Math.sin(step) * 20); // setting it to the bottom of the page 100= lift
}
ctx.lineTo(w, ch);
ctx.lineTo(0, ch);
ctx.fillStyle = gradientB;
ctx.fill();
step += 0.02;
requestId = window.requestAnimationFrame(drawWaves);
}
requestId = window.requestAnimationFrame(drawWaves);
canvas {
display: block;
margin: 0 auto;
width: 100%;
height: 100vh;
}
<canvas id="screen2"></canvas>
I want to display a windmill on js canvas. For now, I display a green line on a cube. I have a problem with rotating the line around it's middle point. Also, I don't want my cube to move. Save() doesn't seem to work? I don't know what I'm doing wrong. I tried looking the answer online but they don't seem to work or I don't understand them. Elements in my canvas somehow disappear.
var x = 600;
function init()
{
window.requestAnimationFrame(draw);
}
function draw()
{
var ctx = document.getElementById('canvas').getContext('2d');
ctx.clearRect(0, 0, 600, 600);
// sciana przednia
ctx.lineWidth = 1;
ctx.strokeStyle = "#000000";
ctx.strokeRect(x/2,x/2,x/4,x/4);
//sciana gorna
ctx.beginPath();
ctx.lineWidth = 1;
ctx.strokeStyle = "#000000";
ctx.moveTo(x/2,x/2);
ctx.lineTo(x-x/3,x/4+x/6);
ctx.lineTo(x-x/8,x/4+x/6);
ctx.lineTo(x/2+x/4,x/2);
ctx.closePath();
ctx.stroke();
//sciana prawa
ctx.beginPath();
ctx.lineWidth = 1;
ctx.strokeStyle = "#000000";
ctx.moveTo(x/2+x/4,x/2+x/4);
ctx.lineTo(x-x/8,x/2+x/7);
ctx.lineTo(x-x/8,x/4+x/6);
ctx.stroke();
ctx.closePath();
//raczka
ctx.beginPath();
ctx.lineWidth = 5;
ctx.strokeStyle = "#808080";
ctx.moveTo(x/2+x/5,x/2-x/5);
ctx.lineTo(x/2+x/5,x/2-x/8+50);
ctx.stroke();
ctx.closePath();
ctx.save();
//smiglo
ctx.beginPath();
ctx.translate(x/2, x/2);
ctx.rotate( (Math.PI / 180) * 25);
ctx.translate(-x/2, -x/2);
ctx.fillStyle = "#00cc00";
ctx.fillRect(x/2+x/5-100,x/2-x/5,200,10);
ctx.closePath();
ctx.restore();
window.requestAnimationFrame(draw);
}
init();
var canvas = document.getElementById('canvas')
var ctx = canvas.getContext('2d');
var x = 600;
function init() {
window.requestAnimationFrame(draw);
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// sciana przednia
ctx.lineWidth = 1;
ctx.strokeStyle = "#000000";
ctx.strokeRect(x / 2, x / 2, x / 4, x / 4);
//sciana gorna
ctx.beginPath();
ctx.lineWidth = 1;
ctx.strokeStyle = "#000000";
ctx.moveTo(x / 2, x / 2);
ctx.lineTo(x - x / 3, x / 4 + x / 6);
ctx.lineTo(x - x / 8, x / 4 + x / 6);
ctx.lineTo(x / 2 + x / 4, x / 2);
ctx.closePath();
ctx.stroke();
//sciana prawa
ctx.beginPath();
ctx.lineWidth = 1;
ctx.strokeStyle = "#000000";
ctx.moveTo(x / 2 + x / 4, x / 2 + x / 4);
ctx.lineTo(x - x / 8, x / 2 + x / 7);
ctx.lineTo(x - x / 8, x / 4 + x / 6);
ctx.stroke();
ctx.closePath();
//raczka
ctx.beginPath();
ctx.lineWidth = 5;
ctx.strokeStyle = "#808080";
ctx.moveTo(x / 2 + x / 5, x / 2 - x / 5);
ctx.lineTo(x / 2 + x / 5, x / 2 - x / 8 + 50);
ctx.stroke();
ctx.closePath();
ctx.save();
//smiglo
ctx.beginPath();
ctx.translate(x / 2, x / 2);
ctx.rotate((Math.PI / 180) * 25);
ctx.translate(-x / 2, -x / 2);
ctx.fillStyle = "#00cc00";
ctx.fillRect(x / 2 + x / 5 - 100, x / 2 - x / 5, 200, 10);
ctx.closePath();
ctx.restore();
window.requestAnimationFrame(draw);
}
init();
<canvas id="canvas" width=600 height=600></canvas>
Edit: I understand now how to rotate around a certain point. I still don't know how to rotate only the line not the whole thing.
Talking about the rotation, I think that you did well: the green line is rotated by 25 degrees in your example. You just need to rotate its middle point.
But to do so, I think it's better if you make other changes in your code: the part that draws the cube is difficult to handle, whenever you want to edit your code, this part will cause issues. I suggest to isolate it in a drawCube() function and to use proper (x,y) coordinates.
According to me, it should look like this:
function draw() {
angle += 5;
ctx.save();
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(Math.PI / 180 * angle);
ctx.translate(-canvas.width / 2, -canvas.height / 2);
// It rotates
drawLine();
ctx.restore();
// It doesn't rotate
drawCube();
}
Then, your code will work. All you will have to do is to make the line rotate around its middle point, but you said that you know how to do it.
Good luck!
EDIT: I added a snippet with an working example, and with a cube like yours as well, may help.
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var btnExample = document.getElementById('btnExample');
var btnCube = document.getElementById('btnCube');
var angle = 0;
var fps = 1000 / 60;
var propellerWidth = 100;
var propellerHeight = 10;
var towerWidth = 2;
var towerHeight = 100;
var mode = {
CUBE: 'CUBE',
EXAMPLE: 'EXAMPLE'
}
var currentMode = mode.EXAMPLE;
btnExample.onclick = function() {
currentMode = mode.EXAMPLE;
}
btnCube.onclick = function() {
currentMode = mode.CUBE;
}
setInterval(function() {
angle += 3;
draw(canvas, ctx, (canvas.width - propellerWidth) / 2, (canvas.height - propellerWidth) / 2, angle);
}, fps);
function draw(canvas, ctx, cx, cy, angle) {
var towerX = cx + propellerWidth / 2 - towerWidth / 2;
var towerY = cy + propellerWidth / 2;
var propellerX = (canvas.width - propellerWidth) / 2;
var propellerY = (canvas.height - propellerHeight) / 2;
ctx.save();
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw other things that don't rotate
if (currentMode === mode.EXAMPLE) {
drawHelp(canvas, ctx);
}
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(Math.PI / 180 * angle);
ctx.translate(-canvas.width / 2, -canvas.height / 2);
// Draw things that rotate
drawPropeller(ctx, propellerX, propellerY, propellerWidth, propellerHeight);
ctx.restore();
// Draw other things that don't rotate
if (currentMode === mode.EXAMPLE) {
drawTower(ctx, towerX, towerY, towerWidth, towerHeight);
} else if (currentMode === mode.CUBE) {
drawCube(ctx, towerX, towerY, 30);
}
}
function drawPropeller(ctx, propellerX, propellerY, propellerWidth, propellerHeight) {
ctx.fillStyle = 'black';
ctx.fillRect(propellerX, propellerY, propellerWidth, propellerHeight);
}
function drawTower(ctx, towerX, towerY, towerWidth, towerHeight) {
ctx.fillStyle = 'red';
ctx.fillRect(towerX, towerY, towerWidth, towerHeight);
}
function drawCube(ctx, x, y, size) {
ctx.strokeStyle = 'black';
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x, y + size);
ctx.closePath();
ctx.stroke();
var x1 = x - size + (size / 4) + (size / 16);
var y1 = y + (size * 1.25);
var x2 = x1 + (size / 3);
var y2 = y1 - (size * 2 / 3);
var x3 = x2 + size;
var y3 = y2;
var x4 = x3 - (size / 3);
var y4 = y1;
var x5 = x4;
var y5 = y4 + size;
var x6 = x3;
var y6 = y3 + size;
ctx.strokeRect(x1, y1, size, size);
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x2, y2);
ctx.lineTo(x3, y3);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x3, y3);
ctx.lineTo(x4, y4);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x3, y3);
ctx.lineTo(x6, y6);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x5, y5);
ctx.lineTo(x6, y6);
ctx.closePath();
ctx.stroke();
}
function drawHelp(canvas, ctx) {
ctx.globalAlpha = 0.2;
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(canvas.width, canvas.height);
ctx.moveTo(canvas.width, 0);
ctx.lineTo(0, canvas.height);
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.arc(canvas.width / 2, canvas.height / 2, propellerWidth / 2, 0, 2 * Math.PI);
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.arc(canvas.width / 2, canvas.height / 2, propellerWidth / 2, 0, 2 * Math.PI);
ctx.stroke();
ctx.closePath();
ctx.globalAlpha = 1;
}
canvas {
border: 1px solid black;
}
<canvas id="canvas" width=200 height=200></canvas>
<br>
<button id="btnExample">Example</button>
<button id="btnCube">Cube</button>
I'd like to draw an ellipse given a cx and cy position-property and a width and height property of the ellipse itself.
Below you can find some working code for this setup:
But now I want to generate a kind of "progress display" by painting a percentage (from 0 to 100) of the ellipse instead of the complete ellipse.
I have attached a graphic here to illustrate the whole thing:
I don't really have a clear idea how to do that. I would prefer a solution where I can do without resizing the canvas - just for performance reasons and I hope someone has a good idea how to solve my problem.
let canvas = document.getElementById("canvas")
let ctx = canvas.getContext("2d");
canvas.width = 400;
canvas.height = 280;
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height)
let ellipse = function(cx, cy, w, h) {
let lx = cx - w / 2,
rx = cx + w / 2,
ty = cy - h / 2,
by = cy + h / 2;
let magic = 0.551784;
let xmagic = magic * w / 2,
ymagic = h * magic / 2;
let region = new Path2D();
region.moveTo(cx, ty);
region.bezierCurveTo(cx + xmagic, ty, rx, cy - ymagic, rx, cy);
region.bezierCurveTo(rx, cy + ymagic, cx + xmagic, by, cx, by);
region.bezierCurveTo(cx - xmagic, by, lx, cy + ymagic, lx, cy);
region.bezierCurveTo(lx, cy - ymagic, cx - xmagic, ty, cx, ty);
ctx.strokeStyle = "red";
ctx.lineWidth = "10";
region.closePath();
ctx.stroke(region);
}
ellipse(canvas.width / 2, canvas.height / 2, 300, 120)
<canvas id="canvas"></canvas>
You can use the built-in function ctx.ellipse - first we draw the green line as a full ellipse. Next, draw the red partial ellipse on top:
let canvas = document.getElementById("canvas")
let ctx = canvas.getContext("2d");
canvas.width = 400;
canvas.height = 280;
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height)
function ellipse(ctx, color, x,y, w, h, thickness, angle) {
ctx.strokeStyle = color;
ctx.beginPath();
ctx.ellipse(canvas.width / 2, canvas.height / 2, h/2,w/2, Math.PI*3/2, 0, angle);
ctx.lineWidth = thickness;
ctx.stroke();
}
function ell(percent) {
let x= canvas.width / 2;
let y= canvas.height / 2;
let w=300;
let h=120;
let th = 10; // example thickness 10px
ellipse(ctx, '#608a32', x,y, w, h, th, Math.PI*2);
ellipse(ctx, '#ed3833', x,y , w, h, th+.3, 2*Math.PI*percent/100);
}
ell(90); // here we start draw for 90%
<canvas id="canvas"></canvas>
You can draw the ellipse with a bit of trigonometry
let canvas = document.getElementById("canvas")
let ctx = canvas.getContext("2d");
canvas.width = 400;
canvas.height = 170;
let ellipse = function(cx, cy, ds, de, w, h, color) {
for (var i = ds; i < de; i ++) {
var angle = i * ((Math.PI * 2) / 360);
var x = Math.cos(angle) * w;
var y = Math.sin(angle) * h;
ctx.beginPath();
ctx.fillStyle = color;
ctx.arc(cx+ x, cy+y, 6, 0, 2 * Math.PI);
ctx.fill();
}
}
let draw = function(cx, cy, ds, de, w, h, color) {
ctx.clearRect(0, 0, canvas.width, canvas.height)
delta += 10
if (delta > 350) delta = 40
hw = canvas.width / 2
hh = canvas.height / 2
ellipse(hw, hh, 0, 360, 150, 60, "red")
ellipse(hw, hh, 0, delta, 150, 60, "blue")
ctx.font = "80px Arial";
ctx.fillStyle = "green";
ctx.fillText(Math.round(delta/3.6) + "%", hw-70, hh+30);
}
delta = 90
setInterval(draw, 100)
<canvas id="canvas"></canvas>
Once you have a nice function you can animate it
I just had a quick thought in mind of drawing a chessboard using JS and Canvas, and I have this code that draws the boxes alright with for loops.
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
var x, y,
boxWidth = 30,
boxHeight = 30;
for (x = 0; x < canvas.width; x += boxWidth) {
for (y = 0; y < canvas.height; y += boxHeight) {
ctx.beginPath();
ctx.rect(x, y, boxWidth, boxHeight);
ctx.stroke();
ctx.closePath();
}
}
<canvas id="canvas" width="240" height="240"></canvas>
Now I'm wondering how I can access each odd box on the axes to change their fill colors (e.g. black, white, black, white, and so on).
I know using global variables isn't the best way, but this is a very small project and I just want to get some logic on how I can alternate the colors of the chessboard. Your help is very much appreciated!
You could also try only incrementing your values by 1 (instead of boxWidth), which would make it simpler to check if they are even or odd. Then you would need to either scale or multiply by boxWidth and boxHeight:
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
var x, y,
boxWidth = 30,
boxHeight = 30;
var numbRows = Math.floor(canvas.width / boxWidth),
numbCols = Math.floor(canvas.height / boxHeight);
ctx.save();
ctx.scale(boxWidth, boxHeight);
for (x = 0; x < numbRows; x++) {
for (y = 0; y < numbCols; y++) {
if ((x+y) % 2 == 0) ctx.fillStyle = 'white';
else ctx.fillStyle = 'black';
ctx.beginPath();
ctx.rect(x, y, boxWidth, boxHeight);
ctx.stroke();
ctx.closePath();
}
}
ctx.restore();
You can use fillRect to do so like this:
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
var x, y,
boxWidth = 30,
boxHeight = 30;
for (x = 0; x < canvas.width; x += boxWidth) {
for (y = 0; y < canvas.height; y += boxHeight) {
ctx.fillStyle = (x / boxWidth + y / boxHeight) % 2 === 0? "white": "black"; // determine which color to use depending on the index of x (x / boxWidth) an the index of y (y / boxHeight)
ctx.fillRect(x, y, boxWidth, boxHeight);
}
}
<canvas id="canvas" width="240" height="240"></canvas>
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
var x, y,
boxWidth = 30,
boxHeight = 30;
for (x = 0; x < canvas.width; x += boxWidth) {
for (y = 0; y < canvas.height; y += boxHeight) {
ctx.beginPath();
ctx.rect(x, y, boxWidth, boxHeight);
// fill odd boxes
(x/boxWidth + y/boxHeight) % 2 && ctx.fill()
ctx.stroke();
ctx.closePath();
}
}
<canvas id="canvas" width="240" height="240"></canvas>
Alternatives to the for loop
Another way without a loop, draw the pattern 2 by 2 square in top corner then repeat that by copying the canvas onto itself.
First create the top 2 by 2 square then fill rest of board with copies.
First 2 by 2 to 4 by 2
then 4 by 2 to 8 by 2
then 8 by 2 to 8 by 4
then 8 by 4 to 8 by 8
Example
const w= 100;
canvas.height = canvas.width = w * 8;
const ctx = canvas.getContext("2d");
ctx.fillStyle = "black";
ctx.fillRect(0, 0, w + w, w + w);
ctx.fillStyle = "white";
ctx.fillRect(0, 0, w, w);
ctx.fillRect(w, w, w, w);
ctx.drawImage(canvas, 0, 0, w * 2, w * 2, w * 2, y , w * 2, w * 2);
ctx.drawImage(canvas, 0, 0, w * 4, w * 2, w * 4, y , w * 4, w * 2);
ctx.drawImage(canvas, 0, 0, w * 8, w * 2, 0 , w * 2, w * 8, w * 2);
ctx.drawImage(canvas, 0, 0, w * 8, w * 4, 0 , w * 4, w * 8, w * 4);
Thus it gets drawn in 7 render calls, if the grid was larger then 2 more calls for 16 by 16, and every doubling in size only needs two more calls.
The pattern can be very complex but not create excessive render stress as in the next example that has shadows and different composite calls.
const squareSize = 72;
const boardSize = 8;
const borderSize = 8;
canvas.height = canvas.width = squareSize * boardSize + borderSize * 2;
const ctx = canvas.getContext("2d");
var x = borderSize;
var y = x;
var w = squareSize;
drawSquare(3, 3, canvas.width - 6, "black", "#F97");
drawSquare(x, y, w, "white", "#964");
drawSquare(w + x, y, w, "black", "#745");
ctx.drawImage(canvas, x, y, w, w, x + w, y + w, w, w);
ctx.drawImage(canvas, x + w, y, w, w, x, y + w, w, w);
ctx.drawImage(canvas, x, y, w * 2, w * 2, x + w * 2, y, w * 2, w * 2);
ctx.drawImage(canvas, x, y, w * 4, w * 2, x + w * 4, y, w * 4, w * 2);
ctx.drawImage(canvas, x, y, w * 8, w * 2, x, y + w * 2, w * 8, w * 2);
ctx.drawImage(canvas, x, y, w * 8, w * 4, x, y + w * 4, w * 8, w * 4);
drawSquare(0,0,canvas.width,"rgba(0,0,0,0.0)","rgba(0,0,0,0.05)");
// done.
// this function is only called twice.
function drawSquare(x,y,size,color,color2){
ctx.save();
ctx.shadowColor = color2;
ctx.shadowBlur = size * 0.2;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.beginPath();
ctx.rect(x,y,size,size);
ctx.clip();
ctx.lineWidth = size;
ctx.fillStyle = color;
ctx.fillRect(x,y,size,size);
ctx.globalAlpha = 0.5;
ctx.strokeRect(x - size / 2,y - size / 2, size * 2, size * 2);
ctx.shadowBlur = size * 0.5;
ctx.strokeRect(x - size / 2,y - size / 2, size * 2, size * 2);
ctx.shadowColor = "rgba(0,0,0,0)";
ctx.shadowBlur = 0;
ctx.globalAlpha = 1;
ctx.strokeStyle = color2;
ctx.lineWidth = 2;
ctx.strokeRect(x+1,y+1,size -2,size-2);
ctx.globalAlpha = 0.75;
ctx.fillRect(x+1,y+1,size-2,size-2);
ctx.globalCompositeOperation = "screen";
ctx.fillStyle = "white";
ctx.globalAlpha = 0.1;
ctx.fillRect(x,y,4,size);
ctx.fillRect(x,y,2,size);
ctx.fillRect(x+4,y,size-4,4);
ctx.fillRect(x+2,y,size-2,2);
ctx.restore();
}
canvas { border : 2px solid black; }
<canvas id="canvas" ></canvas>
Use a pattern style.
Create an offscreen canvas to hold the pattern of the top 2 by 2, draw the pattern, then assign the fillStyle of the on screen canvas to a new pattern created from the off screen canvas and fill the whole canvas.
const w = 72;
const patCan = document.createElement("canvas");
patCan.height = patCan.width = w * 2;
var ctx = patCan.getContext("2d");
ctx.fillStyle = "black";
ctx.fillRect(0, 0, w + w, w + w);
ctx.fillStyle = "white";
ctx.fillRect(0, 0, w, w);
ctx.fillRect(w, w, w, w);
// setup display canvas
canvas.height = canvas.width = w * 8;
var ctx = canvas.getContext("2d");
ctx.fillStyle = ctx.createPattern(patCan, "repeat");
ctx.fillRect(0, 0, w * 8, w * 8);
canvas { border : 8px solid green; }
<canvas id="canvas" ></canvas>
Rendering with 3 lines of code
Here's a neat little trick you can use to draw a chess board:
var ctx = c.getContext("2d");
for(var x = 0; x < c.width; x += c.width / 4) ctx.fillRect(x, 0, c.width/8, c.height);
ctx.globalCompositeOperation = "xor"; // toggle alpha channel for every 2nd line
for(var y = 0; y < c.height; y += c.height / 4) ctx.fillRect(0, y, c.width, c.height/8);
<canvas id=c width=600 height=600></canvas>
We're using canvas' size to detemine the grid size. You can of course change this and offset to anything you like. You'd still use the divisors 4 (2 cells) and 8 (1 cell) with the actual width and height.
The first step draws vertical black stripes every other column. Then we toggle alpha channel for every other row knowing that the default color is black (rgba(0,0,0,0)) using the "xor" composite mode which toggles the alpha channel.
Just remember to start with a empty canvas (which you probably are anyways due to the need to redraw moves) and to set back composite mode to "source-over" after drawing the board.
If you'd like to change the color itself simply add an extra step at the end:
ctx.globalCompositeOperation = "source-atop"; // will draw on top of what is filled
ctx.fillStyle = "#09a";
ctx.fillRect(0, 0, c.width, c.height);
The fillStyle and fillRect() can be replaced or used with an image, pattern, gradient etc.
To fill the white background simply use composite mode "destination-over" (will draw behind anything filled using the alpha channel), then draw for background.
An alternative is to use a toggle switch when filling each cell one by one:
var ctx = c.getContext("2d");
var toggle = false;
ctx.beginPath();
for(var y=0; y < c.height; y += c.height / 8) {
toggle = !toggle; // toggle for each row so they don't line up
for(var x=0; x < c.width; x += c.width / 8) {
// toggle for each cell and check, only draw if toggle = true
if (toggle = !toggle) ctx.rect(x, y, c.width / 8, c.height / 8);
}
}
ctx.fill(); // remember to use beginPath() for consecutive draw ops
<canvas id=c width=600 height=600></canvas>
Access Logic
To know if you're inside a cell you would simply calculate the mouse position relative to the canvas (see this answer on how to do that) and then quantize (using pseudo variables here, replace with real):
var cellSize = boardWidth / 8; // assumes the board is 1:1 square
var pos = getMousePos(event); // see linked answer above
var cellX = Math.floor(pos.x / cellSize) * cellSize; // start of current cell X
var cellY = Math.floor(pos.y / cellSize) * cellSize; // start of current cell Y
(to get index of cell just drop the * cellSize part).
Example:
var ctx = c.getContext("2d"), x, y, w = c.width, h = c.height, cellSize = w / 8;
render();
ctx.lineWidth = 4; ctx.strokeStyle = "red"; ctx.setLineDash([7, 7]);
// non-optimized - in production only redraw when needed (cellX/Y changes)
c.onmousemove = function(e) {
render();
var cell = getCellPos(getMousePos(e));
if (cell.x >= 0 && cell.x < w && cell.y >=0 && cell.y < h)
ctx.strokeRect(cell.x + 2, cell.y + 2, cellSize - 4, cellSize - 4);
}
function getCellPos(pos) {
return {x: Math.floor(pos.x / cellSize) * cellSize,
y: Math.floor(pos.y / cellSize) * cellSize}
}
function getMousePos(e) {
var rect = c.getBoundingClientRect();
return {x: e.clientX-rect.x, y: e.clientY-rect.y}
}
function render() {
ctx.clearRect(0, 0, w, h);
for(x = 0; x < w; x += w>>2) ctx.fillRect(x, 0, cellSize, c.height);
ctx.globalCompositeOperation = "xor"; // toggle alpha channel for every 2nd line
for(y = 0; y < h; y += h>>2) ctx.fillRect(0, y, w, cellSize);
ctx.globalCompositeOperation = "source-atop"; // fg
ctx.fillStyle = "#3c4168";
ctx.fillRect(0, 0, w, h);
ctx.globalCompositeOperation = "destination-over"; // bg
ctx.fillStyle = "#eee";
ctx.fillRect(0, 0, w, h);
ctx.globalCompositeOperation = "source-over"; // reset
}
body {background:#222;margin:20px 0 0 20px;}
<canvas id=c width=600 height=600></canvas>
On an HTML5 canvas I can't find a method to make colored circles. I've been consulting this as a reference.
Here is my current attempt
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.fillRect(20, 20, 100, 100);
ctx.lineJoin = "round";
ctx.lineWidth = "cornerRadius";
<canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;">
(Also on jsFiddle: http://jsfiddle.net/kvnm21r1/1/)
I've tried using the canvas arc method, which creates a circle, but doesn't color it.
I can't use the border-radius property, because ctx is not an element.
Is there any way, I can transform my squares into circles?
Thanks in advance.
You can use quadratic curves to "round-out" the straight lines of your square until they form a circle.
// change sideCount to the # of poly sides desired
//
var sideCount = 4;
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.lineWidth = 2;
ctx.fillStyle = randomColor();
var PI2 = Math.PI * 2;
var cx = 150;
var cy = 150;
var radius = 100;
var xx = function (a) {
return (cx + radius * Math.cos(a));
}
var yy = function (a) {
return (cy + radius * Math.sin(a));
}
var lerp = function (a, b, x) {
return (a + x * (b - a));
}
var sides = [];
for (var i = 0; i < sideCount; i++) {
sides.push(makeSide(i, sideCount));
}
var percent = 0;
var percentDirection = 0.50;
$("#toShape").click(function () {
percentDirection = -0.50;
})
$("#toCircle").click(function () {
percentDirection = 0.50;
})
animate();
// functions
function animate() {
requestAnimationFrame(animate);
drawSides(percent);
percent += percentDirection;
if (percent > 100) {
percent = 100;
}
if (percent < 0) {
percent = 0;
}
}
function drawSides(pct, color) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (pct == 100) {
ctx.beginPath();
ctx.arc(cx, cy, radius, 0, PI2);
ctx.closePath();
ctx.fill();
} else {
ctx.beginPath();
ctx.moveTo(sides[0].x0, sides[0].y0);
for (var i = 0; i < sideCount; i++) {
var side = sides[i];
var cpx = lerp(side.midX, side.cpX, pct / 100);
var cpy = lerp(side.midY, side.cpY, pct / 100);
ctx.quadraticCurveTo(cpx, cpy, side.x2, side.y2);
}
ctx.fill();
}
}
function makeSide(n, sideCount) {
var sweep = PI2 / sideCount;
var sAngle = sweep * (n - 1);
var eAngle = sweep * n;
var x0 = xx(sAngle);
var y0 = yy(sAngle);
var x1 = xx((eAngle + sAngle) / 2);
var y1 = yy((eAngle + sAngle) / 2);
var x2 = xx(eAngle);
var y2 = yy(eAngle);
var dx = x2 - x1;
var dy = y2 - y1;
var a = Math.atan2(dy, dx);
var midX = lerp(x0, x2, 0.50);
var midY = lerp(y0, y2, 0.50);
var cpX = 2 * x1 - x0 / 2 - x2 / 2;
var cpY = 2 * y1 - y0 / 2 - y2 / 2;
return ({
x0: x0,
y0: y0,
x2: x2,
y2: y2,
midX: midX,
midY: midY,
cpX: cpX,
cpY: cpY,
color: randomColor()
});
}
function randomColor() {
return ('#' + Math.floor(Math.random() * 16777215).toString(16));
}
body{ background-color: ivory; }
canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<button id="toShape">Animate to Shape</button>
<button id="toCircle">Animate to Circle</button><br>
<canvas id="canvas" width=300 height=300></canvas>
JSfiddle with a circle
To draw a circle you'll need to draw an arc and have a starting angle and an ending angle. So you'll have to use Pi and define a radius.
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var radius = 70;
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.fillStyle = 'green';
context.fill();
context.lineWidth = 5;
context.strokeStyle = '#003300';
context.stroke();
Avoid w3schools.com whenever possible. Refer to sites like MDN instead.
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext("2d");
ctx.beginPath();
var radius = 50; // Arc radius
var startAngle = 0; // Starting point on circle
var endAngle = Math.PI*2; // End point on circle
ctx.arc(150, 75, radius, startAngle, endAngle, true);
ctx.fill();
<canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;">