So a season specific question. I have made a basic animation to simulate some snow. The problem is that the snow only falls once and the screen stays black after that. I have followed a tutorial which put everything inside the window.onload(). THis seems not as good style to me so here is what i came up with:
let sky, ctx;
let W, H;
const maxSnow = 250;
const snow = [];
function init(){
sky = document.getElementById("sky");
ctx = sky.getContext("2d");
sky.width = W = window.innerWidth;
sky.height = H = window.innerHeight;
for(let i = 0; i < maxSnow; i++)
{
snow.push({
x: Math.random()*W, //x-coordinate
y: -50, //y-coordinate
radius: Math.random()*4+1, //radius
density: Math.random()*maxSnow //density
})
}
}
function draw()
{
ctx.clearRect(0, 0, W, H);
ctx.fillStyle = "rgba(255, 255, 255, 0.8)";
ctx.beginPath();
for(let i = 0; i < maxSnow; i++)
{
var flake = snow[i];
ctx.moveTo(flake.x, flake.y);
ctx.arc(flake.x, flake.y, flake.radius, 0, Math.PI*2, true);
}
ctx.fill();
update();
}
//Function to move the snowflakes
//angle will be an ongoing incremental flag. Sin and Cos functions will be applied to it to create vertical and horizontal movements of the flakes
var angle = 0;
function update()
{
angle += 0.01;
for(var i = 0; i < maxSnow; i++)
{
var p = snow[i];
//Updating X and Y coordinates
//We will add 1 to the cos function to prevent negative values which will lead flakes to move upwards
//Every particle has its own density which can be used to make the downward movement different for each flake
//Lets make it more random by adding in the radius
p.y += Math.cos(angle+p.density) + 1 + p.radius/2;
p.x += Math.sin(angle) * 2;
//Sending flakes back from the top when it exits
//Lets make it a bit more organic and let flakes enter from the left and right also.
if(p.x > W+5 || p.x < -5 || p.y > H)
{
if(i%3 > 0) //66.67% of the flakes
{
snow[i] = {x: Math.random()*W, y: -10, r: p.radius, d: p.density};
}
else
{
//If the flake is exitting from the right
if(Math.sin(angle) > 0)
{
//Enter from the left
snow[i] = {x: -5, y: Math.random()*H, r: p.radius, d: p.density};
}
else
{
//Enter from the right
snow[i] = {x: W+5, y: Math.random()*H, r: p.radius, d: p.density};
}
}
}
}
}
window.onload = function(){
init();
//animation loop
setInterval(draw, 33);
}
window.addEventListener('resize', function(){
sky.width = W = window.innerWidth;
sky.height = H = window.innerHeight;
}, false);
* {
margin: 0;
padding: 0;
}
body {
overflow: hidden;
background-color: rgba(0, 0, 0, 1);
}
<html>
<head>
<link rel="stylesheet" href="style.css">
</head>
<body>
<canvas id="sky"></canvas>
<script src="snow.js"></script>
</body>
</html>
Does anybody see what the problem is and why the particles are only falling once?
Thank you.
The is because when the snow flakes exit the screen you reset them to a wrong object.
You original object (and all methods) expect x,y,radius and density
But when you update the object you create x,y, r and d.
If you rename r and d to the correct names it works.
let sky, ctx;
let W, H;
const maxSnow = 250;
const snow = [];
function init(){
sky = document.getElementById("sky");
ctx = sky.getContext("2d");
sky.width = W = window.innerWidth;
sky.height = H = window.innerHeight;
for(let i = 0; i < maxSnow; i++)
{
snow.push({
x: Math.random()*W, //x-coordinate
y: -50, //y-coordinate
radius: Math.random()*4+1, //radius
density: Math.random()*maxSnow //density
})
}
}
function draw()
{
ctx.clearRect(0, 0, W, H);
ctx.fillStyle = "rgba(255, 255, 255, 0.8)";
ctx.beginPath();
for(let i = 0; i < maxSnow; i++)
{
var flake = snow[i];
ctx.moveTo(flake.x, flake.y);
ctx.arc(flake.x, flake.y, flake.radius, 0, Math.PI*2, true);
}
ctx.fill();
update();
}
//Function to move the snowflakes
//angle will be an ongoing incremental flag. Sin and Cos functions will be applied to it to create vertical and horizontal movements of the flakes
var angle = 0;
function update()
{
angle += 0.01;
for(var i = 0; i < maxSnow; i++)
{
var p = snow[i];
//Updating X and Y coordinates
//We will add 1 to the cos function to prevent negative values which will lead flakes to move upwards
//Every particle has its own density which can be used to make the downward movement different for each flake
//Lets make it more random by adding in the radius
p.y += Math.cos(angle+p.density) + 1 + p.radius/2;
p.x += Math.sin(angle) * 2;
//Sending flakes back from the top when it exits
//Lets make it a bit more organic and let flakes enter from the left and right also.
if(p.x > W+5 || p.x < -5 || p.y > H)
{
if(i%3 > 0) //66.67% of the flakes
{
snow[i] = {x: Math.random()*W, y: -10, radius: p.radius, density: p.density};
}
else
{
//If the flake is exitting from the right
if(Math.sin(angle) > 0)
{
//Enter from the left
snow[i] = {x: -5, y: Math.random()*H, radius: p.radius, density: p.density};
}
else
{
//Enter from the right
snow[i] = {x: W+5, y: Math.random()*H, radius: p.radius, density: p.density};
}
}
}
}
}
window.onload = function(){
init();
//animation loop
setInterval(draw, 33);
}
window.addEventListener('resize', function(){
sky.width = W = window.innerWidth;
sky.height = H = window.innerHeight;
}, false);
* {
margin: 0;
padding: 0;
}
body {
overflow: hidden;
background-color: rgba(0, 0, 0, 1);
}
<html>
<head>
<link rel="stylesheet" href="style.css">
</head>
<body>
<canvas id="sky"></canvas>
<script src="snow.js"></script>
</body>
</html>
Related
When the program runs the mouse is clicked creating a projectile from the center of the screen moving with every frame in the direction it was fired (mouse position on click).
When N+1 projectiles have fired all projectiles on-screen move to the new clicked location instead of continuing their path.
I am can not figure out why the current projectiles change direction when the New projectile's velocity should have no effect on prior projectiles.
index.html
<canvas></canvas>
<script src="./guns.js"></script>
<script src="./indexh.js"></script>
<script src="./runh.js"></script>
runh.html
const projectilesArray = [];
let frameCount = 0;
function animate() {
animationID = requestAnimationFrame(animate);
c.fillStyle = "rgba(0, 0, 0, 1)";
c.fillRect(0, 0, canvas.width, canvas.height);
projectilesArray.forEach((Projectile, pIndex) => {
Projectile.update();
console.log(Projectile)
if (
Projectile.x + Projectile.radius < 0 ||
Projectile.x - Projectile.radius > canvas.width ||
Projectile.y + Projectile.radius < 0 ||
Projectile.y - Projectile.radius > canvas.height
) {
setTimeout(() => {
projectilesArray.splice(pIndex, 1);
}, 0);
}
});
frameCount++;
if (frameCount > 150) {
}
}
var fire = 1;
let fireRate = 1;
const mouse = {
x: 0,
y: 0,
click: true,
};
canvas.addEventListener('mousedown', (event) => {
if (fire % fireRate == 0) {
if (mouse.click == true) {
mouse.x = event.x;
mouse.y = event.y;
const angle = Math.atan2(mouse.y - (canvas.height / 2), mouse.x - (canvas.width / 2));
const fireY = Math.sin(angle);
const fireX = Math.cos(angle);
//score -= 0;
//scoreL.innerHTML = score;
var weapon = new Projectile(cannon);
weapon.velocity.x = fireX * 9;
weapon.velocity.y = fireY * 9;
projectilesArray.push(weapon);
//var gun = object.constructor()
}
}
});
animate();
indexh.js
const canvas = document.querySelector("canvas");
const c = canvas.getContext("2d");
canvas.width = innerWidth;
canvas.height = innerHeight;
class Projectile {
constructor(config) {
this.color = config.color || "rgb(60, 179, 113)";
this.radius = config.radius || 1;
this.speed = config.speed || 5;
this.rounds = config.rounds || 2;
this.x = config.x || canvas.width / 2;
this.y = config.y || canvas.height /2;
this.velocity = config.velocity;
}
draw() {
c.beginPath();
c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
c.fillStyle = this.color;
c.fill();
}
update() {
this.draw();
this.x = this.x + this.velocity.x * this.speed;
this.y = this.y + this.velocity.y * this.speed;
}
}
gums.js
let pistol = {
color : "rgb(255, 0, 0)",
radius : 10,
speed : 1,
rounds : 1,
velocity : {
x: 1,
y: 1
}
}
let cannon = {
color : "rgb(0, 0, 255)",
radius : 30,
speed : .5,
rounds : 1,
velocity : {
x: 1,
y: 1
}
}
Thanks
The issue is that you have this cannon object used as a configuration object:
let cannon = {
color : "rgb(0, 0, 255)",
radius : 30,
speed : .5,
rounds : 1,
velocity : {
x: 1,
y: 1
}
}
And in your Projectile constructor you assign this.velocity = config.velocity; You are assigning this.velocity to be the same object instance for all Projectiles. To fix it, try copying the object, like:
this.velocity = {...config.velocity};
That will make a copy of the object rather than sharing the same object instance.
Also note that you have a bug that you didn't ask about in this line:
projectilesArray.splice(pIndex, 1);
If two timeouts are queued in the same loop, the array will shift when the first timeout fires, and the second timeout will be operating on the wrong index.
I need a circle moving around the edge of the canvas. Moving right then down is working properly, but when it needs to go left it's jumping to the bottom-right and starts moving right again and again. I don't exactly know how to fix that.
var can = document.getElementById('C4');
var ctx = can.getContext('2d');
var x = 5, y = 20;
ctx.fillStyle = "black";
ctx.fillRect(700, 100, 100, 100);
function draw() {
ctx.beginPath();
ctx.arc(x, y, 20, 0, 2 * Math.PI);
ctx.fillStyle = 'rgba(250,0,0,0.4)';
ctx.fill();
do {//moving right
x += 2;
} while (x <! 281);
if (x >= 280){//down
do {
x = 280;
y += 2;
} while (y <! 130);
}
if(y >= 130 && x >= 280){//left
do {
x = x - 2;
y = 130;
} while (x >! 20);
}
if (x <= 20) {//up
do {
x = 20;
y = y-2;
} while (y <! 20);
}
ctx.fillStyle = "rgba(34,45,23,0.4)";
ctx.fillRect(0, 0, can.width, can.height);
requestAnimationFrame(draw);
}
draw();
canvas { border: 1px solid black}
<canvas id="C4"></canvas>
Since you're using a recursion you don't need the do while, the loops are just making the circle jump from one edge to another. You can achive your goal with if conditions, like this:
var can = document.getElementById('C4');
var ctx = can.getContext('2d');
var x = 5, y = 20;
ctx.fillStyle = "black";
ctx.fillRect(700, 100, 100, 100);
function draw() {
ctx.beginPath();
ctx.arc(x, y, 20, 0, 2 * Math.PI);
ctx.fillStyle = 'rgba(250,0,0,0.4)';
ctx.fill();
if (x < 280 && y == 20) {
x += 2;
}
if (x >= 280 && y < 130){//down
x = 280;
y += 2;
}
if(y >= 130 && x > 20){//left
x = x - 2;
y = 130;
}
if (x == 20 && y > 20) {//up
x = 20;
y = y-2;
}
ctx.fillStyle = "rgba(34,45,23,0.4)";
ctx.fillRect(0, 0, can.width, can.height);
requestAnimationFrame(draw);
}
draw();
canvas { border: 1px solid black}
<canvas id="C4"></canvas>
Isolate the ball as an object.
Use the balls direction to check for change in direction.
Clear the canvas first line inside the draw function rather than create a path at the end of the draw function.
Example.
var ctx = canvas.getContext('2d');
const radius = 20;
const speed = 2;
const ball = {
style: "rgba(250,0,0,0.4)",
radius: radius,
pos: {x: radius, y: radius},
vel: {x: speed, y: 0},
step() {
const w = ctx.canvas.width, h = ctx.canvas.height, r = ball.radius;
var x = ball.pos.x, y = ball.pos.y
if(ball.vel.x === speed && x >= w - r ) {
ball.vel.x = 0;
ball.vel.y = speed;
ball.pos.x = w - r;
} else if(ball.vel.y === speed && y >= h - r) {
ball.vel.x = -speed;
ball.vel.y = 0;
ball.pos.y = h - r;
} else if(ball.vel.x === -speed && x <= r) {
ball.vel.x = 0;
ball.vel.y = -speed;
ball.pos.x = r;
} else if(ball.vel.y === -speed && y <= r) {
ball.vel.x = speed;
ball.vel.y = 0;
ball.pos.y = r;
}
ball.pos.x += ball.vel.x;
ball.pos.y += ball.vel.y;
},
draw() {
ctx.fillStyle = ball.style;
ctx.beginPath();
ctx.arc(ball.pos.x, ball.pos.y, ball.radius, 0, 2 * Math.PI);
ctx.fill();
},
}
function draw() {
ctx.fillStyle = "rgba(34,45,23,0.4)";
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ball.step();
ball.draw();
requestAnimationFrame(draw);
}
draw();
canvas { border: 1px solid black}
<canvas id="canvas"></canvas>
I can reshape polygon with mouse by using code in snippet. After drawing polygon, users can change shape by moving points. But I want to modify shape without changing line lengths. The points will change as possible but length of the lines will remain the same.
How can I do this?
var canvas, ctx;
var canvasIsMouseDown = false;
var radius = 6;
var pointIndex = -1;
var points = [
{ x: 10, y: 10 },
{ x: 100, y: 50 },
{ x: 150, y: 100 },
{ x: 60, y: 110 },
{ x: 30, y: 160 }
];
function start() {
canvas = document.getElementById("cnPolygon");
ctx = canvas.getContext("2d");
canvas.addEventListener("mousemove", canvasMouseMove);
canvas.addEventListener("mousedown", canvasMouseDown);
canvas.addEventListener("mouseup", canvasMouseUp);
draw();
}
function canvasMouseMove(ev) {
if (!canvasIsMouseDown || pointIndex === -1) return;
points[pointIndex].x = ev.pageX - this.offsetLeft;
points[pointIndex].y = ev.pageY - this.offsetTop;
draw();
}
function canvasMouseDown(ev) {
canvasIsMouseDown = true;
var x = ev.pageX - this.offsetLeft;
var y = ev.pageY - this.offsetTop;
pointIndex = -1;
var dist;
for (var i = 0; i < points.length; i++) {
dist = Math.sqrt(Math.pow((x - points[i].x), 2) + Math.pow((y - points[i].y), 2));
if (dist <= radius) {
pointIndex = i;
break;
}
}
}
function canvasMouseUp(ev) {
canvasIsMouseDown = false;
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.moveTo(points[0].x, points[0].y);
for (var i = 0; i < points.length; i++) {
ctx.lineTo(points[i].x, points[i].y);
}
ctx.closePath();
ctx.stroke();
for (var i = 0; i < points.length; i++) {
ctx.beginPath();
ctx.arc(points[i].x, points[i].y, radius, 0, Math.PI * 2);
ctx.stroke();
}
}
document.addEventListener("DOMContentLoaded", start);
<canvas id="cnPolygon" width="200" height="200" style="border:solid 1px silver"></canvas>
Take a look at this
var canvas, ctx;
var canvasIsMouseDown = false;
var radius = 3;
var pointIndex = -1;
var points = [
{ x: 10, y: 10 },
{ x: 100, y: 50 },
{ x: 150, y: 100 },
{ x: 60, y: 110 },
{ x: 30, y: 160 }
];
// PHYSICS START ----------------
var stiffness = 0.25 // defines how elastic the contrainst should be
var oscillations = 10 // defines how many iterations should be made, more iterations mean higher precision
function getAngle(x1,y1,x2,y2){
return Math.atan2(y2-y1,x2-x1) + Math.PI/2
}
function getConstraintPos(tx,ty,ox,oy,dist){
var rot = getAngle(tx,ty,ox,oy)
var x = tx+Math.sin(rot)*dist
var y = ty-Math.cos(rot)*dist
return [x,y]
}
function applyContraintForce(point,pos){
point.x += (pos[0] - point.x)*stiffness
point.y += (pos[1] - point.y)*stiffness
}
function defineDistances(){
for (var i = 0; i < points.length; i++) {
var next_point = points[(i+1)%points.length]
points[i].distance = Math.sqrt(Math.pow((next_point.x - points[i].x), 2) + Math.pow((next_point.y - points[i].y), 2))
}
}
function updateContraints(){
// forward pass
for (var i=0;i<points.length;i++)
{
if(i==pointIndex) continue
var j = (+i+1)%points.length
var pos = getConstraintPos(points[j].x,points[j].y,points[i].x,points[i].y,points[i].distance)
applyContraintForce(points[i],pos)
}
//backward pass
for (var i=points.length-1;i>=0;i--)
{
if(i==pointIndex) continue
var j = (i-1)
j = j<0 ? points.length+j : j
var pos = getConstraintPos(points[j].x,points[j].y,points[i].x,points[i].y,points[j].distance)
applyContraintForce(points[i],pos)
}
}
// PHYSICS END ----------------
function start() {
canvas = document.getElementById("cnPolygon");
ctx = canvas.getContext("2d");
canvas.addEventListener("mousemove", canvasMouseMove);
canvas.addEventListener("mousedown", canvasMouseDown);
canvas.addEventListener("mouseup", canvasMouseUp);
defineDistances()
draw();
}
function canvasMouseMove(ev) {
if (!canvasIsMouseDown || pointIndex === -1) return;
points[pointIndex].x = ev.pageX - this.offsetLeft;
points[pointIndex].y = ev.pageY - this.offsetTop;
for(var i=0;i<oscillations;i++){
updateContraints()
}
draw();
}
function canvasMouseDown(ev) {
canvasIsMouseDown = true;
var x = ev.pageX - this.offsetLeft;
var y = ev.pageY - this.offsetTop;
pointIndex = -1;
var dist;
for (var i = 0; i < points.length; i++) {
dist = Math.abs(Math.sqrt(Math.pow((x - points[i].x), 2) + Math.pow((y - points[i].y), 2)));
if (dist <= radius) {
pointIndex = i;
break;
}
}
}
function canvasMouseUp(ev) {
canvasIsMouseDown = false;
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.moveTo(points[0].x, points[0].y);
for (var i = 0; i < points.length; i++) {
ctx.lineTo(points[i].x, points[i].y);
}
ctx.closePath();
ctx.stroke();
for (var i = 0; i < points.length; i++) {
ctx.beginPath();
ctx.arc(points[i].x, points[i].y, radius * 2, 0, Math.PI * 2);
ctx.stroke();
}
}
document.addEventListener("DOMContentLoaded", start);
<canvas id="cnPolygon" width="500" height="300" style="border:solid 1px silver"></canvas>
What this does is calculate the angle between two given points and applies a force based on that angle and the distance delta. The force applied is multiplied by the stiffness.
This has to be done forwards (point A -> point B) and backwards (point A <- point B) in order to account for the differences between the last to first point in the chain.
NOTE this is not 100% accurate. The accuracy can be increased by the iterations count, but as #bhspencer already pointed out, there are cases where this is impossible, simply because of geometry.
I'm building a dynamic radar chart, I got the code reviewed and followed the recommendation from fellow SO member.
This is how far I've come, but seem to have hit a roadblock:
var canv = document.getElementById('canvas');
var canv1 = document.getElementById('canvas1');
var point_xy = document.getElementById('point_xy');
var tipCanvas = document.getElementById("tip");
var tipCtx = tipCanvas.getContext("2d");
var point_xy_cords = [
[]
];
var pentagon_one = 24;
var pentagon_two = 18;
var pentagon_three = 12;
var pentagon_four = 6;
var pentagon_five = 0;
var circles = [];
var contx = canv.getContext('2d');
var contx1 = canv1.getContext('2d');
var offsetX = canv1.offsetLeft;
var offsetY = canv1.offsetTop;
contx.clearRect(0, 0, canv.width, canv.height);
function drawShape(ctx, x, y, points, radius1, radius2, alpha0) {
//points: number of points (or number of sides for polygons)
//radius1: "outer" radius of the star
//radius2: "inner" radius of the star (if equal to radius1, a polygon is drawn)
//angle0: initial angle (clockwise), by default, stars and polygons are 'pointing' up
var radius_size = radius1;
var i, angle, radius;
if (radius2 !== radius1) {
points = 2 * points;
}
for (var i = 0; i <= 5; i++) {
var temp = [];
contx1.beginPath();
for (var j = 0; j <= 4; j++) {
angle = j * 2 * Math.PI / points - Math.PI / 2 + alpha0;
radius = j % 2 === 0 ? radius_size : radius_size;
temp[j] = [(x + radius_size * Math.cos(angle)), (y + radius_size * Math.sin(angle))];
ctx.lineTo(temp[j][0], temp[j][1]);
}
ctx.closePath();
style(ctx);
radius_size = radius_size - 20;
point_xy_cords.push(temp);
}
point_xy.textContent = "[1] = " + point_xy_cords[1] + " y = " + point_xy_cords[1][1];
}
function style(ctx, fill) {
ctx.strokeStyle = "rgba(0, 109, 0, 1)";
ctx.lineWidth = 2;
if (fill) {
ctx.fillStyle = "rgba(74, 157, 33, 0.6)";
ctx.fill();
} else {
ctx.stroke()
}
//contx.fill();
}
var radius = 2;
var Circle = function(x, y, radius) {
this.left = x - radius;
this.top = y - radius;
this.right = x + radius;
this.bottom = y + radius;
this.point_clicked = [];
this.clicked = function(){
points[1][0] = x; //hardcoded part
points[1][1] = y; //hardcoded part
contx1.clearRect(0, 0, canv.width, canv.height);
drawBackgroundPentagons(contx1);
drawMainPentagon(contx1, points);
drawPoints();
}
this.draw = function(ctx) {
//Draw all points
ctx.beginPath();
ctx.arc(x, y, 2, 0, 2 * Math.PI, false);
ctx.lineWidth = 1;
ctx.strokeStyle = "rgba(74, 157, 33, 1)";
ctx.fill();
ctx.stroke();
}
this.containsPoint = function(x,y){
return (x < this.right && x > this.left && y > this.top && y < this.bottom);
}
};
//Draw background
function drawBackgroundPentagons(ctx) {
drawShape(ctx, 120, 120, 5, 100, 100, 0);
}
drawBackgroundPentagons(contx1);
//Draw all the points
function drawPoints(){
for (var x = 1; x <= 5; x++){
for (var y = 0; y <= 4; y++){
var circle = new Circle(point_xy_cords[x][y][0], point_xy_cords[x][y][1], 8);
circle.draw(contx1);
circles.push(circle);
}
}
}
drawPoints();
function drawMainPentagon(ctx, points) {
ctx.beginPath();
ctx.moveTo(points[0][0], points[0][1]);
for (var x = 1; x <= 4; x++) {
ctx.lineTo(points[x][0], points[x][1]);
}
style(ctx, "fill");
ctx.closePath();
}
points = point_xy_cords[1];
drawMainPentagon(contx1, points);
function handleMouseDown(e, message) {
point_xy.textContent = (message);
}
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
canv1.onmousedown = function(e) {
var pos = getMousePos(canv1, e);
var clickedX = pos.x;
var clickedY = pos.y;
var tooltipText = "nothing";
for (var i = 0; i < circles.length; i++) {
var circle = circles[i];
if (circle.containsPoint(clickedX, clickedY)) {
circle.clicked();
return;
}
}
tooltip("points[0]", clickedX, clickedY);
};
function tooltip(text, clickedX, clickedY) {
tipCtx.fillStyle = "black";
tipCtx.fillRect(0, 0, canvas.width, canvas.height);
tipCtx.fillStyle = "white";
tipCtx.fillText(text, 5, 10);
tipCanvas.style.left = (clickedX + 15) + "px";
tipCanvas.style.top = (clickedY - 26) + "px";
}
canv1.onmouseover = function(e) {
return null;
}
canv1.onmouseout = function(e) {
return null;
}
canv1.onmousemove = function(e) {
return null;
}
#tip {
left: -200px;
top: 100px;
position: absolute;
float: left;
maxWidth: 200px;
backgroundColor: rgba(0, 0, 0, 0.8);
border: rgba(45, 65, 45, 1);
borderRadius: 5px;
color: #f9f9f9;
fontSize: 14px;
padding: 5px;
textAlign: left;
}
<div id="canvasesdiv" style="position:relative; width:400px; height:300px">
<canvas id="tip" width=100 height=100 style="z-index: 3;"></canvas>
<canvas id="canvas" style="z-index: 1;
position:absolute;
left:10px;
top:10px;
" height="300px" width="400">
This text is displayed if your browser does not support HTML5 Canvas.
</canvas>
<canvas id="canvas1" style="z-index: 2;
position:absolute;
left:10px;
top:10px;
" height="300px" width="400">
This text is displayed if your browser does not support HTML5 Canvas.
</canvas>
</div>
<div id='point_xy'></div>
If you click a point, it is suppose to move the point of the highlighted pentagon to the clicked point. It works, except I can't figure out what conditions to add in order to move the correct corner of the highlighted pentagon. In the above code I have hardcoded it, so that no matter which point you click, it will move point at index 0.
Any direction would be appreciated.
So what you want to do is let each circle know what spoke or radii it belongs to. Something like this:
var Circle = function(x, y, radius, spoke, value) {
this.x = x;
this.y = y;
this.radius = radius;
this.spoke = spoke;
this.value = value;
Now create them something like:
function drawPoints() {
for (var value = 1; value <= 5; value++){
for (var spoke = 0; spoke <= 4; spoke++){
var circle = new Circle(point_xy_cords[value][spoke][0], point_xy_cords[value][spoke][1], 8, spoke, value);
circle.draw(contx1);
circles.push(circle);
}
}
}
I changed the variable names to something meaningful. One note here is that you mix code to create the circles and code to draw them. You don't want to do this. Create them once on initialization and redraw them as changes are made (clicking). You don't want to re-create the circles every time you redraw.
Lastly change this:
// Circle
this.clicked = function(){
points[this.spoke][0] = this.x;
points[this.spoke][1] = this.y;
updateCanvas();
}
And elsewhere:
function updateCanvas() {
contx1.clearRect(0, 0, canv.width, canv.height);
drawBackgroundPentagons(contx1);
drawMainPentagon(contx1, points);
drawPoints();
}
If I can make a suggestion, start with the simplest code you can. Start just by displaying the circles and pentagons, get that working cleanly and build onto it. Try and keep logic separate in your code. There are several places where you create objects and initialize arrays (like coords) while you are drawing which is both unnecssary but also means that you do it over and over instead of just once. There is a also lot of code here that is unnecessary.
I'm trying to find a way to put as much hexagons in a circle as possible. So far the best result I have obtained is by generating hexagons from the center outward in a circular shape.
But I think my calculation to get the maximum hexagon circles is wrong, especially the part where I use the Math.ceil() and Math.Floor functions to round down/up some values.
When using Math.ceil(), hexagons are sometimes overlapping the circle.
When using Math.floor() on the other hand , it sometimes leaves too much space between the last circle of hexagons and the circle's border.
var c_el = document.getElementById("myCanvas");
var ctx = c_el.getContext("2d");
var canvas_width = c_el.clientWidth;
var canvas_height = c_el.clientHeight;
var PI=Math.PI;
var PI2=PI*2;
var hexCircle = {
r: 110, /// radius
pos: {
x: (canvas_width / 2),
y: (canvas_height / 2)
}
};
var hexagon = {
r: 20,
pos:{
x: 0,
y: 0
},
space: 1
};
drawHexCircle( hexCircle, hexagon );
function drawHexCircle(hc, hex ) {
drawCircle(hc);
var hcr = Math.ceil( Math.sqrt(3) * (hc.r / 2) );
var hr = Math.ceil( ( Math.sqrt(3) * (hex.r / 2) ) ) + hexagon.space; // hexRadius
var circles = Math.ceil( ( hcr / hr ) / 2 );
drawHex( hc.pos.x , hc.pos.y, hex.r ); //center hex ///
for (var i = 1; i<=circles; i++) {
for (var j = 0; j<6; j++) {
var currentX = hc.pos.x+Math.cos(j*PI2/6)*hr*2*i;
var currentY = hc.pos.y+Math.sin(j*PI2/6)*hr*2*i;
drawHex( currentX,currentY, hex.r );
for (var k = 1; k<i; k++) {
var newX = currentX + Math.cos((j*PI2/6+PI2/3))*hr*2*k;
var newY = currentY + Math.sin((j*PI2/6+PI2/3))*hr*2*k;
drawHex( newX,newY, hex.r );
}
}
}
}
function drawHex(x, y, r){
ctx.beginPath();
ctx.moveTo(x,y-r);
for (var i = 0; i<=6; i++) {
ctx.lineTo(x+Math.cos((i*PI2/6-PI2/4))*r,y+Math.sin((i*PI2/6-PI2/4))*r);
}
ctx.closePath();
ctx.stroke();
}
function drawCircle( circle ){
ctx.beginPath();
ctx.arc(circle.pos.x, circle.pos.y, circle.r, 0, 2 * Math.PI);
ctx.closePath();
ctx.stroke();
}
<canvas id="myCanvas" width="350" height="350" style="border:1px solid #d3d3d3;">
If all the points on the hexagon are within the circle, the hexagon is within the circle. I don't think there's a simpler way than doing the distance calculation.
I'm not sure how to select the optimal fill point, (but here's a js snippet proving that the middle isn't always it). It's possible that when you say "hexagon circle" you mean hexagon made out of hexagons, in which case the snippet proves nothing :)
I made the hexagon sides 2/11ths the radius of the circle and spaced them by 5% the side length.
var hex = {x:0, y:0, r:10};
var circle = {x:100, y:100, r:100};
var spacing = 1.05;
var SQRT_3 = Math.sqrt(3);
var hexagon_offsets = [
{x: 1/2, y: -SQRT_3 / 2},
{x: 1, y: 0},
{x: 1/2, y: SQRT_3 / 2},
{x: -1/2, y: SQRT_3 / 2},
{x: -1, y: 0},
{x: -1/2, y: -SQRT_3 / 2}
];
var bs = document.body.style;
var ds = document.documentElement.style;
bs.height = bs.width = ds.height = ds.width = "100%";
bs.border = bs.margin = bs.padding = 0;
var c = document.createElement("canvas");
c.style.display = "block";
c.addEventListener("mousemove", follow, false);
document.body.appendChild(c);
var ctx = c.getContext("2d");
window.addEventListener("resize", redraw);
redraw();
function follow(e) {
hex.x = e.clientX;
hex.y = e.clientY;
redraw();
}
function drawCircle() {
ctx.strokeStyle = "black";
ctx.beginPath();
ctx.arc(circle.x, circle.y, circle.r, 0, 2 * Math.PI, true);
ctx.closePath();
ctx.stroke();
}
function is_in_circle(p) {
return Math.pow(p.x - circle.x, 2) + Math.pow(p.y - circle.y, 2) < Math.pow(circle.r, 2);
}
function drawLine(a, b) {
var within = is_in_circle(a) && is_in_circle(b);
ctx.strokeStyle = within ? "green": "red";
ctx.beginPath();
ctx.moveTo(a.x, a.y);
ctx.lineTo(b.x, b.y);
ctx.closePath();
ctx.stroke();
return within;
}
function drawShape(shape) {
var within = true;
for (var i = 0; i < shape.length; i++) {
within = drawLine(shape[i % shape.length], shape[(i + 1) % shape.length]) && within;
}
if (!within) return false;
ctx.fillStyle = "green";
ctx.beginPath();
ctx.moveTo(shape[0].x, shape[0].y);
for (var i = 1; i <= shape.length; i++) {
ctx.lineTo(shape[i % shape.length].x, shape[i % shape.length].y);
}
ctx.closePath();
ctx.fill();
return true;
}
function calculate_hexagon(x, y, r) {
return hexagon_offsets.map(function (offset) {
return {x: x + r * offset.x, y: y + r * offset.y};
})
}
function drawHexGrid() {
var hex_count = 0;
var grid_space = calculate_hexagon(0, 0, hex.r * spacing);
var y = hex.y;
var x = hex.x;
while (y > 0) {
y += grid_space[0].y * 3;
x += grid_space[0].x * 3;
}
while (y < c.height) {
x %= grid_space[1].x * 3;
while (x < c.width) {
var hexagon = calculate_hexagon(x, y, hex.r);
if (drawShape(hexagon)) hex_count++;
x += 3 * grid_space[1].x;
}
y += grid_space[3].y;
x += grid_space[3].x;
x += 2 * grid_space[1].x;
}
return hex_count;
}
function redraw() {
c.width = window.innerWidth;
c.height = window.innerHeight;
circle.x = c.width / 2;
circle.y = c.height / 2;
circle.r = Math.min(circle.x, circle.y) * 0.9;
hex.r = circle.r * (20 / 110);
ctx.clearRect(0, 0, c.width, c.height);
var hex_count = drawHexGrid();
drawCircle();
ctx.fillStyle = "rgb(0, 0, 50)";
ctx.font = "40px serif";
ctx.fillText(hex_count + " hexes within circle", 20, 40);
}