Why does my canvas spinwheel gets cut off on setting lineWidth? - javascript

This is my draw function for the canvas:
const drawSector = (sector, i) => {
const ang = arc * i;
ctx.save();
// COLOR
ctx.lineWidth = 10;
ctx.strokeStyle = "#ECBA3F";
ctx.beginPath();
ctx.fillStyle = sector.color;
ctx.moveTo(rad, rad);
ctx.arc(rad, rad, rad, ang, ang + arc);
ctx.lineTo(rad, rad);
ctx.fill();
ctx.stroke();
// TEXT
ctx.translate(rad, rad);
ctx.rotate(ang + arc / 2);
ctx.textAlign = "right";
ctx.fillStyle = "#000";
ctx.font = "bold 10px sans-serif";
ctx.fillText(sector.label, rad - 10, 10);
//
ctx.restore();
};
Here's my sandbox
As soon as I set a linewidth on the canvas, part of the edges get cutoff seemingly as if the canvas width and height don't take the linewidth into account. How do I fix this?
Also is there a way I can rotate the text inside the arcs so that they stack rather than just being in a single line. Perhaps something like the below image?

Related

How do I rotate an object in canvas and js; my example is not rotating how I expect

I'm trying to rotate a rectangle about it's center but it's not rotating how I expect.
Here's an example:
https://jsfiddle.net/37ur8dfk/1/
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');
let x = 100;
let y = 100;
let w = 100;
let h = 50;
// Draw a red dot to highlight the point I want to rotate the rectangle around
ctx.fillStyle = '#ff0000';
ctx.beginPath();
ctx.arc(x, y, 4, 0, 2 * Math.PI);
ctx.closePath();
ctx.fill();
// Attempt to rotate the rectangle aroumd x,y
ctx.save();
ctx.fillStyle = '#000000';
ctx.translate(-x, -y);
ctx.rotate(10 * Math.PI/180);
ctx.translate(x, y);
ctx.fillRect(x - w/2, y - h/2, w, h);
ctx.restore();
I have the center of the rectangle as x,y coords. I then translate it by -x,-y to change it's origin to 0,0. Then I rotate it by some degrees, but it does not seem to be rotating about the 0,0 coords. It's my understanding that rotate should rotate the entire context about the origin, or 0,0.
Please take a look at the jsfiddle to see what I mean.
What am I missing here?
You got it inversed.
You are not translating the rectangle, but the context's transformation matrix.
Think of this as a sheet of paper and an arm with pen.
When you translate your context, the arm is moving in the direction provided. When you rotate the context, the arm is rotating.
So to set your rectangle's center as the rotation origin you first need to move the arm so that the pen is in the center of the rectangle, then you'll be able to rotate. And we move back the arm to its initial position so that the x and y coords match.
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
let x = 100;
let y = 100;
let w = 100;
let h = 50;
// Attempt to rotate the rectangle aroumd x,y
ctx.save();
ctx.fillStyle = '#000000';
// move to transformation-origin
ctx.translate(x, y);
// transform
ctx.rotate(10 * Math.PI/180);
// go back to where we were
ctx.translate(-x, -y);
ctx.fillRect(x - w/2, y - h/2, w, h);
ctx.restore();
// Draw a red dot to highlight the point I want to rotate the rectangle around
ctx.fillStyle = '#ff0000';
ctx.beginPath();
ctx.arc(x, y, 4, 0, 2 * Math.PI);
ctx.closePath();
ctx.fill();
<canvas id="canvas" width="500" height="400"></canvas>
Try (This will allow a rotation around the dot)
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');
let x = 100;
let y = 100;
let w = 100;
let h = 50;
let angle = Math.PI/8;
ctx.save();
ctx.fillStyle = '#000000';
ctx.translate(x, y);
ctx.rotate(angle);
ctx.fillRect(0, 0, w, h);
ctx.restore();
ctx.save();
ctx.fillStyle = '#ff0000';
ctx.beginPath();
ctx.arc(x, y, 4, 0, 2 * Math.PI);
ctx.closePath();
ctx.fill();
ctx.restore();
canvas {
border: 1px solid black;
}
<canvas id="canvas" width="500" height="400"></canvas>

How can I move just one of many shapes inside canvas by 50 pixels;

I have a project to do and I cannot do it until I understand how moving object inside canvas work.
I need to move one of the objects below by 50pixels to right.
Anyone willing to help me is greatly appreciated.
Meanwhile thank you very much in advance for your help or suggestions.
function canvasOneShape() {
//refers to the html canvasone id
var canvas = document.getElementById("canvasOne");
this.canvasOne.width = 945;
this.canvasOne.height = 650;
// draws the canvas in 2d
var ctx = canvas.getContext("2d");
// Set the fill colour to blue.
ctx.fillStyle = "blue"; //used like this instead of rgb due personal preference:)
// Create a filled rectangle at co-ordinates (10,10)
// with height and width set to 100.
ctx.fillRect(10, 10, 250, 330); //
// Here I draw the square
// Set the canvas up for drawing in 2D.
// Set the fill colour to blue.
ctx.fillStyle = "rgba(244, 244, 189,.5)";
ctx.fillRect(10, 50, 330, 250);
//draw my first circle
var midXone = canvas.width / 2; //x location
var midXtwo = canvas.height / 2; //y location
var radius = 60; //circle radius
ctx.beginPath();
ctx.arc(midXone, midXtwo, radius, 0, 2 * Math.PI, false);
ctx.fillStyle ="rgba(89, 192, 227,.4)";
ctx.fill();
ctx.lineWidth = 5;
ctx.strokeStyle = '#003300';
ctx.stroke();
//draw the second circle
var midX = canvas.width / 2.35; //x location
var midY = canvas.height / 2.35; //y location
var radius = 50; //circle radius
ctx.beginPath();
ctx.arc(midX, midY, radius, 0, 2 * Math.PI, false);
ctx.fillStyle ="rgba(66, 244, 89,.4)";
ctx.fill();
ctx.lineWidth = 5;
ctx.strokeStyle ="rgba(255, 244, 9,.4)";
ctx.stroke();
//draw Square with circle inside
//square
ctx.fillStyle = "rgb(222, 33, 51)";
ctx.fillRect(550, 20, 300, 300);
//circle
ctx.beginPath();
ctx.arc(700, 170, 150, 0, 2 * Math.PI, false);
ctx.fillStyle ="rgba(66, 244, 89,.4)";
ctx.fill();
ctx.lineWidth = 1;
ctx.strokeStyle = '#f44242';
ctx.stroke();
//The Pacman object
var radius = 100; //circle radius
var x = 100;
var y = 500;
ctx.beginPath();
ctx.arc(120, 500, radius, 1.85 * Math.PI, .15 * Math.PI, true);
//Draw mouth
ctx.lineTo(120, 500);
ctx.closePath();
ctx.fillStyle = "rgb(255, 255, 0)";
ctx.fill();
ctx.lineWidth = 5;
ctx.strokeStyle = 'rgb(0,0,0)';
ctx.stroke();
//draw eye
ctx.beginPath();
ctx.arc(x + 40, y - 40, 10, 0 * Math.PI, 2 * Math.PI, true);
ctx.fillStyle = "rgb(0,0,0)";
ctx.fill();
}
there are no layers or objects on a canvas(it's just a single layer of pixels), so, if you draw something, you overwrite what's underneath it. to move something to the right, you will need to have a function to draw everything behind it, and then variables to store the location (x and y) of the objects you want to move.
then, to move it, you clear the canvas with
ctx.clearRect(0, 0,width of canvas, height height of canvas);,
call the background function to draw everything behind it again, and the redraw your object in a different location.

HTML 5 Canvas, rotate everything

I made a cylinder gauge, very similar to this one:
It is drawn using about 7 or so functions... mine is a little different. It is very fleixble in that I can set the colors, transparency, height, width, whether there is % text shown and a host of other options. But now I have a need for the same thing, but all rotated 90 deg so that I can set the height long and the width low to generate something more like this:
I found ctx.rotate, but no mater where it goes all the shapes fall apart.. ctx.save/restore appears to do nothing, I tried putting that in each shape drawing function. I tried modifying, for example, the drawOval function so that it would first rotate the canvas if horizontal was set to one; but it appeared to rotate it every single iteration, even with save/restore... so the top cylinder would rotate and the bottom would rotate twice or something. Very tough to tell what is really happening. What am I doing wrong? I don't want to duplicate all this code and spend hours customizing it, just to produce something I already have but turned horizontal. Erg! Help.
Option 1
To rotate everything just apply a transform to the element itself:
canvas.style.transform = "rotate(90deg)"; // or -90 depending on need
canvas.style.webkitTransform = "rotate(90deg)";
Option 2
Rotate context before drawing anything and before using any save(). Unlike the CSS version you will first need to translate to center, then rotate, and finally translate back.
You will need to make sure width and height of canvas is swapped before this is performed.
ctx.translate(ctx.canvas.width * 0.5, ctx.canvas.height * 0.5); // center
ctx.rotate(Math.PI * 0.5); // 90°
ctx.translate(-ctx.canvas.width * 0.5, -ctx.canvas.height * 0.5);
And of course, as an option 3, you can recalculate all your values to go along the other axis.
Look at the rotate function in this example. You want to do a translation to the point you want to rotate around.
example1();
example2();
function rotate(ctx, degrees, x, y, fn) {
ctx.save();
ctx.translate(x, y);
ctx.rotate(degrees * (Math.PI / 180));
fn();
ctx.restore();
}
function rad(deg) {
return deg * (Math.PI / 180);
}
function example2() {
var can = document.getElementById("can2");
var ctx = can.getContext('2d');
var w = can.width;
var h = can.height;
function drawBattery() {
var percent = 60;
ctx.beginPath();
ctx.arc(35,50, 25,0,rad(360));
ctx.moveTo(35+percent+25,50);
ctx.arc(35+percent,50,25,0,rad(360));
ctx.stroke();
ctx.beginPath();
ctx.fillStyle = "rgba(0,255,0,.5)";
ctx.arc(35,50,25,0,rad(360));
ctx.arc(35+percent,50,25,0,rad(360));
ctx.rect(35,25,percent,50);
ctx.fill();
ctx.beginPath();
ctx.lineWidth = 2;
ctx.strokeStyle = "#666666";
ctx.moveTo(135,25);
ctx.arc(135,50,25, rad(270), rad(269.9999));
//ctx.moveTo(35,75);
ctx.arc(35,50,25,rad(270),rad(90), true);
ctx.lineTo(135,75);
ctx.stroke();
}
drawBattery();
can = document.getElementById("can3");
ctx = can.getContext('2d');
w = can.width;
h = can.height;
rotate(ctx, -90, 0, h, drawBattery);
}
function example1() {
var can = document.getElementById('can');
var ctx = can.getContext('2d');
var color1 = "#FFFFFF";
var color2 = "#FFFF00";
var color3 = "rgba(0,155,255,.5)"
var text = 0;
function fillBox() {
ctx.save();
ctx.fillStyle = color3;
ctx.fillRect(0, 0, can.width / 2, can.height);
ctx.restore();
}
function drawBox() {
ctx.save();
ctx.beginPath();
ctx.strokeStyle = ctx.fillStyle = color1;
ctx.rect(10, 10, 50, 180);
ctx.font = "30px Arial";
ctx.fillText(text, 25, 45);
ctx.stroke();
ctx.beginPath();
ctx.strokeStyle = color2;
ctx.lineWidth = 10;
ctx.moveTo(10, 10);
ctx.lineTo(60, 10);
ctx.stroke();
ctx.restore();
}
fillBox();
rotate(ctx, 90, can.width, 0, fillBox);
text = "A";
drawBox();
color1 = "#00FFFF";
color2 = "#FF00FF";
text = "B";
rotate(ctx, 90, can.width, 0, drawBox);
centerRotatedBox()
function centerRotatedBox() {
ctx.translate(can.width / 2, can.height / 2);
for (var i = 0; i <= 90; i += 10) {
var radians = i * (Math.PI / 180);
ctx.save();
ctx.rotate(radians);
ctx.beginPath();
ctx.strokeStyle = "#333333";
ctx.rect(0, 0, 50, 50)
ctx.stroke();
ctx.restore();
}
}
}
#can,
#can2,
#can3 {
border: 1px solid #333333
}
<canvas id="can" width="200" height="200"></canvas>
<canvas id="can2" width="200" height="100"></canvas>
<canvas id="can3" width="100" height="200"></canvas>

Draw a text underneath a circle

I'm drawing a circle using html5 canvas and js.
var canvas = document.getElementById(key);
var context = canvas.getContext('2d');
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var radius = 35;
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.fillStyle = 'red';
context.fill();
context.stroke();
I also want to draw a text right underneath it. I tried this:
context.closePath();
context.fill();
context.fillStyle = "black";
context.font = "bold 11";
context.textBaseline = "top";
context.fillText('Hello', 100-radius/4 ,100-radius/2);
But it was drawn in the right-bottom corner for some reason, whereas I wanted exactly under the circle.
Set textAlign to center, then add radius to y position:
var spacing = 5;
context.textAlign = "center";
context.textBaseline = "top";
context.fillText('Hello', centerX, centerY + radius + spacing);
Your text alignment and coordinates for the text are wrong.
var margin = 10; // Increase the margin add space between Circle & text
context.textBaseline = "top";
context.textAlign = "center";
context.fillText('Hello', centerX , centerY + radius + margin);
JSFiddle with desired outcome

Unable to apply fill for path drawn rectangle

I am trying to form a cell object using paths in HTML5 Canvas. But I am unable to apply fill for the path I made. Tried out several things but not able to solve it.
class Cell {
constructor({ i, j }) {
this.i = i;
this.j = j;
this.visited = false;
this.walls = [true, true, true, true]; // top right bottom left
this.show = canvasContext => this._show(canvasContext);
}
_show(ctx) {
const x = this.j * cellSize;
const y = this.i * cellSize;
if (!this.visited) {
ctx.fillStyle = "green";
} else {
ctx.fillStyle = "yellow";
}
// ctx.fillStyle = "green";
ctx.strokeStyle = "black";
ctx.beginPath();
if (this.walls[0]) {
ctx.moveTo(x, y);
ctx.lineTo(x + cellSize, y); // top
}
if (this.walls[3]) {
ctx.moveTo(x, y);
ctx.lineTo(x, y + cellSize); // left
}
if (this.walls[1]) {
ctx.moveTo(x + cellSize, y);
ctx.lineTo(x + cellSize, y + cellSize); // right
}
if (this.walls[2]) {
ctx.moveTo(x, y + cellSize);
ctx.lineTo(x + cellSize, y + cellSize); // bottom
}
ctx.fill();
ctx.stroke();
// ctx.fill();
}
}
See the Pen Maze Generator by Dhanushu (#dhanushuUzumaki) on CodePen.
The problem appears to be that you aren't creating a closed shape, you are just creating a series of lines that don't form a shape.
This is a closed path. If you took a pencil and drew the points, you'd see that it would be closed.
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
ctx.fillStyle = "#F00"; // red
ctx.strokeStyle = "#0F0"; // green
ctx.beginPath();
ctx.moveTo(50, 50);
ctx.lineTo(100, 50);
ctx.lineTo(100, 100);
ctx.lineTo(50, 100);
ctx.closePath();
ctx.fill();
ctx.stroke();
canvas {
border: 1px solid #000;
}
<canvas />
This is an open path, who's stroke looks the same:
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
ctx.fillStyle = "#F00"; // red
ctx.strokeStyle = "#0F0"; // green
ctx.beginPath();
ctx.moveTo(50, 50);
ctx.lineTo(100, 50);
ctx.moveTo(100, 50);
ctx.lineTo(100, 100);
ctx.moveTo(100, 100);
ctx.lineTo(50, 100);
ctx.moveTo(50, 100);
ctx.lineTo(50, 50);
ctx.closePath();
ctx.fill();
ctx.stroke();
canvas {
border: 1px solid #000;
}
<canvas />
By calling moveTo() you are essentially picking the pencil up, and breaking the shape. There is nothing to fill, because it's not one contiguous path. It's just 4 separate lines that are close to one another.
For your particular problem, I see two ways to solve this. The first would be to stop using moveTo() so often and just draw the lines. However, for your particular problem, that could be tricky.
Instead, what you might draw to do is draw individual rectangles to fill in each block of your grid. It seems like that logic might be easier to work out.

Categories

Resources