How to animate polygon size in HTML5 canvas? - javascript

I have drawn a polygon with the following code. Now I want to resize that polygon animatedly. In detail, I want to set an angular movement to one side of the polygon, such that it makes an arc, and so changes the size of polygon. I googled for everything regarding the polygon animation, but didn't get anything, though there is plenty of material for line animation.
<script>
$(function(){
var c=$('#myCanvas');
var ctx=c.getContext("2d");
ctx.fillStyle='#f00';
ctx.beginPath();
ctx.moveTo(0,40);
ctx.lineTo(80,200);
ctx.lineTo(100,200);
ctx.lineTo(40,0);
ctx.closePath();
ctx.fill();
</script>
</div>
Is it possible to pick a line of a polygon and animate it, to change the shape of the polygon?

The trick is to store the coordinates of the polygon in an array and work on the numbers in that array instead.
Then render what you have in the array to canvas. Worry-free when it comes to translation and what-have-you.
See canvas as only a snapshot of whatever you have in the array at the moment.

Ok this is what I did and which has worked out:
<script>
var canvas = document.getElementById("myCanvas");
var dc = canvas.getContext("2d");
var angle = 0;
var x = canvas.width;
var y = canvas.height;
window.setInterval(function(){
angle = (angle + 1) % 360;
dc.clearRect(0, 0, canvas.width, canvas.height);
dc.save();
dc.fillStyle = "#DDDDDD";
dc.translate(x/2,y);
dc.rotate( angle*Math.PI/270 ); // rotate 90 degrees
dc.translate(0,0);
dc.beginPath();
dc.moveTo(-x/16, 0);
dc.lineTo(-x, y/2);
dc.lineTo(-x/2,y);
dc.lineTo(-0,y/16);
dc.closePath();
dc.fill();
dc.restore();
}, 20);
</script>
I referred to Bill's answer at https://stackoverflow.com/a/2767556/2163837 Thanks for your help also.

Here’s how to turn a triangle into a square, a square into a pentagon, etc ….
This code draws a regular polygon with the specified number of sides:
function drawPolygon(sideCount,size,centerX,centerY){
// draw a regular polygon with sideCount sides
ctx.save();
ctx.beginPath();
ctx.moveTo (centerX + size * Math.cos(0), centerY + size * Math.sin(0));
for (var i = 1; i <= sideCount;i += 1) {
var x=centerX + size * Math.cos(i * 2 * Math.PI / sideCount);
var y=centerY + size * Math.sin(i * 2 * Math.PI / sideCount);
ctx.lineTo (x, y);
}
ctx.stroke();
ctx.fill();
ctx.restore();
}
Animating odd-sided polygons into even-sided polygons
In this illustration, you can animate a triangle into a square by animating each colored vertex on the triangle to its corresponding position on the square. All odd-sided polygons are transformed into even-sided polygons that same way.
Animating even-sided polygons into odd-sided polygons
In this illustration, you can animate a square into a pentagon by animating each colored vertex on the square to its corresponding position on the pentagon. In this case, you have to also cut the left-most yellow vertex in two and animate the two portions to the two yellow vertices on the pentagon. All even-sided polygons are transformed into odd-sided polygons that same way.
Here’s code and a Fiddle: http://jsfiddle.net/m1erickson/DjV5f/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; padding:10px; }
canvas{border:1px solid red;}
p{font-size:24px;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
//
var colors=["","blue","green","black"];
drawPolygon(3,50,70,70,2,"blue","red",colors,true);
colors=["","blue","yellow","green","black"];
drawPolygon(4,50,215,70,2,"blue","red",colors,false);
//
ctx.beginPath();
ctx.moveTo(0,162);
ctx.lineTo(300,162);
ctx.stroke();
//
var colors=["black","blue","yellow","green"];
drawPolygon(4,50,70,250,2,"blue","red",colors,true);
colors=["black","blue","yellow","yellow","green"];
drawPolygon(5,50,215,250,2,"blue","red",colors,false);
function drawPolygon(sideCount,size,centerX,centerY,strokeWidth,strokeColor,fillColor,colorPts,showBursePoint){
// draw a regular polygon with sideCount sides
ctx.save();
ctx.beginPath();
ctx.moveTo (centerX + size * Math.cos(0), centerY + size * Math.sin(0));
for (var i = 1; i <= sideCount;i += 1) {
var x=centerX + size * Math.cos(i * 2 * Math.PI / sideCount);
var y=centerY + size * Math.sin(i * 2 * Math.PI / sideCount);
ctx.lineTo (x, y);
}
ctx.fillStyle=fillColor;
ctx.strokeStyle = strokeColor;
ctx.lineWidth = strokeWidth;
ctx.stroke();
ctx.fill();
ctx.restore();
// draw vertex points
for (var i = 1; i <= sideCount;i += 1) {
var x=centerX + size * Math.cos(i * 2 * Math.PI / sideCount);
var y=centerY + size * Math.sin(i * 2 * Math.PI / sideCount);
drawPoint(x,y,colorPts[i]);
}
// draw point where this poly will "burst" to create next poly
if(showBursePoint){
var burstX= centerX + size * Math.cos( Math.floor(sideCount/2) * 2 * Math.PI / sideCount);
var burstY= centerY;
drawPoint(burstX, burstY, "yellow");
}
}
function drawPoint(x,y,fill){
ctx.save()
ctx.strokeStyle="black";
ctx.lineWidth=2;
ctx.fillStyle=fill;
ctx.beginPath();
ctx.arc(x,y,6,0,Math.PI*2,false);
ctx.fill();
ctx.stroke();
ctx.restore();
}
}); // end $(function(){});
</script>
</head>
<body>
<p>Regular polygons (3-8 sides)</p><br/>
<p>Unmoving anchor point is green</p><br/>
<p>Burst point is yellow</p><br/>
<canvas id="canvas" width=300 height=350></canvas>
</body>
</html>

Related

Why are multiple rotate() needed to place every number on the right spot? - Canvas Clock Numbers

Following this tutorial which shows how to make an analog clock using HTML canvas, I've had a hard time understanding what is going on when placing numbers on the clock face.
The code is here, and the following is the part that I'd like to ask.
function drawNumbers(ctx, radius) {
var ang;
var num;
ctx.font = radius * 0.15 + "px arial";
ctx.textBaseline = "middle";
ctx.textAlign = "center";
for(num = 1; num < 13; num++){
ang = num * Math.PI / 6;
ctx.rotate(ang);
ctx.translate(0, -radius * 0.85);
ctx.rotate(-ang);
ctx.fillText(num.toString(), 0, 0);
ctx.rotate(ang);
ctx.translate(0, radius * 0.85);
ctx.rotate(-ang);
}
}
In a for loop, the first ctx.rotate(ang) sets the number on the place it's supposed to be.
The next rotate ctx.rotate(-ang) puts the number back to upright because it's tilted. (although I don't know why it works like this not putting the number back to the first position.)
Then, after ctx.fillText(…) shows the number up, it seems to do the same again.
Why are these two rotate() needed? Do they work differently from the ones in the upper? If do, how?
What this code tries to do is to go back to its previous position, the center of the canvas.
Think of the context as a sheet of paper that you can rotate and move (translate), with a fixed pen over it.
First they do rotate that sheet of paper so that tracing a vertical line will go in the desired direction.
Then they move the sheet of paper vertically, so that the pen is at the correct position. However here, the sheet of paper is still rotated, so if they were to draw the text horizontally from here, the drawing would be oblique.
So they do rotate again in the other way for the text to be at correct angle.
They draw the text.
Now they want to go back to point 1 to be able to draw the next tick. For this they do the same route but in the other way: rotate back the sheet of paper to the desired angle so that they can move vertically to the center.
Move vertically to the center
Finally rotate back so that the sheet of paper is in its original orientation for the next tick.
However you should not do this. rotate() may end up having rounding issues, so doing rotate(angle); rotate(-angle) can not come back to the initial orientation, but to some slightly rotated state, which can be catastrophic for your application since now when you'll try to draw pixel perfect lines, you won't be able and you will kill the whole performances of your app.
Instead use the absolute setTransform method to go back to the original position:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var radius = canvas.height / 2;
radius = radius * 0.90
drawNumbers(ctx, radius);
function drawNumbers(ctx, radius) {
var ang;
var num;
ctx.font = radius * 0.15 + "px arial";
ctx.textBaseline = "middle";
ctx.textAlign = "center";
for(num = 1; num < 13; num++){
ang = num * Math.PI / 6;
// go (back) to center
ctx.setTransform(1, 0, 0, 1, radius, radius);
ctx.rotate(ang);
ctx.translate(0, -radius * 0.85);
ctx.rotate(-ang);
ctx.fillText(num.toString(), 0, 0);
}
// reset to identity matrix;
ctx.setTransform(1, 0, 0, 1, 0, 0);
}
canvas {
background-color: white;
}
<canvas id="canvas" width="400" height="400">
</canvas>
Here is another implementation without using rotate.
Instead I calculate the x, y with a bit of trigonometry.
The starting angle is var ang = Math.PI;
Then in the loop we decrease it ang -= Math.PI / 6;
Calculating the position is easy once you know the formula:
let x = radius * Math.sin(ang)
let y = radius * Math.cos(ang)
Below is a fully functional example
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.font = "16px arial";
ctx.textAlign = "center";
var radius = 60
var ang = Math.PI;
for (let num = 1; num < 13; num++) {
ang -= Math.PI / 6;
let x = radius * Math.sin(ang)
let y = radius * Math.cos(ang)
ctx.fillText(num.toString(), x, y);
ctx.beginPath()
ctx.arc(x, y - 6, 12, 0, 2 * Math.PI);
ctx.stroke();
ctx.beginPath()
ctx.arc(x, y - 6, 45, -ang-2,-ang);
ctx.stroke();
}
<canvas id="canvas" width="160" height="160"></canvas>
I personally never been a fan of using rotate, for a small static canvas image might be fine, but as we move to more complex animations with multiple object, when I have to debug with multiple rotation it quickly becomes painful and quite hard to follow.

Draw points on a circle's edge

I want draw something like this in javascript:
Example:
The bigger circle: r = 5
What do I want to do? to position circles by forming a circle
My questions are (I would like to know):
How much points can I draw on a circle if I know the ray,
the distance between each circle and the radius of each circle?
How can I find the position of each circle (to draw automatically)?
Like the question 1 but the circles do not have the same radius.
Thank you!
Displace points around a circle edge equidistantly
Using HTML Canvas and a bit of trigonometry
Create a reusable circles() function and pass the desired arguments for Number of circles, Size, Radius, Color:
const ctx = document.getElementById("canvas").getContext("2d");
const cvsSize = 400;
ctx.canvas.width = cvsSize;
ctx.canvas.height = cvsSize;
function circles(tot, rad, dist, color) {
const arc = Math.PI * 2 / tot; // Arc in Radians
let ang = 0; // Start at angle 0 (East)
for (let i = 0; i < tot; i++) {
const x = dist * Math.cos(ang) + (cvsSize / 2);
const y = dist * Math.sin(ang) + (cvsSize / 2);
ctx.beginPath();
ctx.arc(x, y, rad, 0, Math.PI * 2, false);
ctx.fillStyle = color;
ctx.fill();
ctx.closePath();
ang += arc;
}
}
// Circles, Radius, Distance, Color
circles(3, 5, 10, "#f0b");
circles(10, 8, 50, "#0bf");
circles(17, 10, 90, "#bf0");
circles(21, 15, 140, "#b0f");
<canvas id="canvas"></canvas>

Calculate rotation of canvas gradient

I'm trying to use a gradient to fill an area of a canvas, but I would like to be able to set the angle of the gradient.
I know this is possible by using different values in the creation of the gradient (ctx.createLinearGradient(x1, y1, x2, y2)) as seen here:
But I can't seem to get my head around the maths needed to convert an angle (radians) to a gradient size that will produce the same angle (The angle I'm referring to is perpendicular to the direction of the gradient, so a 0 radian angle would show the gradient on the right)
In short, how can I convert (quantity) of radians into an X by Y shape?
$(document).ready(function(){
var canvas = document.getElementById("test");
var ctx = canvas.getContext("2d");
var angle = 0.5;
ctx.beginPath();
ctx.moveTo(100, 100);
ctx.arc(100, 100, 100, 0, -angle, true);
ctx.lineTo(100, 100);
ctx.closePath();
// Convert angle into coordinates to tilt the grad
// grad should be perpendicular to the top edge of the arc
var grad = ctx.createLinearGradient(0, 0, 0, 100);
grad.addColorStop(0, "rgba(0,0,0,0)");
grad.addColorStop(1, "rgba(0,0,0,0.8)");
ctx.fillStyle = grad;
ctx.fill();
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="test" width="500" height="500"></canvas>
(So no one wastes their time: I specifically don't want to use a context.rotate() in this case)
You can use the angle with cos and sin to define the line that gives the gradient. The only thing left then is to give the length:
var length = 100, angle = 0;
ctx.createLinearGradient(x, y, x + Math.cos(angle) * length, y + Math.sin(angle) * length);
The gradient will be rendered along (perpendicular) to the line given.
Not stated, but if you need to calculate the length of the line depending on the angle and box you can use the law of sines to do so (used in this way). The example below uses a fixed radius. You can also use max length from (x1, x2) by calculating the hypotenuse: length = Math.sqrt(diffX*diffX + diffY*diffY);.
Example
var ctx = c.getContext("2d"),
x1 = 150, y1 = 150, x2, y2, angle,
length = 150;
render();
cAngle.oninput = render;
function render() {
angle = +cAngle.value / 180 * Math.PI;
// calculate gradient line based on angle
x2 = x1 + Math.cos(angle) * length;
y2 = y1 + Math.sin(angle) * length;
// create and render gradient
ctx.fillStyle = ctx.createLinearGradient(x1, y1, x2, y2);
ctx.fillStyle.addColorStop(0, "#fff");
ctx.fillStyle.addColorStop(1, "#07f");
ctx.fillRect(0, 0, 300, 300);
// show definition line
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
}
<label>Angle: <input id=cAngle max=359 type=range value=0></label><br>
<canvas id=c height=300></canvas>

Changing Stroke color in HTML 5 Canvas

I'm trying to draw a circle, kind of a clock, i start at point p1 and draw an arc in black using the canvas 2d context, when i reach the point p1 (complete circle tour) i change color to white, and continue to draw, that should give it an effect like if the black arc is being erased, however this doesn't work as expected, because when i change the context's color, everything change...
how to keep the first circle, in a color, and draw another one on topof it with different color, without changing the color in the whole scene ?
here's my attempt
<!DOCTYPE html />
<html>
<head>
<title></title>
<script type="text/javascript">
var i = 0.01;
var Color = "Black";
var x = 75; // x coordinate
var y = 75; // y coordinate
var radius = 20; // Arc radius
var startAngle = 0; // Starting point on circle
var anticlockwise = false; // clockwise or anticlockwise
function draw() {
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
var endAngle = i; // End point on circleMath.PI + (Math.PI * 5) /
if (Math.floor(endAngle) >= 6) {
i = 0.01;
if (Color == "Black") {
Color = "White";
} else {
Color = "Black";
}
}
ctx.strokeStyle = Color;
ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);
ctx.stroke();
i = i + 0.05;
//document.getElementById('display').innerHTML = Math.floor(endAngle) + " " + Color;
}
</script>
</head>
<body onload="window.setInterval(function(){draw()},100);">
<canvas id="canvas" width="150" height="150"></canvas>
<span id="display"></span>
</body>
</html>
You're having a little trouble with your angles. You're essentially redrawing the arc from 0 to your endAngle every time. So at the end when endAngle is greater than 6 you're redrawing from 0-6 with a white arc.
The easy fix is to just set endAngle = 0.01 when you reset i. You may also want to update your startAngle on each iteration to be the end of your last arc, just so that it doesn't draw over itself all the time.
Hope this helps!
Using Shaded's answer, you could do the following:
if (Math.floor(endAngle) > 6.0) {
i = 0.01;
endAngle = i;
startAngle = 0;
if (Color == "Black") {
Color = "White";
ctx.lineWidth = 4;
} else {
Color = "Black";
ctx.lineWidth = 1;
}
}
ctx.strokeStyle = Color;
ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);
ctx.stroke();
startAngle = endAngle - 0.1;
Because the white will anti-alias with the black behind it, you'll get the jaggies at the edges if the line widths are the same. Increasing the line width alleviates this issue.
EDIT: Updated to remove excessive over-painting as per Shaded's comments.

drawing Inscribe a Circle in a Triangle using a canvas

I am creating
"Inscribe a Circle in a Triangle using a canvas". But facing lots of problem. Well I tried to draw and Triangle in the middle of the canvas though its created i am wondering where to start drawing an circle which could be perfectly work for me.
With respective to mathematically I knew to draw circle, but when it comes to java script i am stuck.
kindly help me.
Thanks.
i have tried the following code to draw an traing at center of the canvas:-
var c=document.getElementById("myCanvas");
var context =c.getContext("2d");
check(ctx, 100, c.width/2, c.height/2);
function check(ctx, side, cx, cy){
var h = side * (Math.sqrt(3)/2);
ctx.strokeStyle = "black";
ctx.save();
ctx.translate(cx, cy);
ctx.beginPath();
ctx.moveTo(0,-h/2);
ctx.lineTo(-side/2, h / 2); // line a
ctx.lineTo(side /2, h / 2); // line b
ctx.lineTo(0,-h /2); // line c
ctx.stroke();
ctx.closePath();
ctx.save();
}
like this i want..
Ok check this .. Live Demo for equilateral triangle
Radius of circle inscribed in equilateral triangle = Sqrt(3)/6 * side
of triangle;
window.onload = function()
{
var c=document.getElementById("myCanvas");
var context =c.getContext("2d");
check(context,100,c.width/2,c.height/2);
circle(context,100,c.width/2,c.height/2);
}
function check(ctx, side, cx, cy){
var h = side * (Math.sqrt(3)/2);
ctx.strokeStyle = "black";
ctx.save();
ctx.translate(cx, cy);
ctx.beginPath();
ctx.moveTo(0,-h/2);
ctx.lineTo(-side/2, h / 2); // line a
ctx.lineTo(side /2, h / 2); // line b
ctx.lineTo(0,-h /2); // line c
ctx.stroke();
ctx.closePath();
ctx.restore();
}
function circle(ctx,side,cx,cy)
{
var h = side * (Math.sqrt(3)/2);
var radius = Math.sqrt(3)/6 * side; // Radius of the circle
cy = cy + h/2 - radius; // Center Y of circle
ctx.beginPath();
ctx.arc(cx,cy,radius,0,Math.PI * 2,false);
ctx.stroke();
ctx.closePath();
}
Check all formulas to find the radius of circle inscribed in different triangles here

Categories