Creating Spiral in Alphanumeric using javascript - javascript

I want to create a spiral through canvas but in alphanumeric...
just like the code below but in alphanumeric..
it will start at A and end at 0...
<!DOCTYPE HTML>
<html><body>
<canvas id="myCanvas" width="300" height="300" style="border:1px solid #c3c3c3;"></canvas>
<script type="text/javascript">
var c=document.getElementById("myCanvas");
var cxt=c.getContext("2d");
var centerX = 150;
var centerY = 150;
cxt.moveTo(centerX, centerY);
var gap = 1.8; // increase this for spacing between spiral lines
var STEPS_PER_ROTATION = 60; // increasing this makes the curve smoother
var increment = 2*Math.PI/STEPS_PER_ROTATION;
var theta = increment;
while( theta < 20*Math.PI) {
var newX = centerX + theta * Math.cos(theta) * gap;
var newY = centerY + theta * Math.sin(theta) * gap;
cxt.lineTo(newX, newY);
theta = theta + increment;
}
cxt.stroke(); // draw the spiral
</script></body></html>

Rotating and drawing text in the Canvas object isn't the easiest thing to do for someone who hasn't done it before. But that doesn't mean that it's hard.
The first part is drawing the text, so to start conversion we have to remove cxt.lineTo(newX, newY) and add cxt.fillText(char, newX, newY) in
while(theta < 20*Math.PI) {
var newX = centerX + theta * Math.cos(theta) * gap;
var newY = centerY + theta * Math.sin(theta) * gap;
//cxt.lineTo(newX, newY);
cxt.fillText('a', newX, newY);
theta = theta + increment;
}
This will put the character a at every curve-point that the spiral was using, but they all face the same default text direction. So to fix that you must rotate the characters. Using cxt.rotate() and Math.atan2() you can rotate the text for that point in the circle. Using cxt.save(), cxt.restore(), and cxt.translate() you won't have to unrotate or use math to position your characters properly. Putting these together you get:
while( theta < 20*Math.PI) {
var newX = centerX + theta * Math.cos(theta) * gap;
var newY = centerY + theta * Math.sin(theta) * gap;
cxt.save()
cxt.translate(newX, newY);
cxt.rotate(Math.atan2(centerY - newY, centerX - newX));
cxt.fillText('a', 0, 0);
cxt.restore();
theta = theta + increment;
}
By adding (0..2)*Math.PI to the rotation, you can then add a static rotation to the characters, making them all face inwards, or all face outwards, etc.
Adding all of this together, along with a counter and a character map, you can make the spiral slowly grow in font size and get what I believe is roughly what you were looking for.
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="300" height="300" style="border:1px solid #c3c3c3;"></canvas>
<script type="text/javascript">
var c=document.getElementById("myCanvas");
var cxt=c.getContext("2d");
var centerX = 150;
var centerY = 150;
cxt.moveTo(centerX, centerY);
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'.split(''); // character map for spiral
var gap = 3; // increase this for spacing between spiral lines
var rotation = 0; // value between 0..1 that rotates the characters 0..360 degrees.
var spread = 1; // increasing this makes the spread more
var spirals = 10; // number of spirals
var STEPS_PER_ROTATION = 60; // increasing this adds more characters
var increment = spread*2*Math.PI/STEPS_PER_ROTATION;
var theta = increment;
var maxFont = 16;
cxt.font = '0px sans';
cxt.textBaseline = 'center';
let spiralCount = 2*spirals*Math.PI;
let char = 0;
while(theta < spiralCount) {
var newX = centerX + theta * Math.cos(theta) * gap;
var newY = centerY + theta * Math.sin(theta) * gap;
var rot = Math.atan2(newY - centerY, newX - centerX);
cxt.save();
cxt.translate(newX, newY);
cxt.rotate(rot + (rotation*2*Math.PI));
cxt.font = (maxFont*(theta/spiralCount)) + 'px sans';
cxt.fillText(characters[char], 0, 0);
cxt.restore();
theta = theta + increment;
char++;
if (char > characters.length - 1) char = 0;
}
cxt.stroke(); // draw the spiral
</script>
</body>
</html>

Related

How to remove straight lines from multiple rotated arc on canvas?

I want to create Star Trail on canvas.
The idea to create it, i have to draw multiple images that increment size, and some calculation for the next image position. Store multiple image in array. Choose the image randomly each draw and draw it with different angle each image.
I made it.
var canvas=document.getElementById('canvas'),ctx=canvas.getContext('2d');
var starsimage=['https://1.bp.blogspot.com/-prwJzDDwQRU/XTjNfQhQnDI/AAAAAAAACBo/wpbhqkfc-9wQQeg95O6poFbFyu77q4vdACLcBGAs/s1600/CircleWhite100.png','https://1.bp.blogspot.com/-lm4F2UOMCdE/XTjNghXjrOI/AAAAAAAACB0/L6pya6HQk0cU5R5RP9Wo_-Bm_UhO_qCawCLcBGAs/s1600/CircleWhite75.png','https://1.bp.blogspot.com/-7ennrlohEo0/XTjNfoji8KI/AAAAAAAACBw/G0SQhFEZ0IMMf2z3g_Mvbon97BMktSw-QCLcBGAs/s1600/CircleWhite50.png','https://1.bp.blogspot.com/-I7aeA-F4OWY/XTjNfiO-rxI/AAAAAAAACBs/lKYC-SmaWSQWc0PoPVdgCHeyDUPdoJd7gCLcBGAs/s1600/CircleWhite25.png','https://1.bp.blogspot.com/-V2Ak6YU2XNA/XTjNh7iIwZI/AAAAAAAACCA/tNo5Ho6iC4gndoftPJfSCInGqgyfcd6nQCLcBGAs/s1600/TrailBlue100.png','https://1.bp.blogspot.com/-ylpi3AZvces/XTjNi7uy1kI/AAAAAAAACCM/uWZ7_zYRXXQN4q3QRSngCFeT5RoEeG4xgCLcBGAs/s1600/TrailBlue75.png','https://1.bp.blogspot.com/-NoPiS9k0o0U/XTjNivmKVjI/AAAAAAAACCI/gZzDMn9zomMWrQc2hhKfNB9JK0ruh2wyQCLcBGAs/s1600/TrailBlue50.png','https://1.bp.blogspot.com/-KYkWDwtmS7A/XTjNiSE8Y_I/AAAAAAAACCE/jDyTIbJqBBs3FP1tyGFICjShyx_GCCy0gCLcBGAs/s1600/TrailBlue25.png'];
var stars=new Array();var starsloaded=0;
var xpos=0,ypos=0,w=25,h=25,ix=0,xa=25,ya=25,rot=0,ang=0;
for(var i=0;i<starsimage.length;i++){//load images
stars[i]=new Image();stars[i].src=starsimage[i];
stars[i].onload=function(){starsloaded++;if(starsloaded==starsimage.length){
draw();
}}
}
function minmaxdraw(min,max){return Math.floor(Math.random() * (max - min + 1)) + min}
function randomindeximage(l){return Math.floor(Math.random()*l)}
function draw(){
xpos=canvas.width/2;ypos=canvas.height/2;
for(var i=0;i<500;i++){//500 images
xy=minmaxdraw(2,4);//gap randomly
ix=randomindeximage(starsimage.length);
w+=xy;h+=xy;ang=minmaxdraw(0,359);
if(i>0){xa=w-xa;ya=h-ya;xpos=xpos-xa/2;ypos=ypos-ya/2;
ctx.save();
ctx.translate(xpos+w/2,ypos+h/2);
rot=ang*Math.PI/180;ctx.rotate(rot);
ctx.drawImage(stars[ix],-w/2,-h/2,w,h);
ctx.restore();
xa=w;ya=h;
}
else{ctx.drawImage(stars[ix],xpos,ypos,w,h)}
}
}
body{margin:0;padding:0;position:relative;background:#101010;height:580px;width:100%}
#canvas{z-index:2;background-color:transparent;position:absolute;width:100vw;left:50%;top:50%;transform:translate(-50%,-50%)}
body:before{content:'';z-index:1;position:absolute;width:100%;height:100%;left:0;top:0;background:linear-gradient(15deg, #1458ac, #000);}
<canvas id="canvas" width="640" height="580"></canvas>
But. the quality is bad. Because the image size is static, blurry if the increment width is larger than actually image size, and the small stars is barely visible
I think, it's better to create it with multiple arc.
The idea is same, i have to draw multiple arc. But, i'm dummy, i made with two canvas, different angle each canvas. (It's actually same with my work before, but at the time, i use 1 image that have cutted circle). Because if i made it only on one canvas, the straight line appears. So, i draw multiple arc in each canvas. It works, and no straight line appears. (It's still basic arc)
var canvas1=document.getElementById('canvas1'),ctx=canvas1.getContext('2d'),canvas2=document.getElementById('canvas2'),ctx2=canvas2.getContext('2d');var xy=0;var angle=new Array();
function minmaxdraw(min,max){return Math.floor(Math.random() * (max - min + 1)) + min}
function randomindeximage(l){return Math.floor(Math.random()*l)}
function draw1(){
var xpos=canvas1.width/2;var ypos=canvas1.height/2;var r=15,ang=0;
ctx.beginPath();
for(var i=0;i<55;i++){
r+=minmaxdraw(4,9);
ctx.moveTo(xpos + r, ypos);
ctx.arc(xpos, ypos, r, 0, 1.5*Math.PI,true);
}ctx.stroke();
}
function draw2(){
var xpos=canvas2.width/2;var ypos=canvas2.height/2;var r=15;
ctx2.beginPath();
for(var t=0;t<55;t++){
r+=minmaxdraw(4,9);
ctx2.moveTo(xpos, ypos + r);
ctx2.arc(xpos, ypos, r, Math.PI / 2, Math.PI);
}ctx2.stroke();
}
draw1();draw2();
body{margin:0;padding:0;position:relative;height:480px;width:100%}
.canvas{background-color:transparent;position:absolute;width:100vw;left:50%;top:50%;margin:-50% 0 0 -50%}
<canvas id='canvas1' class='canvas' width='640' height='480'></canvas>
<canvas id='canvas2' class='canvas' width='640' height='480'></canvas>
It's not rotated yet.
So, if i want make circle trail, i have to rotate each arc randomly like i did before with image, but the problem is here. The straight lines come wickedly.
var canvas1=document.getElementById('canvas1'),ctx=canvas1.getContext('2d'),canvas2=document.getElementById('canvas2'),ctx2=canvas2.getContext('2d');var xy=0;var angle=new Array();
function minmaxdraw(min,max){return Math.floor(Math.random() * (max - min + 1)) + min}
function randomindeximage(l){return Math.floor(Math.random()*l)}
function draw1(){
var xpos=canvas1.width/2;var ypos=canvas1.height/2;
var r=15,ang=0,dx=0,dy=0;
ctx.beginPath();
for(var i=0;i<55;i++){//200 images
ang=minmaxdraw(0,359);
dx=minmaxdraw(0,canvas1.width),dy=minmaxdraw(0,canvas1.height);
r+=minmaxdraw(3,7);
ang=Math.atan2(dx-xpos,dy-ypos);angle[i]=ang;
ctx.moveTo(xpos, ypos);
ctx.arc(xpos, ypos, r, ang + 0 * Math.PI, ang + Math.PI * 1.5,true);
}ctx.stroke();
}
function draw2(){
var xpos=canvas2.width/2;var ypos=canvas2.height/2;var r=15;
ctx2.beginPath();
for(var t=0;t<55;t++){
r+=minmaxdraw(3,7);
ctx2.moveTo(xpos, ypos + r);
ctx2.arc(xpos, ypos, r, angle[t] + Math.PI / 2, angle[t] + Math.PI);
}ctx2.stroke();
}
draw1();draw2();
body{margin:0;padding:0;position:relative;height:480px;width:100%}
.canvas{background-color:transparent;position:absolute;width:100vw;left:50%;top:50%;margin:-50% 0 0 -50%}
<canvas id='canvas1' class='canvas' width='640' height='480'></canvas>
<canvas id='canvas2' class='canvas' width='640' height='480'></canvas>
Is there any solution to remove that straight lines?
Not sure why you have the calls to moveTo and maybe I just didn't understand the question but I just moved beginPath and stroke inside the loops.
var canvas1 = document.getElementById('canvas1'),
ctx = canvas1.getContext('2d'),
canvas2 = document.getElementById('canvas2'),
ctx2 = canvas2.getContext('2d');
var xy = 0;
var angle = new Array();
function minmaxdraw(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min
}
function randomindeximage(l) {
return Math.floor(Math.random() * l)
}
function draw1() {
var xpos = canvas1.width / 2;
var ypos = canvas1.height / 2;
var r = 15,
ang = 0,
dx = 0,
dy = 0;
for (var i = 0; i < 55; i++) { //200 images
ang = minmaxdraw(0, 359);
dx = minmaxdraw(0, canvas1.width), dy = minmaxdraw(0, canvas1.height);
r += minmaxdraw(3, 7);
ang = Math.atan2(dx - xpos, dy - ypos);
angle[i] = ang;
ctx.beginPath();
ctx.arc(xpos, ypos, r, ang + 0 * Math.PI, ang + Math.PI * 1.5, true);
ctx.stroke();
}
}
function draw2() {
var xpos = canvas2.width / 2;
var ypos = canvas2.height / 2;
var r = 15;
for (var t = 0; t < 55; t++) {
r += minmaxdraw(3, 7);
ctx2.beginPath();
ctx2.arc(xpos, ypos, r, angle[t] + Math.PI / 2, angle[t] + Math.PI);
ctx2.stroke();
}
}
draw1();
draw2();
body {
margin: 0;
padding: 0;
position: relative;
height: 550px;
width: 100%
}
.canvas {
background-color: transparent;
position: absolute;
width: 100vw;
left: 50%;
top: 50%;
margin: -50% 0 0 -50%
}
<canvas id='canvas1' class='canvas' width='640' height='480'></canvas>
<canvas id='canvas2' class='canvas' width='640' height='480'></canvas>
Also I don't understand why you have 2 canvases. It works fine with just one
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d');
var xy = 0;
var angle = new Array();
function minmaxdraw(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min
}
function randomindeximage(l) {
return Math.floor(Math.random() * l)
}
function draw1() {
var xpos = canvas.width / 2;
var ypos = canvas.height / 2;
var r = 15,
ang = 0,
dx = 0,
dy = 0;
for (var i = 0; i < 55; i++) { //200 images
ang = minmaxdraw(0, 359);
dx = minmaxdraw(0, canvas.width), dy = minmaxdraw(0, canvas.height);
r += minmaxdraw(3, 7);
ang = Math.atan2(dx - xpos, dy - ypos);
angle[i] = ang;
ctx.beginPath();
ctx.arc(xpos, ypos, r, ang + 0 * Math.PI, ang + Math.PI * 1.5, true);
ctx.stroke();
}
}
function draw2() {
var xpos = canvas.width / 2;
var ypos = canvas.height / 2;
var r = 15;
for (var t = 0; t < 55; t++) {
r += minmaxdraw(3, 7);
ctx.beginPath();
ctx.arc(xpos, ypos, r, angle[t] + Math.PI / 2, angle[t] + Math.PI);
ctx.stroke();
}
}
draw1();
draw2();
<canvas id='canvas' class='canvas' width='640' height='480'></canvas>
The reason you got lines before is all arc does is add points.
arc is effectively this
// pseudo code
function arc(x, y, radius, start, end) {
const numSegments = 100; // no idea what number real arc uses here
for (let i = 0; i < numSegments; ++i) {
const angle = start + (end - start) * i / numSegments;
ctx.lineTo(Math.cos(angle) * radius, Math.sin(angle) * radius);
}
}
As you can see the code above just adds the points around the arc. So if you do a
ctx.moveTo(x, y);
ctx.arc(x, y, ...);
you're adding a point in the center of the arc and then more points to the edge. That's why you're getting a line from the centers to the edge of each arc.
If you wanted to leave the code the same as you had it and just stroke all the arcs at once then you'd need to change the moveTo to move to the edge of the arc instead of the center.
ctx.moveTo(x + Math.cos(start) * radius, y + Math.sin(start) * radius);
ctx.arc(x, y, radius, start, ...);

how i get a coordinate of each rectangle?

i have tried this,
public drawNumbers(ctx, x1, y1, length, count) {
let angle = 0;
for (let i = 0; i <= count; i++ ) {
angle += 2 * Math.PI / (count );
const x2 = x1 + length * Math.cos(angle),
y2 = y1 + length * Math.sin(angle);
ctx.beginPath();
ctx.fillRect(x2, y2, 10, 20);
ctx.stroke();
}
}
this.canvas.drawNumbers(ctx, this.midX, this.midY, 160, 60);
output:
expected result:
i want to calculate a four coordinate(rectangle) of rotated axis.
How do i detect click event on each rectangle?
Using setTransform
Salix alba answer is a solution though a few too many steps.
It can be done in a single transform using setTransform and applying the translate and rotations in one step. Also the second translation is where you draw the box relative to its origin. When using transforms always draw objects around the center of rotation.
ctx.strokeRect(-10,-10,20,20); // rotation is always around 0,0
const ctx = canvas.getContext("2d");
const centerX = 250;
const centerY = 250;
const radius = 200;
const boxWidth = 10;
const bobLength = 20;
// draw boxs around circle center at cx,cy and radius rad
// box width bw, and box height bh
// spacing optional is the distance between boxes
function drawCircleOfBoxes(cx,cy,rad,bw,bh,spacing = 5){
var steps = ((rad - bw /2) * Math.PI * 2) / (bw + spacing) | 0; // get number boxes that will fit circle
ctx.beginPath();
for(var i = 0; i < steps; i ++){
const ang = (i / steps) * Math.PI * 2;
var xAxisX = Math.cos(ang); // get the direction of he xAxis
var xAxisY = Math.sin(ang);
// set the transform to circle center x Axis out towards box
// y axis at 90 deg to x axis
ctx.setTransform(xAxisX, xAxisY, -xAxisY, xAxisX, cx, cy);
// draw box offset from the center so its center is distance radius
ctx.rect(rad - bh / 2, -bw / 2, bh, bw);
}
ctx.fill();
ctx.stroke();
ctx.setTransform(1,0,0,1,0,0); // reset transform
}
ctx.fillStyle = "#FCD";
ctx.strokeStyle = "#000";
drawCircleOfBoxes(centerX, centerY, radius, boxWidth, bobLength);
<canvas id="canvas" width="500" height="500"></canvas>
Manually apply the transform to a point
If you wish to transform the box in code you can use the transform applied in the above and apply it directly to a set of points. You can not apply it to the ctx.rect function that needs the API transform.
To transform a point px,py you need the the direction of the rotated x axis
const xAx = Math.cos(dirOfXAxis);
const xAy = Math.sin(dirOfXAxis);
You can then move the point px distance along the xAxis and then turn 90 deg and move py distance along the y axis
var x = px * xAx; // move px dist along x axis
var y = px * xAy;
x += py * -xAy; // move px dist along y axis
y += py * xAx;
Then just add the translation
x += translateX;
y += translateY;
Or done in one go
var x = px * xAx - py * xAy + translateX; // move px dist along x axis
var y = px * xAy + py * xAx + translateY;
The snippet shows it in action
const ctx = canvas.getContext("2d");
const centerX = 250;
const centerY = 250;
const radius = 200;
const boxWidth = 10;
const boxLength = 20;
// draw boxs around circle center at cx,cy and radius rad
// box width bw, and box height bh
// spacing optional is the distance between boxes
function drawCircleOfBoxes(cx,cy,rad,bw,bh,spacing = 5){
var points = [ // setout points of box with coord (0,0) as center
{x : bh / 2, y : -bw / 2},
{x : bh / 2 + bh, y : -bw / 2},
{x : bh / 2 + bh, y : -bw / 2 + bw},
{x : bh / 2, y : -bw / 2 + bw},
];
var steps = (((rad - bw /2) * Math.PI * 2) / (bw + spacing) )+ 4| 0; // get number boxes that will fit circle
ctx.beginPath();
for(var i = 0; i < steps; i ++){
const ang = (i / steps) * Math.PI * 2;
const xAx = Math.cos(ang); // get the direction of he xAxis
const xAy = Math.sin(ang);
var first = true
for(const p of points){ // for each point
// Apply the transform to the point after moving it
// to the circle (the p.x + rad)
const x = (p.x + rad) * xAx - p.y * xAy + cx;
const y = (p.x + rad) * xAy + p.y * xAx + cy;
if(first){
ctx.moveTo(x,y);
first = false;
}else{
ctx.lineTo(x,y);
}
}
ctx.closePath();
}
ctx.fill();
ctx.stroke();
}
ctx.fillStyle = "#CFD";
ctx.strokeStyle = "#000";
for(var i = boxLength + 5; i < radius; i += boxLength + 5){
drawCircleOfBoxes(centerX, centerY, i , boxWidth, boxLength);
}
<canvas id="canvas" width="500" height="500"></canvas>
To get rotated rectangles you need to use the transform() method of the graphics context.
Imagine a set of axis at the top left of the drawing area. Any drawing will be done relative to these axis which we can move with transform.
To translate by xshift, yshift
ctx.transform(1,0,0,1, xshift, yshift);
ctx.fillRect(0,0,100,100);
To rotate by angle ang in radians
ctx.transform(Math.cos(ang),Math.sin(ang),
-Math.sin(ang),Math.cos(ang), 0,0);
We can combine things with three transformations. The first moves the origin to the center of the circle. Then rotate the axes about this point,
then shift the axes to where you want the shape to appear. Finally, draw the shape.
for(deg = 0; deg < 360; deg+=20) {
ctx.setTransform(1,0,0,1,0,0); // reset transformation
ang = deg * Math.PI/180;
ctx.transform(1,0,0,1,100,100); // shift origin
ctx.transform(Math.cos(ang),Math.sin(ang),
-Math.sin(ang),Math.cos(ang), 0,0);
ctx.transform(1,0,0,1,50,0);
ctx.fillRect(0,0,30,10);
}
You can achieve the same this using the translate and rotate
for(deg = 0; deg < 360; deg+=20) {
ctx.setTransform(1,0,0,1,0,0); // reset transformation
ang = deg * Math.PI/180;
ctx.translate(100,100); // shift origin
ctx.rotate(ang);
ctx.translate(50,0);
ctx.fillRect(0,0,30,10);
}

html canvas check that object is in angle

I have a circle, and a object.
I want to draw a circle segment with specified spread, and next check that the object is in defined angle, if it is, angle color will be red, otherwise green. But my code does not work in some cases...
in this case it work:
in this too:
but here it isn't:
I know that my angle detection code part is not perfect, but I have no idea what I can do.
This is my code:
html:
<html>
<head></head>
<body>
<canvas id="c" width="800" height="480" style="background-color: #DDD"></canvas>
<script src="script.js"></script>
</body>
</html>
js:
window.addEventListener('mousemove', updateMousePos, false);
var canvas = document.getElementById("c");
var context = canvas.getContext("2d");
//mouse coordinates
var mx = 0, my = 0;
draw();
function draw()
{
context.clearRect(0, 0, canvas.width, canvas.height);
//object coordinates
var ox = 350, oy = 260;
context.beginPath();
context.arc(ox,oy,5,0,2*Math.PI);
context.fill();
//circle
var cx = 400, cy = 280;
var r = 100;
var segmentPoints = 20;
var circlePoints = 40;
var spread = Math.PI / 2;
var mouseAngle = Math.atan2(my - cy, mx - cx); //get angle between circle center and mouse position
context.beginPath();
context.strokeStyle = "blue";
context.moveTo(cx + r, cy);
for(var i=0; i<circlePoints; i++)
{
var a = 2 * Math.PI / (circlePoints - 1) * i;
var x = cx + Math.cos(a) * r;
var y = cy + Math.sin(a) * r;
context.lineTo(x, y);
}
context.lineTo(cx + r, cy);
context.stroke();
var objAngle = Math.atan2(oy - cy, ox - cx);
var lowerBorder = mouseAngle - spread / 2;
var biggerBorder = mouseAngle + spread / 2;
/////////////////////////////////////////////ANGLES DETECTION PART
if(objAngle >= lowerBorder && objAngle <= biggerBorder ||
objAngle <= biggerBorder && objAngle >= lowerBorder)
{
context.strokeStyle = "red";
}
else
context.strokeStyle = "green";
context.lineWidth = 3;
//angle center line
context.beginPath();
context.moveTo(cx, cy);
context.lineTo(cx + Math.cos(mouseAngle) * r * 2, cy + Math.sin(mouseAngle) * r * 2);
context.stroke();
//draw spread arc
context.beginPath();
context.moveTo(cx, cy);
for(var i=0; i<segmentPoints; i++)
{
var a = mouseAngle - spread / 2 + spread / (segmentPoints - 1) * i;
var x = cx + Math.cos(a) * r;
var y = cy + Math.sin(a) * r;
context.lineTo(x, y);
}
context.lineTo(cx, cy);
context.stroke();
//show degrees
context.font = "20px Arial";
context.fillText((lowerBorder * 180 / Math.PI).toFixed(2), Math.cos(lowerBorder) * r + cx, Math.sin(lowerBorder) * r + cy);
context.fillText((biggerBorder * 180 / Math.PI).toFixed(2), Math.cos(biggerBorder) * r + cx, Math.sin(biggerBorder) * r + cy);
context.fillText((mouseAngle * 180 / Math.PI).toFixed(2), Math.cos(mouseAngle) * r + cx, Math.sin(mouseAngle) * r + cy);
//update
setTimeout(function() { draw(); }, 10);
}
//getting mouse coordinates
function updateMousePos(evt)
{
var rect = document.getElementById("c").getBoundingClientRect();
mx = evt.clientX - rect.left;
my = evt.clientY - rect.top;
}
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
background-color: black;
}
canvas {
position: absolute;
margin: auto;
left: 0;
right: 0;
border: solid 1px white;
border-radius: 10px;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script type="application/javascript">
// Rotation here is being measured in Radians
// Given two 2D vectors A & B, the angle between them can be drawn from this formula
// A dot B = length(a) * length(b) * cos(angle)
// if the vectors are normalized (the length is 1) the formula becomes
// A dot B = cos(angle)
// angle = acos(a.x * b.x + a.y * b.y)
// So here you are concerned with the direction of the two vectors
// One will be the vector facing outward from the middle of your arc segment
// The other will be a directional vector from the point you want to do collision with to the center
// of the circle
var canvasWidth = 180;
var canvasHeight = 160;
var canvas = null;
var ctx = null;
var bounds = {top: 0.0, left: 0.0};
var circle = {
x: (canvasWidth * 0.5)|0,
y: (canvasHeight * 0.5)|0,
radius: 50.0,
rotation: 0.0, // In Radians
arcSize: 1.0
};
var point = {
x: 0.0,
y: 0.0
};
window.onmousemove = function(e) {
point.x = e.clientX - bounds.left;
point.y = e.clientY - bounds.top;
}
// runs after the page has loaded
window.onload = function() {
canvas = document.getElementById("canvas");
canvas.width = canvasWidth;
canvas.height = canvasHeight;
bounds = canvas.getBoundingClientRect();
ctx = canvas.getContext("2d");
loop();
}
function loop() {
// Update Circle Rotation
circle.rotation = circle.rotation + 0.025;
if (circle.rotation > 2*Math.PI) {
circle.rotation = 0.0;
}
// Vector A (Point Pos -> Circle Pos)
var aX = circle.x - point.x;
var aY = circle.y - point.y;
var aLength = Math.sqrt(aX * aX + aY * aY);
// Vector B (The direction the middle of the arc is facing away from the circle)
var bX = Math.sin(circle.rotation);
var bY =-Math.cos(circle.rotation); // -1 is facing upward, not +1
var bLength = 1.0;
// Normalize vector A
aX = aX / aLength;
aY = aY / aLength;
// Are we inside the arc segment?
var isInsideRadius = aLength < circle.radius;
var isInsideAngle = Math.abs(Math.acos(aX * bX + aY * bY)) < circle.arcSize * 0.5;
var isInsideArc = isInsideRadius && isInsideAngle;
// Clear the screen
ctx.fillStyle = "gray";
ctx.fillRect(0,0,canvasWidth,canvasHeight);
// Draw the arc
ctx.strokeStyle = isInsideArc ? "green" : "black";
ctx.beginPath();
ctx.moveTo(circle.x,circle.y);
ctx.arc(
circle.x,
circle.y,
circle.radius,
circle.rotation - circle.arcSize * 0.5 + Math.PI * 0.5,
circle.rotation + circle.arcSize * 0.5 + Math.PI * 0.5,
false
);
ctx.lineTo(circle.x,circle.y);
ctx.stroke();
// Draw the point
ctx.strokeStyle = "black";
ctx.fillStyle = "darkred";
ctx.beginPath();
ctx.arc(
point.x,
point.y,
5.0,
0.0,
2*Math.PI,
false
);
ctx.fill();
ctx.stroke();
// This is better to use then setTimeout()
// It automatically syncs the loop to 60 fps for you
requestAnimationFrame(loop);
}
</script>
</body>
</html>

Apply noise on circle according to mouseX and mouseY

I have created a blob with small points. I want my blob to show noise on its surface according to mouseX and mouseY. I want it to show high noise in the quadrant in which the mouse lies. I want it to be wavy. Below is my code.
var ctx = document.querySelector("canvas").getContext("2d");
var cx = 200;
var cy = 200;
var radius = 50;
var amp = 2;
var mouseX = 0;
var mouseY = 0;
document.querySelector("canvas").addEventListener("mousemove", function (e) {
mouseX = e.clientX;
mouseY = e.clientY;
});
function drawTheBlob() {
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, 400, 400);
ctx.beginPath();
ctx.strokeStyle = "#fff";
for (var a = 0; a < 360; a ++) {
var angle = a * Math.PI/180;
var x = cx + radius * Math.cos(angle) + Math.random() * amp;
var y = cy + radius * Math.sin(angle) + Math.random() * amp;
ctx.lineTo(x, y);
}
ctx.stroke();
ctx.closePath();
requestAnimationFrame(drawTheBlob);
}
drawTheBlob();
<canvas width="400" height="400"></canvas>
Adds a sin wave on the circle. Use ctx.arc to draw the flat part of the circle for speed as drawing many circles with lines will be slow. See code for comments on what is done.
var ctx = document.querySelector("canvas").getContext("2d");
ctx.lineWidth = 3;
ctx.lineJoin = "round";
var cx = 100;
var cy = 100;
var radius = 50;
var mouseX = 0;
var mouseY = 0;
const quadWidth = Math.PI / 2; // area of effect PI/2 is 90 degree
const steps = radius / quadWidth; // number steps around the circle matches 1 pixel per step,
const noiseAmpMax = 5; // in pixels
const noiseWaveMoveSpeed = 2; // speed of waves on circle in radians per second
const noiseWaveFreq = 16; // how many waves per 360 deg
document.querySelector("canvas").addEventListener("mousemove", function(e) {
mouseX = e.clientX;
mouseY = e.clientY;
});
function drawTheBlob(time) { // time is passed from the requestAnimationFrame call
var amp = 0; // amplitude of noise
var wavePos = ((time / 1000) * Math.PI) * noiseWaveMoveSpeed;
var mouseDir = Math.atan2(mouseY - cy, mouseX - cx);
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, 400, 400);
ctx.beginPath();
ctx.strokeStyle = "#fff";
ctx.fillStyle = "red";
// draw arc for parts that have no noise as it is a log quicker
ctx.arc(cx, cy, radius, mouseDir + quadWidth / 2, mouseDir + Math.PI * 2 - quadWidth / 2);
for (var a = 0; a < 1; a += 1 / steps) {
var angle = (mouseDir - quadWidth / 2) + a * quadWidth;
var angDist = Math.abs(angle - mouseDir); // find angular distance from mouse
// as a positive value, it does not mater
// what the sign is
if (angDist < quadWidth / 2) { // is angle distance within the range of effect
// normalise the distance (make it 0 to 1)
amp = 1 - angDist / (quadWidth / 2);
} else {
amp = 0; // no noise
}
// amp will be zero if away from mouse direction and 0 to 1 the closer to
// mouse angle it gets.
// add a sin wave to the radius and scale it by amp
var dist = radius + Math.sin(wavePos + noiseWaveFreq * angle) * noiseAmpMax * amp;
var x = cx + dist * Math.cos(angle);
var y = cy + dist * Math.sin(angle);
ctx.lineTo(x, y);
}
ctx.closePath(); // use close path to close the gap (only needed if you need to draw a line from the end to the start. It is not needed to match beginPath
ctx.fill();
ctx.stroke();
requestAnimationFrame(drawTheBlob);
}
requestAnimationFrame(drawTheBlob); // start this way so that you get the time argument
<canvas width="200" height="200"></canvas>
How it works.
Mouse direction
First we need the direction from the circle to the mouse. To do that we use the function Math.atan2 It takes the vector from the circle to the mouse and returns the direction in radians. The function is a little weird as it takes y first, then x.
var mouseDir = Math.atan2(mouseY - cy, mouseX - cx);
Draw arc to save CPU time
Now that we have the direction to the mouse we can draw the parts of the circle that has no noise using arc .
ctx.arc(cx, cy, radius, mouseDir + quadWidth / 2, mouseDir + Math.PI * 2 - quadWidth / 2);
The variable quadWidth is angular size of the noise bit so from the mouseDir we add half that angular width and draw the arc around to mouseDir plus 360deg take half the quadWidth.
Quick word on Radians
Almost all programming languages use radians to define angles, 360deg is equal to 2 * PI or 2 * 3.1415, which can be hard to get your head around, but there is good reason to use radians. For now just remember that a full circle in radians is 2 * Math.PI = 360deg, Math.PI = 180deg, Math.PI / 2 = 90deg, Math.PI / 4 = 45Deg and Math.PI / 180 = 1deg. You dont have to remember the digits just Math.PI is half a circle.
quadWidth from above is a constant defined as const quadWidth = Math.PI / 2; which is 90deg.
The for loop
The for loop only draws the (Math.PI / 2) 90deg section around the mouseDir, from 45 deg left to 45 right. or whatever you set quadWidth to.
for (var a = 0; a < 1; a += 1 / steps) {
I loop from 0 to 1 the number of steps that give a reasonably smooth curve. We can find what part of the noisy arc we are drawing by multiplying the value a *
quadWidth and adding that to the mouseDir - quadWidth / 2. This means that we start at mouseDir - 45deg and move clock wise to mouseDir + 45deg
var angle = (mouseDir - quadWidth / 2) + a * quadWidth;
Next i find how far that angle is from the mouseDir (could optimize it here a bit here but this way is a little more flexible, if you want to draw more noise on the other part of the arc)
var angDist = Math.abs(angle - mouseDir);
If that number is less than quadWidth / 2 convert the value to the range 0 to 1 where 0 is at the angle furthest from the mouse direction and 1 closest.
if (angDist < quadWidth / 2) {
amp = 1 - angDist / (quadWidth / 2);
} else {
amp = 0;
}
The sin wave
Now we calculate the radius of the circle at the current angle and add a sin wave to it. First the radius then the sin wave multiplied by the amp calculated in the last step. Where amp is zero none of the sin wave is added, where amp is 1 (in the direction of the mouse) the full sin wave is added.
var dist = radius + Math.sin(wavePos + noiseWaveFreq * angle) * noiseAmpMax * amp
The values wavePos, noiseWaveFreq, and noiseAmpMax control the animation of the sin wave. Play around with these values to get a feel of what they do, wavePos is calculated based on the time at the start of the function.
With dist we can calculate the x,y position for the next line of the circle
var x = cx + dist * Math.cos(angle);
var y = cy + dist * Math.sin(angle);
ctx.lineTo(x, y);
Experiment
I added some constants
const quadWidth = Math.PI / 2; // area of effect PI/2 is 90 degree
const steps = radius / quadWidth; // number steps around the circle matches 1 pixel per step,
const noiseAmpMax = 5; // in pixels
const noiseWaveMoveSpeed = 2; // speed of waves on circle in radians per second
const noiseWaveFreq = 16; // how many waves per 360 deg
To get a understanding what they do experiment and change the numbers to see what happens.

Drawing a spiral on an HTML canvas using JavaScript

I have searched and haven't found anything really on how to draw spirals in canvas using JavaScript.
I thought it might be possible to do it with the bezier curve and if that didn't work use lineTo(), but that seemed a lot harder.
Also, to do that I'm guessing I would have to use trigonometry and graphing with polar coordinates and its been a while since I did that. If that is the case could you point me in the right direction on the math.
The Archimedean spiral is expressed as r=a+b(angle). Convert that into x, y coordinate, it will be expressed as x=(a+b*angle)*cos(angle), y=(a+b*angle)*sin(angle). Then you can put angle in a for loop and do something like this:
for (i=0; i< 720; i++) {
angle = 0.1 * i;
x=(1+angle)*Math.cos(angle);
y=(1+angle)*Math.sin(angle);
context.lineTo(x, y);
}
Note the above assumes a = 1 and b = 1.
Here is a jsfiddle link: http://jsfiddle.net/jingshaochen/xJc7M/
This is a slightly-changed, javascript-ified version of a Java spiral I once borrowed from here
It uses lineTo() and its not all that hard.
<!DOCTYPE HTML>
<html><body>
<canvas id="myCanvas" width="300" height="300" style="border:1px solid #c3c3c3;"></canvas>
<script type="text/javascript">
var c=document.getElementById("myCanvas");
var cxt=c.getContext("2d");
var centerX = 150;
var centerY = 150;
cxt.moveTo(centerX, centerY);
var STEPS_PER_ROTATION = 60;
var increment = 2*Math.PI/STEPS_PER_ROTATION;
var theta = increment;
while( theta < 40*Math.PI) {
var newX = centerX + theta * Math.cos(theta);
var newY = centerY + theta * Math.sin(theta);
cxt.lineTo(newX, newY);
theta = theta + increment;
}
cxt.stroke();
</script></body></html>
Here's a function I wrote for drawing Archimedean spirals:
CanvasRenderingContext2D.prototype.drawArchimedeanSpiral =
CanvasRenderingContext2D.prototype.drawArchimedeanSpiral ||
function(centerX, centerY, stepCount, loopCount,
innerDistance, loopSpacing, rotation)
{
this.beginPath();
var stepSize = 2 * Math.PI / stepCount,
endAngle = 2 * Math.PI * loopCount,
finished = false;
for (var angle = 0; !finished; angle += stepSize) {
// Ensure that the spiral finishes at the correct place,
// avoiding any drift introduced by cumulative errors from
// repeatedly adding floating point numbers.
if (angle > endAngle) {
angle = endAngle;
finished = true;
}
var scalar = innerDistance + loopSpacing * angle,
rotatedAngle = angle + rotation,
x = centerX + scalar * Math.cos(rotatedAngle),
y = centerY + scalar * Math.sin(rotatedAngle);
this.lineTo(x, y);
}
this.stroke();
}
there is a fine free tool that will help if you have illustrator
ai2canvas
it will create all the curves to javascript in html canvas tag for you!
(if you are looking for archmedes spiral than you will first have to get it from coreldraw and copy that to illustrator, because the default spiral tool enlarges the angle with each point)
this is example of drawing spiral using function below:
spiral(ctx, {
start: {//starting point of spiral
x: 200,
y: 200
},
angle: 30 * (Math.PI / 180), //angle from starting point
direction: false,
radius: 100, //radius from starting point in direction of angle
number: 3 // number of circles
});
spiral drawing code:
spiral = function(ctx,obj) {
var center, eAngle, increment, newX, newY, progress, sAngle, tempTheta, theta;
sAngle = Math.PI + obj.angle;
eAngle = sAngle + Math.PI * 2 * obj.number;
center = {
x: obj.start.x + Math.cos(obj.angle) * obj.radius,
y: obj.start.y + Math.sin(obj.angle) * obj.radius
};
increment = 2 * Math.PI / 60/*steps per rotation*/;
theta = sAngle;
ctx.beginPath();
ctx.moveTo(center.x, center.y);
while (theta <= eAngle + increment) {
progress = (theta - sAngle) / (eAngle - sAngle);
tempTheta = obj.direction ? theta : -1 * (theta - 2 * obj.angle);
newX = obj.radius * Math.cos(tempTheta) * progress;
newY = obj.radius * Math.sin(tempTheta) * progress;
theta += increment;
ctx.lineTo(center.x + newX, center.y + newY);
}
ctx.stroke();
};
The following code approximates a spiral as a collection of quarters of a circle each with a slightly larger radius. It might look worse than an Archimedes spiral for small turning numbers but it should run faster.
function drawSpiral(ctx, centerx, centery, innerRadius, outerRadius, turns=2, startAngle=0){
ctx.save();
ctx.translate(centerx, centery);
ctx.rotate(startAngle);
let r = innerRadius;
let turns_ = Math.floor(turns*4)/4;
let dr = (outerRadius - innerRadius)/turns_/4;
let cx = 0, cy = 0;
let directionx = 0, directiony = -1;
ctx.beginPath();
let angle=0;
for(; angle < turns_*2*Math.PI; angle += Math.PI/2){
//draw a quarter arc around the center point (x, cy)
ctx.arc( cx, cy, r, angle, angle + Math.PI/2);
//move the center point and increase the radius so we can draw a bigger arc
cx += directionx*dr;
cy += directiony*dr;
r+= dr;
//rotate direction vector by 90 degrees
[directionx, directiony] = [ - directiony, directionx ];
}
//draw the remainder of the last quarter turn
ctx.arc( cx, cy, r, angle, angle + 2*Math.PI*( turns - turns_ ))
ctx.stroke();
ctx.restore();
}
Result:

Categories

Resources