Making Balls Bounce - javascript

//Initializing General Setup Variables
'''var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var h = canvas.height;
var w = canvas.width;
var sAngle = 0;
const PI = Math.PI;
var r = 10;
totalNum = 5;'''
//total num is the number of balls wanted
//Function to Create The Initial Positions of All The Balls
'''var initialPos = [];
function makePositions(){
for (var i = 1; i <= totalNum; i++)
{initialPos.push([i*100*Math.random(),i*100*Math.random()])}
console.log(initialPos);
}
makePositions();'''
//Function to Draw Balls at initial positions in the array makePositions
function drawBalls (){
for (i = 0; i <= totalNum - 1; i++) {
ctx.beginPath();
ctx.arc(initialPos[i][0], initialPos[i][1], r, sAngle, 2*PI);
ctx.stroke();
ctx.fill();
}
}
drawBalls();
//Initializing Variables to Move Balls
var dt = 0.01;
var vx = 50;
var vy = 50;
//Creating New Positions With Velocities
function changePos (num){return num + vx*dt} //Chenges position based on velocity
and time
function changePos2 (num){return num - vx*dt} //Chenges position based on velocity
and time
var allPos = [];
function move () {
allPos = [];
var newPos;
for (j = 0; j <= totalNum - 1; j++){
if (0 < initialPos[j][0]< 500 && 0 < initialPos[j][1]< 500)
{newPos = initialPos[j].map(changePos)}
if (initialPos[j][0] < 0 || initialPos[j][0] > w)
{var newPos = [changePos2(initialPos[j][0]),changePos(initialPos[j][1])]}
if (initialPos[j][1] < 0 || initialPos[j][1] > h) {var newPos =
[changePos(initialPos[j][0]),changePos2(initialPos[j][1])]}
allPos.push(newPos);
}
initialPos = allPos;
console.log(allPos)
}
/* new positions are pushed into the array allPos
map iterates through the array intiialPos and performs the changePos function on each element in the array */
//Clearing Canvas
function resetCanvas () {
canvas.width = canvas.width;
}
//Drawing the Balls at their new positions
function drawBalls2 (){
for (var i = 0; i <= totalNum - 1; i++) {
ctx.beginPath();
ctx.arc(allPos[i][0], allPos[i][1], r, sAngle, 2*PI);
ctx.stroke();
ctx.fill();
}
}
//Drawing Balls -> Moving Balls -> Reset Canvas -> Draw
function ball (){
resetCanvas();
move();
drawBalls2();
}
setInterval(ball, 100 * dt);
I'm trying to make it so that in my move function when the balls hit the canvas borders, they bounce off in the opposite direction, however with my current parameters in my move function, it is not working. The balls are gathering in a corner and then freezing. Any ideas on how to fix this?

In general there are multiple flaws within your move function.
1) 0 < initialPos[j][0] < 500
Checking for ranges inJavaScript dosn't work like this.
This will boil down to (0 < initialPos[j][0]) < 500. This gets evaluated to 0 or 1 < 500 which is true for all cases.
2) initialPos[j].map(changePos)
When I understood that correctly, initialPos is a two dimensional array,
containing an array with the positions for all balls.
But with the map(changePos) you're applying the X-vector to the X and Y coordinate of the ball.
3) You're not permanently changing the vector for the given ball (The cause for the ball being stuck)
Within your corner case blocks your applying the changePos2 transformation correctly.
But since the new position will then be back into the bounds you're falling back to the normal newPos = initialPos[j].map(changePos) transition without changing the movement direction.
In order to solve that you'll need an array containing the vectors of each ball.
Then within the move loopapply this vector to the balls position. If the new position is out of the game, invert the vector component (x or y) and the ball will bounce off the wall again.

Related

how can i rewrite this code to make an object fall from top of canvas and when hits bottom it resets at top

I am trying to implement a group of objects that will fall from the top of the canvas in random spots, and then when they hit the bottom of the canvas they respawn at the top of the canvas. Ideally, I want to change this code in the future to use sprites that will fall from the top of the canvas and explode when they hit the bottom of the canvas but then respawn again at the top.
I have the bellow code that works when the mouse is pressed but I would like to rewrite this code so the event happens automatically without the mouse needed to be pressed to make the objects fall.
see code below.
var x, y;
var particle = [];
function setup() {
createCanvas(720, 400);
// Starts in the middle
x = width / 2;
y = height;
}
function draw() {
background(0);
if (mouseIsPressed){
stroke(50);
fill(100);
ellipse(x, y, 24, 24);
}
x = x + 0;
// Moving up at a constant speed
y = y + 2;
// Reset to the bottom
if (y >= height) {
y = 0;
}
}
Use the mousePressed() to add new particles to the list. The particles are added at the current mouse position (mouseX, mouseY). Move and draw the particles in a loop:
var particles = [];
function setup() {
createCanvas(720, 400);
}
function mousePressed() {
particles.push([mouseX, mouseY]);
}
function draw() {
background(0);
stroke(50);
for (let i=0; i < particles.length; i++) {
particles[i][1] += 2;
if (particles[i][1] > height) {
particles[i][1] = 0;
}
fill(100);
ellipse(particles[i][0], particles[i][1], 24, 24);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
Alternatively, you can spawn the particles with a time interval. Get the number of milliseconds (thousandths of a second) since starting the sketch with millis(). The y-coordinate of a new particle is 0 and the x-coordinate is random():
var particles = [];
function setup() {
createCanvas(720, 400);
}
function mousePressed() {
particles.push([mouseX, mouseY]);
}
function draw() {
let max_no_of_particles = 40;
let expected_no_of_praticles = min(max_no_of_particles, millis() / 100);
if (particles.length < expected_no_of_praticles) {
particles.push([random(12, width-12), 0]);
}
background(0);
stroke(50);
for (let i=0; i < particles.length; i++) {
particles[i][1] += 2;
if (particles[i][1] > height) {
particles[i][1] = 0;
}
fill(100);
ellipse(particles[i][0], particles[i][1], 24, 24);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>

p5.js – Smoothly morphing random shape

first of all, i am a beginner on js and p5.js. My aim on this program is a smoothly morphing random shape. I was satisfied with the calculateShape()-function and the drawShape()-function, but when it comes to morphing (updateShape()) it gets really ugly. I thought it might be a good idea to save my current array into a temporary array, then loop over the array and add a random number to each x and y of each index and then replace the old x and y at this index. The main problem is, that it is always adding new shapes on the screen instead of changing the values of the vertices of the existing shape. Can anybody of you please give me a hint or point out my mistake(s)? THANK YOU IN ADVANCE!
var c1;
var c2;
var c3;
var centerX;
var centerY;
var fb;
var radius;
var angle;
var shape = [];
var temp;
/*function to calculate the inital shape*/
function calculateShape() {
//calculate coordinates and save into array
for (var i = 0; i < fb; i++) {
var x = cos(angle * i) * radius + random(-77,77);
var y = sin(angle * i) * radius + random(-77,77);
var v = createVector(x, y);
shape.push(v);
}
}
/*function for morphing the shape*/
function updateShape() {
var temp = shape;
for (var i = 0; i < shape.length - 1; i++) {
var x = temp[i].x + random(-1, 1);
var y = temp[i].y + random(-1, 1);
var p = temp[i];
var v = createVector(x, y);
shape.splice(p,1);
shape.push(v);
}
}
/*function for drawing the shape on the screen*/
function createShape(){
beginShape();
curveVertex(shape[shape.length-1].x, shape[shape.length-1].y);
for (var i = 0; i < shape.length; i++){
curveVertex(shape[i].x, shape[i].y);
}
curveVertex(shape[0].x, shape[0].y);
endShape(CLOSE);
}
function setup() {
createCanvas(windowWidth, windowHeight);
smooth();
background(250);
//frameRate(2);
// defining possible colors
c1 = color(0, 196, 181, 235);
c2 = color(50, 227, 232, 235);
c3 = color(248, 49, 62, 255);
var colors = [c1, c2, c3];
//center of the window
centerX = windowWidth/2;
centerY = windowHeight/2;
//defining all variables
fb = 8;
angle = radians(360 / fb);
radius = random(120, 140);
//calling thefunction that initalises the shape
calculateShape();
}
function draw() {
translate(centerX, centerY);
blendMode(BLEND);
fill(c3);
noStroke();
createShape();
updateShape();
}
The main problem is, that it is always adding new shapes on the screen instead of changing the values of the vertices of the existing shape.
Sure, you just need to clear the screen before drawing again. So, reset the background with the background(250) from setup, in draw.

Javascript 3d Terrain Without Three.js

I have searched around but I can't find anything like what I'm trying to do that doesn't use Three.js in some way (I can't use Three.js because my computer is too old to support Webgl). Here's what I've got so far:
HTML:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="terrain.js"></script>
<title>Terrain</title>
</head>
<body>
<canvas id="canvas" height="400" width="400"></canvas>
</body>
</html>
Javascript:
var canvas, ctx, row1 = [], row2 = [], intensity = 15, width = 20, height = 20, centery = 200, centerx = 200, minus, delta = 1.6, nu = .02;
window.onload = function() {
canvas = document.getElementById('canvas'), ctx = canvas.getContext('2d');
ctx.lineStyle = '#000'
for (var i = 0; i < height; i++) {
row2 = [];
minus = 200
for (var j = 0; j < width; j++) {
row2[j] = {
x: centerx - (minus * (delta * (nu * i))),
y: Math.floor(Math.random() * intensity) + (height * i)
}
minus -= height;
}
ctx.beginPath();
ctx.moveTo(row2[0].x,row2[0].y)
for (var k = 1; k < row2.length; k++) {
ctx.lineTo(row2[k].x,row2[k].y)
if (k == row2.length) {ctx.clostPath()}
}
ctx.stroke();
if (row1[0] && row2[0]) {
for (var l = 0; l < row2.length; l++) {
ctx.beginPath();
ctx.moveTo(row2[l].x,row2[l].y)
ctx.lineTo(row1[l].x,row1[l].y)
ctx.closePath();
ctx.stroke();
}
}
row1 = row2;
}
}
Currently, the result looks like a Christmas tree but I want it to look more like actual 3d wireframe terrain.
3D wire frame basics
3D can be done on any systems that can move pixels. Thought not by dedicated hardware Javascript can do alright if you are after simple 3d.
This answers shows how to create a mesh, rotate and move it, create a camera and move it, and project the whole lot onto the 2D canvas using simple moveTo, and lineTo calls.
This answer is a real rush job so apologies for the typos (if any) and messy code. Will clean it up in the come few days (if time permits). Any questions please do ask in the comments.
Update
I have not done any basic 3D for some time so having a little fun I have added to the answer with more comments in the code and added some extra functionality.
vec3 now has normalise, dot, cross functions.
mat now has lookat function and is ready for much more if needed.
mesh now maintains its own world matrix
Added box, and line that create box and line meshs
Created a second vector type vec3S (S for simple) that is just coordinates no functionality
Demo now shows how to add more objects, position them in the scene, use a lookat transform
Details about the code.
The code below is the basics of 3D. It has a mesh object to create objects out of 3D points (vertices) connected via lines.
Simple transformation for rotating, moving and scaling a model so it can be placed in the scene.
A very very basic camera that can only look forward, move up,down, left,right, in and out. And the focal length can be changed.
Only for lines as there is no depth sorting.
The demo does not clip to the camera front plane, but rather just ignores lines that have any part behind the camera;
You will have to work out the rest from the comments, 3D is a big subject and any one of the features is worth a question / answer all its own.
Oh and coordinates in 3D are origin in center of canvas. Y positive down, x positive right, and z positive into the screen. projection is basic so when you have perspective set to 400 than a object at 400 units out from camera will have a one to one match with pixel size.
var ctx = canvas.getContext("2d");
// some usage of vecs does not need the added functionality
// and will use the basic version
const vec3Basic = { x : 0, y : 0, z: 0};
const vec3Def = {
// Sets the vector scalars
// Has two signatures
// setVal(x,y,z) sets vector to {x,y,z}
// setVal(vec) set this vector to vec
setVal(x,y = x.y,z = x.z + (x = x.x) * 0){
this.x = x;
this.y = y;
this.z = z;
},
// subtract v from this vector
// Has two signatures
// setVal(v) subtract v from this returning a new vec3
// setVal(v,vec) subtract v from this returning result in retVec
sub(v,retVec = vec3()){
retVec.x = this.x - v.x;
retVec.y = this.y - v.y;
retVec.z = this.z - v.z;
return retVec;
},
// Cross product of two vectors this and v.
// Cross product can be thought of as get the vector
// that is perpendicular to the plane described by the two vector we are crossing
// Has two signatures
// cross(vec); // returns a new vec3 as the cross product of this and vec
// cross(vec, retVec); // set retVec as the cross product
cross (v, retVec = vec3()){
retVec.x = this.y * v.z - this.z * v.y;
retVec.y = this.z * v.x - this.x * v.z;
retVec.z = this.x * v.y - this.y * v.x;
return retVec;
},
// Dot product
// Dot product of two vectors if both normalized can be thought of as finding the cos of the angle
// between two vectors. If not normalised the dot product will give you < 0 if v points away from
// the plane that this vector is perpendicular to, if > 0 the v points in the same direction as the
// plane perpendicular to this vector. if 0 then v is at 90 degs to the plane this is perpendicular to
// Using vector dot on its self is the same as getting the length squared
// dot(vec3); // returns a number as a float
dot (v){ return this.x * v.x + this.y * v.y + this.z * this.z },
// normalize normalizes a vector. A normalized vector has length equale to 1 unit
// Has two signitures
// normalise(); normalises this vector returning this
// normalize(retVec); normalises this vector but puts the normalised vector in retVec returning
// returning retVec. Thiis is unchanged.
normalize(retVec = this){
// could have used len = this.dot(this) but for speed all functions will do calcs internaly
const len = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
// it is assumed that all vector are valid (have length) so no test is made to avoid
// the divide by zero that will happen for invalid vectors.
retVec.x = this.x / len;
retVec.y = this.y / len;
retVec.z = this.z / len;
}
}
// Created as a singleton to close over working constants
const matDef = (()=>{
// to seed up vector math the following closed over vectors are used
// rather than create and dispose of vectors for every operation needing them
// Currently not used
const V1 = vec3();
return {
// The matrix is just 3 pointers one for each axis
// They represent the direction and scale in 3D of each axis
// when you transform a point x,y,z you move x along the x axis,
// then y along y and z along the z axis
xAxis : null,
yAxis : null,
zAxis : null,
// this is a position x,y,z and represents where in 3D space an objects
// center coordinate (0,0,0) will be. It is simply added to a point
// after it has been moved along the 3 axis.
pos : null,
// This function does most of the 3D work in most 3D environments.
// It rotates, scales, translates, and a whole lot more.
// It is a cut down of the full 4 by 4 3D matrix you will find in
// Libraries like three.js
transformVec3(vec,retVec = {}){
retVec.x = vec.x * this.xAxis.x + vec.y * this.yAxis.x + vec.z * this.zAxis.x + this.pos.x;
retVec.y = vec.x * this.xAxis.y + vec.y * this.yAxis.y + vec.z * this.zAxis.y + this.pos.y;
retVec.z = vec.x * this.xAxis.z + vec.y * this.yAxis.z + vec.z * this.zAxis.z + this.pos.z;
return retVec;
},
// resets the matrix
identity(){ // default matrix
this.xAxis.setVal(1,0,0); // x 1 unit long in the x direction
this.yAxis.setVal(0,1,0); // y 1 unit long in the y direction
this.zAxis.setVal(0,0,1); // z 1 unit long in the z direction
this.pos.setVal(0,0,0); // and position at the origin.
},
init(){ // need to call this before using due to the way I create these
// objects.
this.xAxis = vec3(1,0,0);
this.yAxis = vec3(0,1,0);
this.zAxis = vec3(0,0,1);
this.pos = vec3(0,0,0);
return this; // must have this line for the constructor function to return
},
setRotateY(amount){
var x = Math.cos(amount);
var y = Math.sin(amount);
this.xAxis.x = x;
this.xAxis.y = 0;
this.xAxis.z = y;
this.zAxis.x = -y;
this.zAxis.y = 0;
this.zAxis.z = x;
},
// creates a look at transform from the current position
// point is a vec3.
// No check is made to see if look at is at pos which will invalidate this matrix
// Note scale is lost in this operation.
lookAt(point){
// zAxis along vector from pos to point
this.pos.sub(point,this.zAxis).normalize();
// use y as vertical reference
this.yAxis.x = 0;
this.yAxis.y = 1;
this.yAxis.z = 0;
// get x axis perpendicular to the plane described by z and y axis
// need to normalise as z and y axis may not be at 90 deg
this.yAxis.cross(this.zAxis,this.xAxis).normalize();
// Get the y axis that is perpendicular to z and x axis
// Normalise is not really needed but rounding errors can be problematic
// so the normalise just fixes some of the rounding errors.
this.zAxis.cross(this.xAxis,this.yAxis).normalize();
},
}
})();
// Mesh object has buffers for the
// model as verts
// transformed mesh as tVerts
// projected 2D verts as dVerts (d for display)
// An a array of lines. Each line has two indexes that point to the
// vert that define their ends.
// Buffers are all preallocated to stop GC slowing everything down.
const meshDef = {
addVert(vec){
this.verts.push(vec);
// vec3(vec) in next line makes a copy of the vec. This is important
// as using the same vert in the two buffers will result in strange happenings.
this.tVerts.push(vec3S(vec)); // transformed verts pre allocated so GC does not bite
this.dVerts.push({x:0,y:0}); // preallocated memory for displaying 2d projection
// when x and y are zero this means that it is not visible
return this.verts.length - 1;
},
addLine(index1,index2){
this.lines.push(index1,index2);
},
transform(matrix = this.matrix){
for(var i = 0; i < this.verts.length; i++){
matrix.transformVec3(this.verts[i],this.tVerts[i]);
}
},
eachVert(callback){
for(var i = 0; i < this.verts.length; i++){
callback(this.tVerts[i],i);
}
},
eachLine(callback){
for(var i = 0; i < this.lines.length; i+= 2){
var ind1 = this.lines[i];
var v1 = this.dVerts[ind1]; // get the start
if(v1.x !== 0 && v1.y !== 0){ // is valid
var ind2 = this.lines[i+ 1]; // get end of line
var v2 = this.dVerts[ind2];
if(v2.x !== 0 && v2.y !== 0){ // is valid
callback(v1,v2);
}
}
}
},
init(){ // need to call this befor using
this.verts = [];
this.lines = [];
this.dVerts = [];
this.tVerts = [];
this.matrix = mat();
return this; // must have this line for the construtor function to return
}
}
const cameraDef = {
projectMesh(mesh){ // create a 2D mesh
mesh.eachVert((vert,i)=>{
var z = (vert.z + this.position.z);
if(z < 0){ // is behind the camera then ignor it
mesh.dVerts[i].x = mesh.dVerts[i].y = 0;
}else{
var s = this.perspective / z;
mesh.dVerts[i].x = (vert.x + this.position.x) * s;
mesh.dVerts[i].y = (vert.y + this.position.y) * s;
}
})
},
drawMesh(mesh){ // renders the 2D mesh
ctx.beginPath();
mesh.eachLine((v1,v2)=>{
ctx.moveTo(v1.x,v1.y);
ctx.lineTo(v2.x,v2.y);
})
ctx.stroke();
}
}
// vec3S creates a basic (simple) vector
// 3 signatures
//vec3S(); // return vec 1,0,0
//vec3S(vec); // returns copy of vec
//vec3S(x,y,z); // returns {x,y,z}
function vec3S(x = {x:1,y:0,z:0},y = x.y ,z = x.z + (x = x.x) * 0){ // a 3d point
return Object.assign({},vec3Basic,{x, y, z});
}
// vec3S creates a basic (simple) vector
// 3 signatures
//vec3S(); // return vec 1,0,0
//vec3S(vec); // returns copy of vec
//vec3S(x,y,z); // returns {x,y,z}
function vec3(x = {x:1,y:0,z:0},y = x.y ,z = x.z + (x = x.x) * 0){ // a 3d point
return Object.assign({},vec3Def,{x,y,z});
}
function mat(){ // matrix used to rotate scale and move a 3d point
return Object.assign({},matDef).init();
}
function mesh(){ // this is for storing objects as points in 3d and lines conecting points
return Object.assign({},meshDef).init();
}
function camera(perspective,position){ // this is for displaying 3D
return Object.assign({},cameraDef,{perspective,position});
}
// grid is the number of grids x,z and size is the overal size for x
function createLandMesh(gridx,gridz,size,maxHeight){
var m = mesh(); // create a mesh
var hs = size/2 ;
var step = size / gridx;
for(var z = 0; z < gridz; z ++){
for(var x = 0; x < gridx; x ++){
// create a vertex. Y is random
m.addVert(vec3S(x * step - hs, (Math.random() * maxHeight), z * step-hs)); // create a vert
}
}
for(var z = 0; z < gridz-1; z ++){
for(var x = 0; x < gridx-1; x ++){
if(x < gridx -1){ // dont go past end
m.addLine(x + z * gridx,x + 1 + z * gridx); // add line across
}
if(z < gridz - 1){ // dont go past end
m.addLine(x + z * (gridx-1),x + 1 + (z + 1) * (gridx-1));
}
}
}
return m;
}
function createBoxMesh(size){
var s = size / 2;
var m = mesh(); // create a mesh
// add bottom
m.addVert(vec3S(-s,-s,-s));
m.addVert(vec3S( s,-s,-s));
m.addVert(vec3S( s, s,-s));
m.addVert(vec3S(-s, s,-s));
// add top verts
m.addVert(vec3S(-s,-s, s));
m.addVert(vec3S( s,-s, s));
m.addVert(vec3S( s, s, s));
m.addVert(vec3S(-s, s, s));
// add lines
/// bottom lines
m.addLine(0,1);
m.addLine(1,2);
m.addLine(2,3);
m.addLine(3,0);
/// top lines
m.addLine(4,5);
m.addLine(5,6);
m.addLine(6,7);
m.addLine(7,4);
// side lines
m.addLine(0,4);
m.addLine(1,5);
m.addLine(2,6);
m.addLine(3,7);
return m;
}
function createLineMesh(v1 = vec3S(),v2 = vec3S()){
const m = mesh();
m.addVert(v1);
m.addVert(v2);
m.addLine(0,1);
return m;
}
//Create a land mesh grid 20 by 20 and 400 units by 400 units in size
var land = createLandMesh(20,20,400,20); // create a land mesh
var box = createBoxMesh(50);
var box1 = createBoxMesh(25);
var line = createLineMesh(); // line conecting boxes
line.tVerts[0] = box.matrix.pos; // set the line transformed tVect[0] to box matrix.pos
line.tVerts[1] = box1.matrix.pos; // set the line transformed tVect[0] to box1 matrix.pos
var cam = camera(200,vec3(0,0,0)); // create a projection with focal len 200 and at 0,0,0
box.matrix.pos.setVal(0,-100,400);
box1.matrix.pos.setVal(0,-100,400);
land.matrix.pos.setVal(0,100,300); // move down 100, move away 300
var w = canvas.width;
var h = canvas.height;
var cw = w / 2; // center of canvas
var ch = h / 2;
function update(timer){
// next section just maintains canvas size and resets state and clears display
if (canvas.width !== innerWidth || canvas.height !== innerHeight) {
cw = (w = canvas.width = innerWidth) /2;
ch = (h = canvas.height = innerHeight) /2;
}
ctx.setTransform(1,0,0,1,0,0); // reset transform
ctx.globalAlpha = 1; // reset alpha
ctx.fillStyle = "black";
ctx.fillRect(0,0,canvas.width,canvas.height);
// end of standard canvas maintenance
// render from center of canvas by setting canvas origin to center
ctx.setTransform(1,0,0,1,canvas.width / 2,canvas.height / 2)
land.matrix.setRotateY(timer/1000); // set matrix to rotation position
land.transform();
// move the blue box
var t = timer/1000;
box1.matrix.pos.setVal(Math.sin(t / 2.1) * 100,Math.sin( t / 3.2) * 100, Math.sin(t /5.3) * 90+300);
// Make the cyan box look at the blue box
box.matrix.lookAt(box1.matrix.pos);
// Transform boxes from local to world space
box1.transform();
box.transform();
// set camera x,y pos to mouse pos;
cam.position.x = mouse.x - cw;
cam.position.y = mouse.y - ch;
// move in and out
if (mouse.buttonRaw === 1) { cam.position.z -= 1 }
if (mouse.buttonRaw === 4) {cam.position.z += 1 }
// Converts mesh transformed verts to 2D screen coordinates
cam.projectMesh(land);
cam.projectMesh(box);
cam.projectMesh(box1);
cam.projectMesh(line);
// Draw each mesh in turn
ctx.strokeStyle = "#0F0";
cam.drawMesh(land);
ctx.strokeStyle = "#0FF";
cam.drawMesh(box);
ctx.strokeStyle = "#00F";
cam.drawMesh(box1);
ctx.strokeStyle = "#F00";
cam.drawMesh(line);
ctx.setTransform(1,0,0,1,cw,ch / 4);
ctx.font = "20px arial";
ctx.textAlign = "center";
ctx.fillStyle = "yellow";
ctx.fillText("Move mouse to move camera. Left right mouse move in out",0,0)
requestAnimationFrame(update);
}
requestAnimationFrame(update);
// A mouse handler from old lib of mine just to give some interaction
// not needed for the 3d
var mouse = (function () {
var m; // alias for mouse
var mouse = {
x : 0, y : 0, // mouse position
buttonRaw : 0,
buttonOnMasks : [0b1, 0b10, 0b100], // mouse button on masks
buttonOffMasks : [0b110, 0b101, 0b011], // mouse button off masks
bounds : null,
event(e) {
m.bounds = m.element.getBoundingClientRect();
m.x = e.pageX - m.bounds.left - scrollX;
m.y = e.pageY - m.bounds.top - scrollY;
if (e.type === "mousedown") { m.buttonRaw |= m.buttonOnMasks[e.which - 1] }
else if (e.type === "mouseup") { m.buttonRaw &= m.buttonOffMasks[e.which - 1] }
e.preventDefault();
},
start(element) {
m.element = element === undefined ? document : element;
"mousemove,mousedown,mouseup".split(",").forEach(name => document.addEventListener(name, mouse.event) );
document.addEventListener("contextmenu", (e) => { e.preventDefault() }, false);
return mouse;
},
}
m = mouse;
return mouse;
})().start(canvas);
canvas { position:absolute; top : 0px; left : 0px;}
<canvas id="canvas"></canvas>

Is it possible to detect where a canvas is clicked on and remove an item after it's clicked?

I have a set of javascript code which creates a circle object and then pushes it onto the canvas. Is there a way so that if a user were to click on one of the circles the object would disappear?
Here's my Javascript Code
// get a reference to the canvas and its context
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
// set canvas to equal window size
window.addEventListener('resize', resizeCanvas, false);
// newly spawned objects start at Y=25
var spawnLineY = 25;
// spawn a new object every 1500ms
var spawnRate = 1500;
// when was the last object spawned
var lastSpawn = -1;
// this array holds all spawned object
var objects = [];
// save the starting time (used to calc elapsed time)
var startTime = Date.now();
// start animating
animate();
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
/**
* Your drawings need to be inside this function otherwise they will
be reset when
* you resize the browser window and the canvas goes will be cleared.
*/
}
function spawnRandomObject() {
// select a random type for this new object
var t;
// About Math.random()
// Math.random() generates a semi-random number
// between 0-1. So to randomly decide if the next object
// will be A or B, we say if the random# is 0-.49 we
// create A and if the random# is .50-1.00 we create B
var random;
random = Math.random();
if (random < 0.75 && random > .50) {
t = "red";
} else if (random > .75) {
t = "blue";
} else if (random < .50 && random > .25) {
t = "purple"
} else if (random < .25) {
t = "green"
}
// create the new object
var object = {
// set this objects type
type: t,
// set x randomly but at least 15px off the canvas edges
x: Math.random() * (canvas.width - 30) + 15,
// set y to start on the line where objects are spawned
y: spawnLineY,
downspeed: Math.floor((Math.random() * 100) + 5)/100,
radius: Math.floor((Math.random() * 175) + 5),
onclick : alert('blah'),
}
// add the new object to the objects[] array
objects.push(object);
}
// the code to make the circle disappear would go here
popBalloon()
function animate() {
// get the elapsed time
var time = Date.now();
// see if its time to spawn a new object
if (time > (lastSpawn + spawnRate)) {
lastSpawn = time;
spawnRandomObject();
}
// request another animation frame
requestAnimationFrame(animate);
// clear the canvas so all objects can be
// redrawn in new positions
ctx.clearRect(0, 0, canvas.width, canvas.height);
// draw the line where new objects are spawned
ctx.beginPath();
ctx.moveTo(0, spawnLineY);
ctx.lineTo(canvas.width, spawnLineY);
ctx.stroke();
// move each object down the canvas
for (var i = 0; i < objects.length; i++) {
var object = objects[i];
object.y += object.downspeed;
ctx.beginPath();
ctx.arc(object.x, object.y, object.radius, 0, Math.PI * 2);
ctx.closePath();
ctx.fillStyle = object.type;
ctx.fill();
}
}
resizeCanvas();
To get the mouse click position on the canvas
// assuming canvas is already defined and references an DOM canvas Element
const mouse = {x ;0,y :0, clicked : false};
function mouseClickEvent(event){
mouse.x = event.offsetX;
mouse.y = event.offsetY;
mouse.clicked = true;
}
canvas.addEventListener("click",mouseClickEvent);
Then in your render loop check for mouse clicks
if(mouse.clicked){
// do what is needed
mouse.clicked = false; /// clear the clicked flag
}
To find out if a point is inside a circle
// points is {x,y}
// circle is {x,y,r} where r is radius
// returns true if point touches circle
function isPointInCircle(point,circle){
var x = point.x - circle.x;
var y = point.y - circle.y;
return Math.sqrt(x * x + y * y) <= circle.r;
}
Or with ES6
function isPointInCircle(point,circle){
return Math.hypot(point.x - circle.x, point.y - circle.y) <= circle.r;
}
I am not entirely sure how to get the mouse x and y position but if you can get that this function should help detect collision
function checkColision()// if mouse collides with object
{
for (var i = 0; i < objects.length; i++)
{
// get the distance between mouse and object
var xRange = Math.abs(mouse.x - object[i].x);//abslute value so no negative numbers
var yRange = Math.abs(mouse.y - object[i].y);
if (xRange < MIN_COLISION_RANGE && yRange < MIN_COLISION_RANGE)
{
object.splice(i, 1);//remove object
i--;//go back one iteration
}
}
}

calculate the x, y position of a canvas point

I'm trying to learn some canvas in html5 and javascript and I want to create those typical Illustrator sun rays:
But my problem is that I want to automate it and make it full screen.
To calculate the coordinates of the points in the middle isn't hard, it's the outer points that I cant seem to get a grip on.
K, so this is what I got.
The problem lies in the for-loop for creating an array for the outer coordinates.
So it starts calculating from the center of the screen.
If it's the first point (we ignore the inner points for now) it takes the x_coordinate variable (which is the horizontal center of the screen) and adds the width_between_rays divided by two (because I want to mimic the picture above with some space between the two upper rays).
The rest of the points are checked if they are divided by two to see if I should add the width_between_rays (should probably be offset or something) or the width_of_rays to the last points cordinates.
Well this seems pretty straight forward but since the window size isn't a fixed size I need some way of calculating where the point should be if, for example; the position of a point is outside the width/height of the screen.
So my way of calculating this doesn't work (I think).
Anyways, can someone (who's obviously smarter than me) point me in the right direction?
function sun_rays(z_index, element, color, number_of_rays, width_of_rays, width_between_rays) {
// Start the canvas stuff
var canvas = document.getElementById(element);
var ctx = canvas.getContext("2d");
console.log();
ctx.canvas.width = $(window).width();
ctx.canvas.height = $(window).width();
ctx.fillStyle = color;
// calculate the window size and center position
var window_width = $(window).width();
var window_hight = $(window).height();
var x_coordinate = window_width / 2;
var y_coordinate = window_hight / 2;
// create an array for the center coordinates
var center_coordinate_array = new Array();
for(i=0; i < number_of_rays; i++){
center_coordinate_array[i] = new Array(x_coordinate, y_coordinate);
}
// create an array for the outer coordinates
var outer_coordinate_array = new Array();
for(i=1; i == number_of_rays*2; i++){
if(i == 1) {
// X
var last_outer_x_coordinate = x_coordinate + (width_between_rays/2);
// Y
if(last_outer_x_coordinate < window_width) {
last_outer_y_coordinate = last_outer_y_coordinate;
} else {
$x_coordinate_difference = last_outer_x_coordinate - window_width;
last_outer_y_coordinate = x_coordinate_difference;
}
center_coordinate_array[i] = new Array(last_outer_x_coordinate, last_outer_y_coordinate);
} else {
if(i % 2 == 0) {
// X
last_outer_x_coordinate = last_outer_x_coordinate + width_of_rays;
// Y
//calculate the y position
center_coordinate_array[i] = new Array(last_outer_x_coordinate);
} else {
// X
last_outer_x_coordinate = last_outer_x_coordinate + width_between_rays;
// Y
//calculate the y position
center_coordinate_array[i] = new Array(last_outer_x_coordinate);
}
}
}
}
It seems like you should use the trig functions to do something like this.
var coordinate_array = [];
var xCoord = 0;
var yCoord = 0;
var angleIncrement = 15;
var i = 0;
//iterate over angles (in degrees) from 0 to 360
for (var theta = 0; theta < 360; theta += angleIncrement) {
//angle is in sector from bottom right to top right corner
if (theta >= 315 || theta <= 45)
{
xCoord = $(window).width();//point on right side of canvas
yCoord = abs($(window).width()/2 * tan(theta));
yCoord = tranformY(theta,yCoord);
}
//angle is in sector from top right to top left corner
else if (theta > 45 && theta <= 135)
{
yCoord = 0; //top is zero
xCoord = abs($(window).height()/2 * tan(theta));
xCoord = transformX(theta, xCoord);
}
//angle is in sector from top left to bottom left corner
else if (theta > 135 && theta <= 225)
{
xCoord = 0; //left edge on a canvas is zero
yCoord = abs($(window).width()/2 * tan(theta);
yCoord = transformY(theta, yCoord);
}
//angle is in sector from bottom left to bottom right corner
else // theta > 225 && theta < 315
{
yCoord = $(window).height();
xCoord = abs($(window).height()/2 * tan(theta));
xCoord = transformX(theta, xCoord);
}
coordinate_array[i++] = new Array(xCoord, yCoord);
}
//Transform from cartesian coordinates to top left is 0,0
function tranformY(theta, y)
{
var centerYCoord = $(window).height()/2;
//if angle falls in top half (Quadrant 1 or 2)
if(theta > 0 && theta < 180)
{
return centerYCoord - y;
}
elseif(theta > 180 && theta < 360)
{
return centerYCoord + y;
}
//coord falls on 0/360 or 180 (vert. median)
return centerYCoord;
}
//Transform from cartesian coordinates to top left is 0,0
function transformX(theta, x)
{
var centerXCoord = $(window).width()/2;
//if angle falls in right half (Quadrant 1 or 4)
if(theta > 270 || theta < 90)
{
return centerXCoord + x;
}
elseif(theta > 90 && theta < 270)
{
return centerXCoord - x;
}
//coordinate falls on 270 or 90 (center)
return centerXCoord;
}
//now draw your rays from the center coordinates to the points in coordinate_array
//NOTE: This code will need to be cleaned up - I just wrote it in the textbox.
The previous code puts the coordinates for the red points into an array.
This problem is by its very nature related to the incremental change of an angle. Your solution is going to need to deal with the angles using trig functions.

Categories