Rotating figure 8 - leaving the path on screen in time intervals - javascript

I would like the figure 8 thats rotating around the center point to print itself (and stay there) along the circle in certain frequencies.
I need to see the overlap printed on the screen and draw its pattern around its orbit.
requestAnimationFrame(animate);
function animate(){
requestAnimationFrame(animate);
ctx.beginPath();
ctx.arc(cx,cy,10,0,Math.PI*2);
ctx.closePath();
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.fill();
ctx.save();
ctx.translate(cx,cy);
ctx.rotate(rotation);
ctx.beginPath();
for (var t = 0.0; t < 15; t += 0.1){
console.log(x, y);
x = Math.sin(t)*20 + 150;
y = Math.sin(a * t + b)*20 + 150;
dy = Math.cos(a * t + b) * a;
ctx.lineTo(x, y);
if (dy < 0.1 && dy > -0.1)
ctx.fillRect(x,y,0,0);
ctx.stroke();
}
ctx.restore();
rotation+=Math.PI/50;
}
I started a codepen
https://codepen.io/braydendevito/pen/zQEJjz
I'm guessing it needs a loop, but I can't seem to get this to work.
I appreciate it.

Related

context.moveTo() x and y parameters are not decrementing using requestAnimationFrame

I have two lines at a 90 degree angle and I want to make the angle get smaller and larger between 90 and 0 by rotating the vertical line. I'm trying to do this my changing the moveTo parameters I could increase the value but not decrease them. also could you help me make sure that the line that moves is the same length as the horizontal line during the animation. now it looks like it gets smaller then gets larger as it completes.
window.onload = function(){
var canvas =document.getElementById("canvas");
var context = canvas.getContext("2d");
var length = 50;
var x = 50;
var y= 50;
var forward = true;
(function animate(){
if(x <= 201 && y <= 201){
x++
y++
}
if(x > 195){
forward = false;
}
// console.log(x, y)
if(forward == false){
// alert("yo")
x = x - 1
y = y -1
}
console.log(x)
console.log(forward)
context.clearRect(0,0, canvas.width, canvas.height)
context.beginPath();
context.moveTo(x,y)
context.lineTo(50,200);
context.stroke();
context.closePath();
window.requestAnimationFrame(animate)
context.beginPath();
context.moveTo(50, 200);
context.lineTo(200, 200)
context.stroke();
context.closePath();
}())
}
<canvas id="canvas" width="400" height="400"></canvas>
EDIT:::
window.onload = function(){
var canvas =document.getElementById("canvas");
var context = canvas.getContext("2d");
var length = 50;
var x = 50;
var y= 50;
var dlt = 1
var forward = true;
var i = 0;
(function animate(){
if(x >= 50 && x < 200 ){
i++
x += dlt
y += dlt
console.log(i)
$(".display").html(i)
if(i >= 150){
X = 200;
Y = 200;
x -= dlt
y -= dlt
}
}
console.log("x", x)
console.log(forward)
context.clearRect(0,0, canvas.width, canvas.height)
context.beginPath();
context.moveTo(x,y)
context.lineTo(50,200);
context.stroke();
context.closePath();
window.requestAnimationFrame(animate)
context.beginPath();
context.moveTo(50, 200);
context.lineTo(200, 200)
context.stroke();
context.closePath();
}())
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="canvas" width="400" height="400"></canvas>
You can use transformations instead of trigonometry to draw your vertically collapsing line:
Here is annotated code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var nextTime=0;
var delay=100;
var angle=-Math.PI/2;
var cx=150;
var cy=150;
var radius=50;
requestAnimationFrame(animate);
function animate(time){
// wait until the desired time elapses
if(time<nextTime){requestAnimationFrame(animate); return;}
nextTime+=delay;
// draw the horizontal line
ctx.clearRect(0,0,cw,ch);
ctx.beginPath();
ctx.moveTo(cx,cy);
ctx.lineTo(cx+radius,cy);
ctx.stroke();
// use transformations to draw the vertical line
// at the desired angle
ctx.translate(cx,cy);
ctx.rotate(angle);
ctx.beginPath();
ctx.moveTo(0,0);
ctx.lineTo(radius,0);
ctx.stroke();
ctx.setTransform(1,0,0,1,0,0);
// if the vertical line isn't horizontal,
// request another animation frame
if(angle<0){ requestAnimationFrame(animate); }
// adjust the angle
angle+=Math.PI/90;
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<canvas id="canvas" width=300 height=300></canvas>
If you want to use Trig then you can calculate the line endpoint like this:
var lineX1 = lineX0 + radius*Math.cos(angle);
var lineY1 = lineY1 + radius*Math.sin(angle);
[Addition: add trigonometry to questioner's code]
Here is your code refactored to use trigonometry to reposition the vertical line.
var canvas =document.getElementById("canvas");
var context = canvas.getContext("2d");
var length = 50;
var x = 50;
var y= 50;
var dlt = 1
var forward = true;
var i = 0;
var angle=-Math.PI/2;
var direction=1;
(function animate(){
/*
if(x >= 50 && x < 200 ){
i++
x += dlt
y += dlt
console.log(i)
$(".display").html(i)
if(i >= 150){
X = 200;
Y = 200;
x -= dlt
y -= dlt
}
}
*/
var moveX=50+(200-50)*Math.cos(angle);
var moveY=200+(200-50)*Math.sin(angle);
// change the angle
angle+=(Math.PI/120*direction);
// if the angle is beyond vertical or horizontal then
// swing it the other way
if(angle<-Math.PI/2 || angle>0){ direction*=-1;}
context.clearRect(0,0, canvas.width, canvas.height)
context.beginPath();
context.moveTo(moveX,moveY)
context.lineTo(50,200);
context.stroke();
// context.closePath();
context.beginPath();
context.moveTo(50, 200);
context.lineTo(200, 200)
context.stroke();
// context.closePath();
window.requestAnimationFrame(animate)
}())
<canvas id="canvas" width=300 height=300></canvas>
Sin and Cos
All graphics programmers should know these two trigonometric functions thoroughly.
MarkE has given a good answer but I will point out the simple trig method that you can use to find a point that is a desired distance and angle.
Lets say the line you want has a length,
var length = 100;
its starts at,
var posX = 200;
var posY = 200;
the unknown end location will be,
var endX;
var endY;
and the angle you want is
var angle = 0; // in radians
Radians V Degrees
In javascript all Math functions that require angles use angle as radians.
The above angle is in radians, with angle = 0 pointing from left to right across the screen, angle = Math.PI/2 (90deg) from top down the screen, angle = Math.PI (180deg) from right to left across the screen and angle = Math.PI * (3/2) (270deg) from bottom to top up the screen.
If you like to work in degrees then you can convert to radians by multiplying degrees by Math.PI/180.
Function to convert degrees to radians
function degree2Radians(deg){
return deg * (Math.PI/180);
}
Back to getting the line at an angle. We will use the trig functions Math.cos and Math.sin see Wiki trigonometric functions for details.
So the calculation in steps
var x,y; // temp working variables
x = Math.cos(angle); // get the amount of x in the line for angle
y = Math.sin(angle); // get the amount of y in the line for angle
// cos and sin will return values -1 to 1 inclusive.
// now scale the values to the length of the line
x *= length;
y *= length;
// now you have the offset from the start of the line
// to get the end point add the start
endX = x + posX;
endY = y + posY;
Now you can draw the line at the angle you wanted.
ctx.beginPath();
ctx.moveTo(posX,posY);
ctx.lineTo(endX,endY);
ctx.stroke();
Of course that was a long way. It all can be done in two steps.
endX = Math.cos(angle) * length + posX;
endY = Math.sin(angle) * length + posY;
or if you like a function that works in degrees
// add to the current path a line from
// startX,startY of
// length pixels long
// at the angle angleDeg in degrees. Negative anticlockwise positive clockwise
function lineAtAngle(ctx, startX, startY, angleDeg, length){
var angle = angleDeg * (Math.PI / 180);
ctx.moveTo(startX, startY);
ctx.lineTo(
Math.cos(angle) * length + startX,
Math.sin(angle) * length + startY
)
}
And to use it
ctx.beginPath(); // begin the line
lineAtAngle(ctx,200,200,-45,100); // draw a line at -45deg
// (from bottom left to top right)
// 100 pixels long.
ctx.stroke(); // draw the line
Thats how to use sin and cos to draw a line at an angle and length.

Incorporate rotation into regular polygon algorithm

I wrote this algorithm to draw a regular polygon:
var sides = 6,
radius = 50;
ctx.beginPath();
ctx.moveTo(x, y - radius);
for(n = 1; n <= sides; n++)
ctx.lineTo(
x + (radius * Math.sin(n * 2 * Math.PI / sides)),
y + (-1 * radius * Math.cos(n * 2 * Math.PI / sides))
);
ctx.stroke();
It works wonderfully, but I need to incorporate a way to rotate the polygon, without using the ctx.rotate() function.
Thanks for your help. If you down vote, please tell me why so I can improve this question.
Here's code to generate a regular polygon with the first vertex at zero-angle-right-of-center:
The code uses trigonometry to rotate the polygon instead of context.rotate.
function regularPolygon(cx,cy,sides,radius,radianRotation){
var deltaAngle=Math.PI*2/sides;
var x=function(rAngle){return(cx+radius*Math.cos(rAngle-radianRotation));}
var y=function(rAngle){return(cy+radius*Math.sin(rAngle-radianRotation));}
ctx.beginPath();
ctx.moveTo(x(0),y(0));
for(n = 1; n <= sides; n++){
var angle=deltaAngle*n;
ctx.lineTo(x(angle),y(angle));
}
ctx.closePath();
ctx.stroke();
}

Filling a custom-drawn shape in Javascript

I'm trying to fill an ellipse that I've made, but although I can get it to draw the outline, I can't get it to fill it. I've looked at a bunch of resources, including http://www.html5canvastutorials.com/tutorials/html5-canvas-shape-fill/ and https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Canvas_tutorial/Drawing_shapes, but following the advice there didn't fix the problem. I've tried to account for other errors - like spelling mistakes, errors with passing parameters, or errors with my ellipse-drawing method, but they all work independently just fine. I can draw the outline of ellipse. I can pass the context to a function. I can fill a non-ellipse. But I can't get my ellipse to fill. Here is what the code looks like:
main();
function main(){
var canvas = document.getElementById('landscape');
var context = canvas.getContext('2d');
// var mySky = new sky(0, 0);
// mySky.render(context);
var myLake = new lake(400, 500, context);
myLake.render(context);
var ctx = context;
ctx.beginPath();
ctx.moveTo(75,50);
ctx.lineTo(100,75);
ctx.lineTo(100,25);
ctx.fill();
}
function lake(x, y, context){
this.context = context;
this.x = x;
this.y = y;
var width = this.context.canvas.width/2;
var height = this.context.canvas.height/4;
var a = width/2;
var b = height/2;
var phi = Math.PI/2;
this.render = function(context){
var inc = (2*Math.PI)/200;
var end = 200*inc;
var oldX = oldY = newX = newY = 0;
var x_0 = xcoord(0);
var y_0 = ycoord(0);
console.log("" + x_0 + ", " + y_0);
var i = 0;
context.beginPath();
context.moveTo(x_0, y_0);
while(i < end){
i += inc;
newX = xcoord(i);
newY = ycoord(i);
context.lineTo(newX, newY);
context.moveTo(newX, newY);
console.log("" + newX + ", " + newY);
}
context.lineTo(x_0, y_0); // close up the ellipse
context.moveTo(x_0, y_0);
context.closePath();
context.fillStyle = '#6EB1F5';
context.fill();
}
function xcoord(t){
return x + a*Math.cos(t)*Math.sin(phi) + b*Math.sin(t)*Math.cos(phi);
}
function ycoord(t){
return y + a*Math.cos(t)*Math.cos(phi) - b*Math.sin(t)*Math.sin(phi);
}
Am I using the fill() function correctly? Is it because my ellipse isn't closed up properly? Please don't give me too much information if possible - I'd like to do it on my own, I just can't figure out what's wrong and I've spent nearly 3 hours trying to figure this out now.
Try by removing the moveTo the following places:
context.beginPath();
context.moveTo(x_0, y_0); /// keep this
while(i < end){
i += inc;
newX = xcoord(i);
newY = ycoord(i);
context.lineTo(newX, newY);
///context.moveTo(newX, newY); /// remove this
console.log("" + newX + ", " + newY);
}
///context.lineTo(x_0, y_0); /// not needed as closePath will close it
///context.moveTo(x_0, y_0); /// remove this
context.closePath();
As you are using moveTo for each new coordinates you will create sub-paths consisting of only a single line which can't be filled. You want to create a continuous line which are closed at the end forming a closed polygon.
Besides from that, you are using fill() correctly.
Since you're asking, there are easier and cheaper ways of drawing an ellipse.
Something like the following:
function ellipse(context, x, y, a, b, theta) {
context.beginPath();
context.save();
/* translate to avoid having our x and y values scaled */
context.translate(x, y);
/* we can even do some rotation. (rotate before stretching!) */
context.rotate(theta);
/* now stretch the axes */
context.scale(a, b);
/* circle of radius 1, centred at the origin */
context.arc(0, 0, 1, 0, 2*Math.PI, false);
/* undo transformations */
context.restore();
context.closePath();
}
JSFiddle here.
function ellipse(context, x, y, a, b, theta) {
context.beginPath();
context.save();
/* translate to avoid having our x and y values scaled */
context.translate(x, y);
context.scale(Math.random() * 1 ,Math.random() * 1);
/* we can even do some rotation. (rotate before stretching!) */
context.rotate(theta);
/* now stretch the axes */
context.scale(a, b);
/* circle of radius 1, centred at the origin */
context.arc(0, 0, 1, 0, 2*Math.PI, false);
/* undo transformations */
context.restore();
context.closePath();};
setInterval(function(){abc()}, 100);
var c = document.getElementById("c");
var ctx = c.getContext("2d");
function abc()
{
c.width = c.width;
for (var i=0; i < 20; i++) {
ellipse(ctx, i*50+25, 100, 20, 30, Math.random() * 10 - 5);
if (i % 3)
ctx.fill();
else
ctx.stroke();
};
};

Canvas - How to draw the grid of the dots with the light source in the center?

I think it's usual question, but I have some problems with displaying dots in canvas. The first thing I'd like to know is how to draw dot like this (please zoom it).
The second thing is, how to draw a shadow to each element of the grid of this dots, with the light source in the center.
What I have at this moment right here:
the part of my code:
context.fillStyle = "#ccc";
context.shadowColor = '#e92772';
context.shadowOffsetX = 15;
context.shadowOffsetY = 15;
while (--e >= 1) {
x -= z;
if(x < 0) {
x = z*w;
y -= z;
}
context.moveTo(x, y);
context.fillRect( x, y, 1, 1 );
outs = a[e];
}
Also, I've tried to use "context.arc();", but I think "context.fillRect();" is more easier. And one else moment, when I use "while (--e >= 0)" instead of "while (--e >= 1)" I have two more dots, on the top. Why?
If you know some articles or tutorials, would you give me the link to them. Preferably without the use of the frameworks. Thanks.
You can use some trigonometry to simulate 3D dots with a light source.
HERE IS AN ONLINE DEMO
This is one way you can do it, there are of course others (this was the first that came to mind):
Draw the grid with some dots on the main canvas
Render a radial gradient to an off-screen canvas
Change composition mode so anything is draw on already existing pixels
Calculate distance and angle to light source and draw the dot to each grid point offset with the angle/dist.
Here is some code from the demo that does this.
Draw the grid with dots
We skip one grid point as we will fill each dot later with the gradient dot which otherwise would paint over the neighbor dot.
/// draw a grid of dots:
for (y = 0; y < ez.width; y += gridSize * 2) {
for (x = 0; x < ez.height; x += gridSize * 2) {
ctx.beginPath();
ctx.arc(x + offset, y + offset, radius, 0, arcStop);
ctx.closePath();
ctx.fill();
}
}
Create a light "reflection"
Prepare the gradient dot to an off-screen canvas (dctx = dot-context). I am using easyCanvas for the setup and to give me an off-screen canvas with center point already calculated, but one can setup this manually too of course:
grd = dctx.createRadialGradient(dot.centerX, dot.centerY, 0,
dot.centerX, dot.centerY, gridSize);
grd.addColorStop(0, '#fff');
grd.addColorStop(0.2, '#777'); // thighten up
grd.addColorStop(1, '#000');
dctx.fillStyle = grd;
dctx.fillRect(0, 0, gridSize, gridSize);
Do the math
Then we do all the calculation and offsetting:
/// change composition mode
ctx.globalCompositeOperation = 'source-atop';
/// calc angle and distance to light source and draw each
/// dot gradient offset in relation to this
for (y = 0; y < ez.width; y += gridSize) {
for (x = 0; x < ez.height; x += gridSize) {
/// angle
angle = Math.atan2(lightY - y, lightX - x);
//if (angle < 0) angle += 2;
/// distance
dx = lightX - x;
dy = lightY - y;
dist = Math.sqrt(dx * dx + dy * dy);
/// map distance to our max offset
od = dist / maxLength * maxOffset * 2;
if (od > maxOffset * 2) od = maxOffset * 2;
/// now get new x and y position based on angle and offset
offsetX = x + od * Math.cos(angle) - maxOffset * 0.5;
offsetY = y + od * Math.sin(angle) - maxOffset * 0.5;
/// draw the gradient dot at offset
ctx.drawImage(dot.canvas, x + offsetX, y + offsetY);
}
}
Shadow
For the shadow you just inverse the offset while using the composition mode destination-over which will draw outside the already drawn pixels:
/// Shadow, same as offsetting light, but in opposite
/// direction and with a different composite mode
ctx.globalCompositeOperation = 'destination-over';
for (y = 0; y < ez.width; y += gridSize) {
for (x = 0; x < ez.height; x += gridSize) {
/// angle
angle = Math.atan2(lightY - y, lightX - x);
//if (angle < 0) angle += 2;
/// distance
dx = lightX - x;
dy = lightY - y;
dist = Math.sqrt(dx * dx + dy * dy);
/// map distance to our max offset
od = dist / maxLength * maxOffset * 2;
if (od > maxOffset * 4) od = maxOffset * 4;
/// now get new x and y position based on angle and offset
offsetX = x - od * Math.cos(angle) + gridSize * 0.5;
offsetY = y - od * Math.sin(angle) + gridSize * 0.5;
ctx.beginPath();
ctx.arc(x + offsetX, y + offsetY, radius, 0, arcStop);
ctx.fill();
}
}
This can all be optimized of-course into a single loop pair but for overview the code is separated.
Additional
In the demo I added mouse tracking so the mouse becomes the light-source and you can see the dot-reflection changes while you move the mouse. For best performance use Chrome.
To match your need just scale down the values I am using - or - draw to a big off-screen canvas and use drawImage to scale it down to a main canvas.

radial motion in javascript

By default HTML 5 canvas has rectangular shape, though if i do any animation under canvas, it will move into rectangular area.
What if i bound area to radial shape?It shouldn't definitely go out of radial shape.
Can we limit boundary to radial instead of default rectangular shape?
You can look at ball is going out of radial boundary- http://jsfiddle.net/stackmanoz/T4WYH/
I designed boundary in radial shape now i want to limit radial area too.
Limit area for ball-
function bounce() {
if (x + dx > 293 || x + dx < 0) {
dx = -dx;
}
if (y >= 290) {
y = 290;
}
if (y + dy > 290 || y + dy < 0) {
dx *= 0.99;
dy = -dy;
}
if (Math.abs(dx) < 0.01) {
dx = 0;
}
dy++;
}
The cartesian formula for a circle is (x − a)2 + (y − b)2 = r2
So check for this in your bounce condition:
function bounce() {
if( Math.pow(x - 150, 2) + Math.pow(y - 150, 2) > Math.pow(150, 2))
{
dx = -dx * (0.6 + (Math.random() * 0.8));
dy = -dy * (0.6 + (Math.random() * 0.8));
}
}
I am using random bouncing because I could not work out the correct bounce angle using the incident speed and the normal at the bounce point (I'm sure there is somebody ele here who can)
updated fiddle here: http://jsfiddle.net/T4WYH/1/
Few points, use requestAnimationFrame rather than setInterval
I would draw the large circle in the canvas, rather than as a border, then you can use isPointInPath(x, y) to work out if your ball is within the circle (or any other arbitrary path for that matter)
function draw() {
ctx.clearRect(0, 0, 300, 300);
ctx.beginPath()
ctx.lineWidth = 1;
ctx.strokeStyle = '#000';
ctx.arc(150, 150, 150, 0, 2 * Math.PI, true);
ctx.stroke();
ctx.closePath();
console.log(ctx.isPointInPath(x, y)); //Is (center) of ball in big circle
ctx.beginPath();
ctx.arc(x, y, 10, 0, 2 * Math.PI, true);
ctx.closePath();
ctx.fill();
x += dx;
y += dy;
bounce();
}
I don't want to implement this particular task, but you can get ideas how to bounce from circle from this site: bbdizains.com
And coresponding function:
move = function(){
for (i=1; i<=3; i++) {
A.x[i] = A.x[i] - A.v[i]*Math.sin(A.alfa[i]);
A.y[i] = A.y[i] - A.v[i]*Math.cos(A.alfa[i]);
x = A.x[i]-175;
y = A.y[i]-233;
x2 = x*x;
y2 = y*y;
if (x2+y2>=6561) {
if (A.flag[i]==1) {
gamma = Math.atan(y/x);
A.alfa[i] = - 2*gamma - A.alfa[i];
A.flag[i] = 0;
}
} else {
A.flag[i] = 1;
}
$('#r'+i).css('left',A.x[i]+'px');
$('#r'+i).css('top',A.y[i]+'px');
if ((Math.random()>0.95) && (x2+y2<6000)) {
A.alfa[i] = A.alfa[i] + Math.PI*Math.random()*0.1;
}
}
}

Categories