Related
I would like to create an isometric 3D cube with fillRect whose 3 faces have the same dimensions as the image below:
Edit: I want to do it with fillRect. The reason for this is that I will draw images on the 3 faces of the cube afterwards. This will be very easy to do since I will use exactly the same transformations as for drawing the faces.
Edit 2: I didn't specify that I want to avoid using an external library so that the code is as optimized as possible. I know that it is possible to calculate the 3 matrices beforehand to draw the 3 faces and make a perfect isometric cube.
Edit 3: As my example code showed, I want to be able to set the size of the side of the isometric cube on the fly (const faceSize = 150).
I have a beginning of code but I have several problems:
The faces are not all the same dimensions
I don't know how to draw the top face
const faceSize = 150;
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
// Top Face (not big enough)
ctx.save();
ctx.translate(centerX, centerY);
ctx.scale(1, .5);
ctx.rotate(-45 * Math.PI / 180);
ctx.fillStyle = 'yellow';
ctx.fillRect(0, -faceSize, faceSize, faceSize);
ctx.restore();
// Left Face (not high enough)
ctx.save();
ctx.translate(centerX, centerY);
ctx.transform(1, .5, 0, 1, 0, 0);
ctx.fillStyle = 'red';
ctx.fillRect(-faceSize, 0, faceSize, faceSize);
ctx.restore();
// Right Face (not high enough)
ctx.save();
ctx.translate(centerX, centerY);
ctx.transform(1, -.5, 0, 1, 0, 0);
ctx.fillStyle = 'blue';
ctx.fillRect(0, 0, faceSize, faceSize);
ctx.restore();
<canvas width="400" height="400"></canvas>
I used a large part of #enhzflep's code which I adapted so that the width of the cube is dynamically changeable.
All the code seems mathematically correct, I just have a doubt about the value 1.22 given as a parameter to scaleSelf. Why was this precise value chosen?
Here is the code:
window.addEventListener('load', onLoad, false);
const canvas = document.createElement('canvas');
function onLoad() {
//canvas.width = cubeWidth;
//canvas.height = faceSize * 2;
canvas.width = 400;
canvas.height = 400;
document.body.appendChild(canvas);
drawCube(canvas);
}
function drawCube() {
const scale = Math.abs(Math.sin(Date.now() / 1000) * canvas.width / 200); // scale effect
const faceSize = 100 * scale;
const radians = 30 * Math.PI / 180;
const cubeWidth = faceSize * Math.cos(radians) * 2;
const centerPosition = {
x: canvas.width / 2,
y: canvas.height / 2
};
const ctx = canvas.getContext('2d');
ctx.save();
ctx.fillStyle = '#000';
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
const defaultMat = [1, 0, 0, 1, 0, 0];
// Left (red) side
const leftMat = new DOMMatrix(defaultMat);
leftMat.translateSelf(centerPosition.x - cubeWidth / 2, centerPosition.y - faceSize / 2);
leftMat.skewYSelf(30);
ctx.setTransform(leftMat);
ctx.fillStyle = '#F00';
ctx.fillRect(0, 0, cubeWidth / 2, faceSize);
// Right (blue) side
const rightMat = new DOMMatrix(defaultMat);
rightMat.translateSelf(centerPosition.x, centerPosition.y);
rightMat.skewYSelf(-30);
ctx.setTransform(rightMat);
ctx.fillStyle = '#00F';
ctx.fillRect(0, 0, cubeWidth / 2, faceSize);
// Top (yellow) side
const topMat = new DOMMatrix(defaultMat);
const toOriginMat = new DOMMatrix(defaultMat);
const fromOriginMat = new DOMMatrix(defaultMat);
const rotMat = new DOMMatrix(defaultMat);
const scaleMat = new DOMMatrix(defaultMat);
toOriginMat.translateSelf(-faceSize / 2, -faceSize / 2);
fromOriginMat.translateSelf(centerPosition.x, centerPosition.y - faceSize / 2);
rotMat.rotateSelf(0, 0, -45);
scaleMat.scaleSelf(1.22, (faceSize / cubeWidth) * 1.22);
topMat.preMultiplySelf(toOriginMat);
topMat.preMultiplySelf(rotMat);
topMat.preMultiplySelf(scaleMat);
topMat.preMultiplySelf(fromOriginMat);
ctx.setTransform(topMat);
ctx.fillStyle = '#FF0';
ctx.fillRect(0, 0, faceSize, faceSize);
ctx.restore();
requestAnimationFrame(drawCube);
}
Here's a quick n dirty approach to the problem. It's too hot here for me to really think very clearly about this question. (I struggle with matrix maths too)
There's 2 things I think worth mentioning, each of which has an effect on the scaling operation.
width and height of the finished figure (and your posted example image) are different.
I think it's the ratio of the distance between (opposite) corners of the untransformed rectangle which fills 1/4 of the canvas, and the finished yellow side which affect the scaling.
Also, note that I'm drawing a square of canvas.height/2 sidelength for the yellow side, whereas I was drawing a rectangle for the red and blue sides.
In the scaling section, width/4 and height/4 are both shorthand for (width/2)/2 and (height/2)/2. width/2 and height/2 give you a rectangle filling 1/2 of the canvas, with a centre (middle of the square) located at (width/2)/2, (height/2)/2 - height/4 means something different in the translation section (even though it's the same number)
With that said, here's the sort of thing I was talking about earlier.
"use strict";
window.addEventListener('load', onLoaded, false);
function onLoaded(evt)
{
let width = 147;
let height = 171;
let canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
document.body.appendChild(canvas);
drawIsoDemo(canvas);
}
function drawIsoDemo(destCanvas)
{
let ctx = destCanvas.getContext('2d');
let width = destCanvas.width;
let height = destCanvas.height;
ctx.fillStyle = '#000';
ctx.fillRect(0,0,width,height);
var idMatVars = [1,0, 0,1, 0,0];
// left (red) side
let leftMat = new DOMMatrix( idMatVars );
leftMat.translateSelf( 0, 0.25*height );
leftMat.skewYSelf(30);
ctx.save();
ctx.transform( leftMat.a, leftMat.b, leftMat.c, leftMat.d, leftMat.e, leftMat.f);
ctx.fillStyle = '#F00';
ctx.fillRect(0,0,width/2,height/2);
ctx.restore();
// right (blue) side
let rightMat = new DOMMatrix( idMatVars );
rightMat.translateSelf( 0.5*width, 0.5*height );
rightMat.skewYSelf(-30);
ctx.save();
ctx.transform( rightMat.a, rightMat.b, rightMat.c, rightMat.d, rightMat.e, rightMat.f);
ctx.fillStyle = '#00F';
ctx.fillRect(0,0,width/2,height/2);
ctx.restore();
// top (yellow) side
let topMat = new DOMMatrix( idMatVars );
let toOriginMat = new DOMMatrix( idMatVars );
let fromOriginMat = new DOMMatrix(idMatVars);
let rotMat = new DOMMatrix(idMatVars);
let scaleMat = new DOMMatrix(idMatVars);
toOriginMat.translateSelf(-height/4, -height/4);
fromOriginMat.translateSelf(width/2,height/4);
rotMat.rotateSelf(0,0,-45);
scaleMat.scaleSelf(1.22,((height/2)/width)*1.22);
topMat.preMultiplySelf(toOriginMat);
topMat.preMultiplySelf(rotMat);
topMat.preMultiplySelf(scaleMat);
topMat.preMultiplySelf(fromOriginMat);
ctx.save();
ctx.transform( topMat.a, topMat.b, topMat.c, topMat.d, topMat.e, topMat.f);
ctx.fillStyle = '#FF0';
ctx.fillRect(0,0,height/2,height/2);
ctx.restore();
}
If we overlay a circle on your isometric cube, we can see that the outer vertices are spaced equally apart. In fact it's always 60°, which is no wonder as it's a hexagon.
So all we have to do is obtaining the coordinates for the outer vertices. This is quite easy as we can make a further assumption: if you look at the shape again, you'll notice that the length of each of the cube's sides seems to be the radius of the circle.
With the help of a little trigonometry and a for-loop which increments by 60 degrees, we can put calculate and put all those vertices into an array and finally connect those vertices to draw the cube.
Here's an example:
let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
function drawCube(x, y, sideLength) {
let vertices = [new Point(x, y)];
for (let a = 0; a < 6; a++) {
vertices.push(new Point(x + Math.cos(((a * 60) - 30) * Math.PI / 180) * sideLength, y + Math.sin(((a * 60) - 30) * Math.PI / 180) * sideLength));
}
ctx.fillStyle = "#ffffff";
ctx.beginPath();
ctx.moveTo(vertices[0].x, vertices[0].y);
ctx.lineTo(vertices[5].x, vertices[5].y);
ctx.lineTo(vertices[6].x, vertices[6].y);
ctx.lineTo(vertices[1].x, vertices[1].y);
ctx.lineTo(vertices[0].x, vertices[0].y);
ctx.fill();
ctx.fillStyle = "#a0a0a0";
ctx.beginPath();
ctx.moveTo(vertices[0].x, vertices[0].y);
ctx.lineTo(vertices[1].x, vertices[1].y);
ctx.lineTo(vertices[2].x, vertices[2].y);
ctx.lineTo(vertices[3].x, vertices[3].y);
ctx.lineTo(vertices[0].x, vertices[0].y);
ctx.fill();
ctx.fillStyle = "#efefef";
ctx.beginPath();
ctx.moveTo(vertices[0].x, vertices[0].y);
ctx.lineTo(vertices[3].x, vertices[3].y);
ctx.lineTo(vertices[4].x, vertices[4].y);
ctx.lineTo(vertices[5].x, vertices[5].y);
ctx.lineTo(vertices[0].x, vertices[0].y);
ctx.fill();
}
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
drawCube(200, 150, 85);
canvas {
background: #401fc1;
}
<canvas id="canvas" width="400" height="300"></canvas>
EDIT
What you want to achieve is ain't that easily simply because the CanvasRenderingContext2D API actually does not offer a skewing/shearing transform.
Nevertheless with the help of a third-party library we're able to transform the three sides in an orthographic way. It's called perspective.js
Still we need to calculate the outer vertices but instead of using the moveTo/lineTo commands, we forward the coordinates to perspective.js to actually do the perspective distortion of some source images.
Here's another example:
let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
function drawCube(x, y, sideLength) {
let vertices = [new Point(x, y)];
for (let a = 0; a < 6; a++) {
vertices.push(new Point(x + Math.cos(((a * 60) - 30) * Math.PI / 180) * sideLength, y + Math.sin(((a * 60) - 30) * Math.PI / 180) * sideLength));
}
let p = new Perspective(ctx, images[0]);
p.draw([
[vertices[5].x, vertices[5].y],
[vertices[6].x, vertices[6].y],
[vertices[1].x, vertices[1].y],
[vertices[0].x, vertices[0].y]
]);
p = new Perspective(ctx, images[1]);
p.draw([
[vertices[0].x, vertices[0].y],
[vertices[1].x, vertices[1].y],
[vertices[2].x, vertices[2].y],
[vertices[3].x, vertices[3].y]
]);
p = new Perspective(ctx, images[2]);
p.draw([
[vertices[4].x, vertices[4].y],
[vertices[5].x, vertices[5].y],
[vertices[0].x, vertices[0].y],
[vertices[3].x, vertices[3].y]
]);
}
function loadImages(index) {
let image = new Image();
image.onload = function(e) {
images.push(e.target);
if (index + 1 < sources.length) {
loadImages(index + 1);
} else {
drawCube(200, 150, 125, e.target);
}
}
image.src = sources[index];
}
let sources = ["https://picsum.photos/id/1079/200/300", "https://picsum.photos/id/76/200/300", "https://picsum.photos/id/79/200/300"];
let images = [];
loadImages(0);
canvas {
background: #401fc1;
}
<script src="https://cdn.rawgit.com/wanadev/perspective.js/master/dist/perspective.min.js"></script>
<canvas id="canvas" width="400" height="300"></canvas>
I have 2 canvasses that visualize values from 2 different labels.
I wrote 2 almost the same javascripts and when I run my application. It skips the first visualitation and only shows the second one. Where is my mistake?
Here is my html code:-
<div><canvas id="canvas" width="300" height="300"></canvas></div>
<asp:Label ID="LblGauge" runat="server"></asp:Label>
<div><canvas id="canvas2" width="300" height="300"></canvas></div>
<asp:Label ID="LblGauge1" runat="server"></asp:Label>
And here are my 2 javascripts. The only difference now is the canvas/canvas2 and the lblgauge and lblgauge1. Even if I change all the variables in the second script it will still only show the second visualition.
<script>
window.onload = function () {
//canvas initialization
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
//dimensions
var W = canvas.width;
var H = canvas.height;
//Variables
var degrees = document.getElementById("LblGauge").textContent;
var new_degrees = 0;
var difference = 0;
var color = "lightgreen";
var bgcolor = "#222";
var text;
var animation_loop, redraw_loop;
function init() {
//Clear the canvas everytime a chart is drawn
ctx.clearRect(0, 0, W, H);
//Background 360 degree arc
ctx.beginPath();
ctx.strokeStyle = bgcolor;
ctx.lineWidth = 30;
ctx.arc(W / 2, H / 2, 100, 0, Math.PI * 2, false);
ctx.stroke();
//Angle in radians = angle in degrees * PI / 180
var radians = degrees * Math.PI / 180;
ctx.beginPath();
ctx.strokeStyle = color;
ctx.lineWidth = 30;
//the arc will start from the topmost end
ctx.arc(W / 2, H / 2, 100, 0 - 90 * Math.PI / 180, radians - 90 * Math.PI / 180, false);
ctx.stroke();
//Lets add the text
ctx.fillStyle = color;
ctx.font = "50px bebas";
text = Math.floor(degrees / 360 * 100) + "%";
text_width = ctx.measureText(text).width;
ctx.fillText(text, W / 2 - text_width / 2, H / 2 + 15);
}
function draw() {
//Cancel any movement animation if a new chart is requested
if (typeof animation_loop != undefined) clearInterval(animation_loop);
////time for each frame is 1sec / difference in degrees
animation_loop = setInterval(animate_to, 1000 / difference);
}
//function to make the chart move to new degrees
function animate_to() {
if (degrees == new_degrees)
if (degrees < new_degrees)
degrees++;
else
degrees--;
init();
}
draw();
}
</script>
<script>
window.onload = function () {
//canvas initialization
var canvas = document.getElementById("canvas2");
var ctx = canvas.getContext("2d");
//dimensions
var W = canvas.width;
var H = canvas.height;
//Variables
var degrees = document.getElementById("LblGauge1").textContent;
var new_degrees = 0;
var difference = 0;
var color = "lightgreen";
var bgcolor = "#222";
var text;
var animation_loop, redraw_loop;
function init() {
//Clear the canvas everytime a chart is drawn
ctx.clearRect(0, 0, W, H);
//Background 360 degree arc
ctx.beginPath();
ctx.strokeStyle = bgcolor;
ctx.lineWidth = 30;
ctx.arc(W / 2, H / 2, 100, 0, Math.PI * 2, false);
ctx.stroke();
//Angle in radians = angle in degrees * PI / 180
var radians = degrees * Math.PI / 180;
ctx.beginPath();
ctx.strokeStyle = color;
ctx.lineWidth = 30;
//the arc will start from the topmost end
ctx.arc(W / 2, H / 2, 100, 0 - 90 * Math.PI / 180, radians - 90 * Math.PI / 180, false);
ctx.stroke();
//Lets add the text
ctx.fillStyle = color;
ctx.font = "50px bebas";
text = Math.floor(degrees / 360 * 100) + "%";
text_width = ctx.measureText(text).width;
ctx.fillText(text, W / 2 - text_width / 2, H / 2 + 15);
}
function draw() {
//Cancel any movement animation if a new chart is requested
if (typeof animation_loop != undefined) clearInterval(animation_loop);
////time for each frame is 1sec / difference in degrees
animation_loop = setInterval(animate_to, 1000 / difference);
}
//function to make the chart move to new degrees
function animate_to() {
if (degrees == new_degrees)
if (degrees < new_degrees)
degrees++;
else
degrees--;
init();
}
draw();
}
</script>
Can somebody tell me how to change my code.
This is what the javascript shows me when it works.
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>
I'm drawing lines with a pattern i'm creating on different canvas.
I'm translating and scaling the context matrices and creating another pattern to achieve that each line will start exactly from the beginning of the pattern. (as we know that patterns are created from the beginning of the context repeatedly for all context area and not depends on the drawing)
I've managed to do so as show below for most of the cases.
Each row represents a scale. and drawing many lines on different Y values.
Each line should have red circles repeatedly along the X axis. It is working for many scales.
The problem is in scale 1.6. The 3rd row lines. As we see, the lines in this row are not well patterned as the Y value is growing, and also the start is not right.
I think it is some floating point problem.. but i can't find the problem.
var ctx = demo.getContext('2d'),
pattern,
offset = 0;
/// create main pattern
ctx.fillStyle = 'red';
ctx.beginPath();
ctx.arc(8, 8, 7, 0, Math.PI * 2);
ctx.fill();
runScale(1, 0);
runScale(1.5, 120);
runScale(1.6, 240);
runScale(2, 360);
runScale(3, 480);
function runScale(scale, firstPntX) {
var newCanvasSize = {
width: demo.width * scale,
height: demo.height * scale
};
demo2.width = Math.round(newCanvasSize.width);
demo2.height = Math.round(newCanvasSize.height);
var firstPnt = {
x: firstPntX
};
var offsetPnt = {
x: 0,
y: (newCanvasSize.height / 2)
};
var ctx2 = demo2.getContext('2d');
var pt = ctx2.createPattern(demo, 'repeat');
ctx = demo3.getContext('2d');
for (var i = 20; i < 1000; i += (demo2.height + 10)) {
drawLines(i);
}
function drawLines(y) {
firstPnt.y = y;
demo2.width = demo2.width;
ctx2.fillStyle = pt;
var offsets = [firstPnt.x, y - demo2.height / 2];
ctx2.translate(offsets[0], offsets[1]);
ctx2.scale(scale, scale);
ctx2.fillRect(-offsets[0] / scale, -offsets[1] / scale, demo2.width / scale, demo2.height / scale);
ctx.lineWidth = newCanvasSize.height;
pattern = ctx.createPattern(demo2, 'repeat');
ctx.beginPath();
ctx.moveTo(firstPnt.x, firstPnt.y);
ctx.lineTo(firstPnt.x + 100, firstPnt.y);
ctx.strokeStyle = 'lightgreen';
ctx.stroke();
ctx.strokeStyle = pattern;
ctx.stroke();
}
}
canvas {
border: 1px solid #000
}
<canvas id="demo" width=16 height=16></canvas>
<canvas id="demo2"></canvas>
<canvas id="demo3" width=600 height=400></canvas>
After struggling all day with this problem i finally decided to post this question here.
And now, an hour later I've found the solution by myself..
I've decided not to delete it for the sake of the forum.
The solution is simply change the offsets.
Change this line
var offsets = [firstPnt.x, y - demo2.height / 2];
to this line
var offsets = [firstPnt.x % demo2.width,firstPnt.y % demo2.height - demo2.height / 2];
var ctx = demo.getContext('2d'),
pattern,
offset = 0;
/// create main pattern
ctx.fillStyle = 'red';
ctx.beginPath();
ctx.arc(8, 8, 7, 0, Math.PI * 2);
ctx.fill();
runScale(1, 0);
runScale(1.5, 120);
runScale(1.6, 240);
runScale(2, 360);
runScale(3, 480);
function runScale(scale, firstPntX) {
var newCanvasSize = {
width: demo.width * scale,
height: demo.height * scale
};
demo2.width = Math.round(newCanvasSize.width);
demo2.height = Math.round(newCanvasSize.height);
var firstPnt = {
x: firstPntX
};
var offsetPnt = {
x: 0,
y: (newCanvasSize.height / 2)
};
var ctx2 = demo2.getContext('2d');
var pt = ctx2.createPattern(demo, 'repeat');
ctx = demo3.getContext('2d');
for (var i = 20; i < 1000; i += (demo2.height + 10)) {
drawLines(i);
}
function drawLines(y) {
firstPnt.y = y;
demo2.width = demo2.width;
ctx2.fillStyle = pt;
var offsets = [firstPnt.x % demo2.width, firstPnt.y % demo2.height - demo2.height / 2];
ctx2.translate(offsets[0], offsets[1]);
ctx2.scale(scale, scale);
ctx2.fillRect(-offsets[0] / scale, -offsets[1] / scale, demo2.width / scale, demo2.height / scale);
ctx.lineWidth = newCanvasSize.height;
pattern = ctx.createPattern(demo2, 'repeat');
ctx.beginPath();
ctx.moveTo(firstPnt.x, firstPnt.y);
ctx.lineTo(firstPnt.x + 100, firstPnt.y);
ctx.strokeStyle = 'lightgreen';
ctx.stroke();
ctx.strokeStyle = pattern;
ctx.stroke();
}
}
canvas {
border: 1px solid #000
}
<canvas id="demo" width=16 height=16></canvas>
<canvas id="demo2"></canvas>
<canvas id="demo3" width=600 height=400></canvas>
Thanks for reading :D
Alright guys, I'm sure this has been asked before, but I couldn't find anything that directly related to what I was doing. So I have these 4 self drawing circles (or gauges.) Each one has it's own value, and I've been looking through just nit picking through codes and books to build this. My question I need to figure out is how I would go about putting in a count up? Basically I want a counter to go from 1 - x (x being the degree of the circle it's in). I've included my js and HTML 5 for you guys to look at.
HTML
<canvas id="a" width="300" height="300"></canvas>
<script>
var canvas = document.getElementById('a');
var context = canvas.getContext('2d');
var x = canvas.width / 2;
var y = canvas.height / 2;
var radius = 75;
var startAngle = 1.5 * Math.PI;
var endAngle = 3.2 * Math.PI;
var counterClockwise = false;
context.beginPath();
context.arc(x, y, radius, startAngle, endAngle, counterClockwise);
context.lineWidth = 15;
// line color
context.strokeStyle = 'black';
context.stroke();
</script>
Canvas.JS
$(document).ready(function(){
function animate(elementId, endPercent) {
var canvas = document.getElementById(elementId);
var context = canvas.getContext('2d');
var x = canvas.width / 2;
var y = canvas.height / 2;
var radius = 75;
var curPerc = 0;
var counterClockwise = false;
var circ = Math.PI * 2;
var quart = Math.PI / 2;
context.lineWidth = 15;
context.strokeStyle = '#85c3b8';
context.shadowOffsetX = 0;
context.shadowOffsetY = 0;
context.shadowBlur = 10;
function render(current) {
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.arc(x, y, radius, -(quart), ((circ) * current) - quart, false);
context.stroke();
curPerc++;
if (curPerc < endPercent) {
requestAnimationFrame(function () {
render(curPerc / 100);
});
}
}
render();
}
$(window).scroll(function(){
if($(this).scrollTop()<1600){
animate('a', 85);
animate('b', 95);
animate('c', 80);
animate('d', 75);
}
});
});
Keep in mind that I am very new to canvas, I appreciate all the help guys!
Demo: http://jsfiddle.net/m1erickson/mYKp5/
You can save your gauges as objects in an array:
var guages=[];
guages.push({ x:50, y:100, radius:40, start:0, end:70, color:"blue" });
guages.push({ x:200, y:100, radius:40, start:0, end:90, color:"green" });
guages.push({ x:50, y:225, radius:40, start:0, end:35, color:"gold" });
guages.push({ x:200, y:225, radius:40, start:0, end:55, color:"purple" });
The render function takes a guage object draws its progress
function render(guage,percent) {
var pct=percent/100;
var extent=parseInt((guage.end-guage.start)*pct);
var current=(guage.end-guage.start)/100*PI2*pct-quart;
ctx.beginPath();
ctx.arc(guage.x,guage.y,guage.radius,-quart,current);
ctx.strokeStyle=guage.color;
ctx.stroke();
ctx.fillStyle=guage.color;
ctx.fillText(extent,guage.x-15,guage.y+5);
}
And the animation loop asks render to draw all gauges from 0-100 percent of their full values
function animate() {
// if the animation is not 100% then request another frame
if(percent<100){
requestAnimationFrame(animate);
}
// redraw all guages with the current percent
drawAll(percent);
// increase percent for the next frame
percent+=1;
}
function drawAll(percent){
// clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// draw all the guages
for(var i=0;i<guages.length;i++){
render(guages[i],percent);
}
}