How to organize circles into coherent "glob" - javascript

Here's my code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Johnny's Potential Homepage</title>
<style type="text/css">
#font-face {
font-family: Abel Regular;
src: url("fonts/Abel-Regular.ttf");
}
body {
margin: 0px;
overflow: hidden;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script type="text/javascript">
class Circle {
constructor(x, y, radius) {
this.x = x;
this.y = y;
this.destinationX = x;
this.destinationY = y;
this.originalRadius = radius;
this.radius = radius/2;
this.contentAlpha = 0;
this.displayDelay = 50;
this.displayDelayTicker = 0;
}
update() {
var offset = Math.sqrt(Math.sqrt(Math.pow(this.x-mouseX, 2)+(Math.pow(this.y-mouseY, 2))));
var angle = -Math.atan2(this.x-mouseX, this.y-mouseY) - Math.PI/2;
this.destinationX = canvas.width / 2 + offset * Math.cos(angle);
this.destinationY = canvas.height / 2 + offset * Math.sin(angle);
this.fixCollisions();
this.x += (this.destinationX - this.x) / 10;
this.y += (this.destinationY - this.y) / 10;
this.radius += (this.originalRadius - this.radius) / 10;
if (this.displayDelayTicker >= this.displayDelay) {
this.contentAlpha += (1 - this.contentAlpha) / 10;
}
this.displayDelayTicker = Math.min(this.displayDelay, this.displayDelayTicker+1);
}
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2);
ctx.fillStyle = "#000";
ctx.filter = "blur(120px)";
ctx.fill();
ctx.filter = "blur(0px)";
ctx.fillStyle = "rgba(0, 0, 0, 0.8)";
ctx.fill();
}
fixCollisions() {
for (var i = 0; i < circles.length; i++) {
if (circles[i] != this) {
var angle = getAngle([this.x, this.y], [circles[i].x, circles[i].y]);
var dist = ((this.radius + circles[i].radius)/2 + padding);
// Distribute
this.destinationX += Math.cos(-angle) * dist;
this.destinationY += Math.sin(-angle) * dist;
}
}
}
}
class Centerpiece extends Circle {
constructor(x, y, radius) {
super(x, y, radius);
this.time = 0;
this.readTime = "";
this.readSeconds = "";
}
update() {
super.update();
var date = new Date();
var destTime = date.getHours()*3600 + date.getMinutes()*60 + date.getSeconds();
if (this.displayDelayTicker >= this.displayDelay) {
this.time += (destTime - this.time) / 10;
}
var hours = Math.floor(this.time / 3600);
if (hours > 12) {
hours -= 12;
}
var minutes = (this.time/60) % 60;
minutes = Math.floor(minutes);
if (minutes < 10) {
minutes = "0" + minutes;
}
var seconds = (this.time) % 60;
seconds = Math.floor(seconds);
if (seconds < 10) {
seconds = "0" + seconds;
}
this.readTime = hours + ":" + minutes;
this.readSeconds = seconds;
}
draw() {
super.draw();
ctx.font = "14px Abel Regular";
var secondsWidth = ctx.measureText(this.readSeconds).width;
ctx.font = "21px Abel Regular";
var timeWidth = ctx.measureText(this.readTime).width;
ctx.textAlign = "center";
ctx.fillStyle = "rgba(255, 255, 255, " + this.contentAlpha + ")";
ctx.fillText(this.readTime, this.x - secondsWidth/2 -1, this.y);
ctx.font = "14px Abel Regular";
ctx.fillText(this.readSeconds, this.x + timeWidth/2 + 1, this.y);
}
}
var canvas = document.getElementById("canvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ctx = canvas.getContext("2d");
var circles = [];
var mouseX = canvas.width/2;
var mouseY = canvas.height/2;
var scale = 1.0;
var scrollAmount = 0;
var padding = Math.sqrt(getDistance([0, 0], [canvas.width/2, canvas.height/2]));
circles.push(new Centerpiece(canvas.width/2, canvas.height/2, 120));
circles.push(new Centerpiece(canvas.width/2, canvas.height/2, 100));
circles.push(new Centerpiece(canvas.width/2, canvas.height/2, 80));
circles.push(new Centerpiece(canvas.width/2, canvas.height/2, 90));
window.onresize = function() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var padding = Math.sqrt(getDistance([0, 0], [canvas.width/2, canvas.height/2])) + 10;
}
window.onmousemove = function(e) {
mouseX = e.clientX;
mouseY = e.clientY;
}
window.onwheel = function(e) {
scrollAmount -= Math.min(Math.max(-0.5, e.deltaY), 0.5);
scale = (Math.log10(Math.min(2, Math.max(0.5, scrollAmount + 1)))) + 1;
console.log(scrollAmount + " " + scale);
}
function getDistance(a, b) {
return (Math.sqrt((b[0]-a[0])*(b[0]-a[0])-(b[1]-a[1])*(b[1]-a[1])));
}
function getAngle(a, b) {
return Math.atan2(b[1] - a[1], b[0] - a[0]);
}
function loop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < circles.length; i++) {
circles[i].update();
}
for (var i = 0; i < circles.length; i++) {
circles[i].draw();
}
requestAnimationFrame(loop);
}
loop();
</script>
</body>
</html>
My goal is to have the circles "glob" together in the middle, yet not collide. Take a look at the fixCollisions() function and see if there's a way to update things so the circles spread evenly across the x and y axis.
Also, I'm not sure why it bugs out at the beginning.

I think this does what you need, if I understood correctly. I commented out some lines and changed some others. I removed the mouse following part because I did not get to fix that and it causes a problem by resetting the position of the blobs every time it updates.
Then I changed the way the blobs are made to be attracted to each other but not allowing them to overlap.
I also simplified the way the sines and cosines are calculated. You don't actually need to first find the angle and the take the cosine and sine of the angle since you already have these when dividing the x and y displacements by the distance. That is in actual fact dividing the opposite and the adjacent sides by the hypotenuse, which is actually the definition of a sine and cosine.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Johnny's Potential Homepage</title>
<style type="text/css">
#font-face {
font-family: Abel Regular;
src: url("fonts/Abel-Regular.ttf");
}
body {
margin: 0px;
overflow: hidden;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script type="text/javascript">
class Circle {
constructor(x, y, radius) {
this.x = x;
this.y = y;
this.destinationX = x;
this.destinationY = y;
this.originalRadius = radius;
this.radius = radius/2;
this.contentAlpha = 0;
this.displayDelay = 50;
this.displayDelayTicker = 0;
}
update() {
var offset = Math.sqrt(Math.sqrt(Math.pow(this.x-mouseX, 2)+(Math.pow(this.y-mouseY, 2))));
var angle = -Math.atan2(this.x-mouseX, this.y-mouseY) - Math.PI/2;
//this.destinationX = canvas.width / 2 + offset * Math.cos(angle);
//this.destinationY = canvas.height / 2 + offset * Math.sin(angle);
this.fixCollisions();
this.x += (this.destinationX - this.x) / 10;
this.y += (this.destinationY - this.y) / 10;
// removed this.radius += (this.originalRadius - this.radius) / 10;
// changed
this.x += Math.min( 1 * ( canvas.width / 2 - this.x ),1 );
this.y += Math.min( 1 * ( canvas.height / 2 - this.y ),1 );
if (this.displayDelayTicker >= this.displayDelay) {
this.contentAlpha += (1 - this.contentAlpha) / 10;
}
this.displayDelayTicker = Math.min(this.displayDelay, this.displayDelayTicker+1);
}
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2);
ctx.fillStyle = "#000";
ctx.filter = "blur(120px)";
ctx.fill();
ctx.filter = "blur(0px)";
ctx.fillStyle = "rgba(0, 0, 0, 0.8)";
ctx.fill();
}
fixCollisions() {
for (var i = 0; i < circles.length; i++)
{
if (circles[i] != this) {
//var angle = getAngle([this.x, this.y], [circles[i].x, circles[i].y]);
//var dist = ((this.radius + circles[i].radius)/2 + padding);
var minDist = ((this.radius + circles[i].radius));
var dist = Math.sqrt( (this.x-circles[i].x)*(this.x-circles[i].x) + (this.y-circles[i].y)*(this.y-circles[i].y) )
// Distribute
if ( dist < minDist) // overlapping so push away
{
this.destinationX += 100 * (( this.x - circles[i].x) / minDist ) / ( dist + 1 );
this.destinationY += 100 * (( this.y - circles[i].y) / minDist ) / ( dist + 1 );
}
if ( dist > minDist) // overlapping so push away
{
this.destinationX += -0.001 * (( this.x - circles[i].x) / minDist ) * dist;
this.destinationY += -0.001 * (( this.y - circles[i].y) / minDist ) * dist;
}
}
}
}
}
class Centerpiece extends Circle {
constructor(x, y, radius) {
super(x, y, radius);
this.time = 0;
this.readTime = "";
this.readSeconds = "";
}
update() {
super.update();
var date = new Date();
var destTime = date.getHours()*3600 + date.getMinutes()*60 + date.getSeconds();
if (this.displayDelayTicker >= this.displayDelay) {
this.time += (destTime - this.time) / 10;
}
var hours = Math.floor(this.time / 3600);
if (hours > 12) {
hours -= 12;
}
var minutes = (this.time/60) % 60;
minutes = Math.floor(minutes);
if (minutes < 10) {
minutes = "0" + minutes;
}
var seconds = (this.time) % 60;
seconds = Math.floor(seconds);
if (seconds < 10) {
seconds = "0" + seconds;
}
this.readTime = hours + ":" + minutes;
this.readSeconds = seconds;
}
draw() {
super.draw();
ctx.font = "14px Abel Regular";
var secondsWidth = ctx.measureText(this.readSeconds).width;
ctx.font = "21px Abel Regular";
var timeWidth = ctx.measureText(this.readTime).width;
ctx.textAlign = "center";
ctx.fillStyle = "rgba(255, 255, 255, " + this.contentAlpha + ")";
ctx.fillText(this.readTime, this.x - secondsWidth/2 -1, this.y);
ctx.font = "14px Abel Regular";
ctx.fillText(this.readSeconds, this.x + timeWidth/2 + 1, this.y);
}
}
var canvas = document.getElementById("canvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ctx = canvas.getContext("2d");
var circles = [];
var mouseX = canvas.width/2;
var mouseY = canvas.height/2;
var scale = 1.0;
var scrollAmount = 0;
var padding = Math.sqrt(getDistance([0, 0], [canvas.width/2, canvas.height/2]));
circles.push(new Centerpiece(0.27 * canvas.width, 0.67 * canvas.height/2, 120));
circles.push(new Centerpiece(0.98 * canvas.width, 0.23 * canvas.height/2, 100));
circles.push(new Centerpiece(0.46 * canvas.width, 0.15 * canvas.height/2, 80));
circles.push(new Centerpiece(0.37 * canvas.width, 0.29 * canvas.height/2, 90));
window.onresize = function() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var padding = Math.sqrt(getDistance([0, 0], [canvas.width/2, canvas.height/2])) + 10;
}
window.onmousemove = function(e) {
mouseX = e.clientX;
mouseY = e.clientY;
}
window.onwheel = function(e) {
scrollAmount -= Math.min(Math.max(-0.5, e.deltaY), 0.5);
scale = (Math.log10(Math.min(2, Math.max(0.5, scrollAmount + 1)))) + 1;
console.log(scrollAmount + " " + scale);
}
function getDistance(a, b) {
return (Math.sqrt((b[0]-a[0])*(b[0]-a[0])-(b[1]-a[1])*(b[1]-a[1])));
}
function getAngle(a, b) {
return Math.atan2(b[1] - a[1], b[0] - a[0]);
}
function loop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < circles.length; i++) {
circles[i].update();
}
for (var i = 0; i < circles.length; i++) {
circles[i].draw();
}
requestAnimationFrame(loop);
}
loop();
</script>
</body>
</html>

Related

Uncaught TypeError: ctx.clearReact is not a function at animate

Im pretty new here and in coding, and Im trying to finish this tutorial:
https://www.youtube.com/watch?v=d620nV6bp0A&ab_channel=Frankslaboratory
const canvas = document.getElementById('canvas1');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let particlesArray;
let mouse = {
x: null,
y: null,
radius: (canvas.height/80) * (canvas.width/80)
}
window.addEventListener('mousemove',
function(event){
mouse.x = event.x;
mouse.y = event.y;
}
);
class Particle {
constructor(x, y, directionX, directionY, size, color){
this.x = x;
this.y = y;
this.directionX = directionX;
this.directionY = directionY;
this.size = size;
this.color = color;
}
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2, false );
ctx.fillStyle = '#8C5523';
ctx.fill();
}
update() {
if(this.x > canvas.width || this.x <0 ){
this.directionX = - this.directionX;
}
if(this.y > canvas.height || this.y < 0){
this.directionY = - this.directionY;
}
let dx = mouse.x - this.x;
let dy = mouse.y - this.y;
let distance = Math.sqrt(dx * dx + dy * dy)
if (disance < mouse.radius + this.size){
if(mouse.x < this.x && this.x <canvas.width - this.size * 10){
this.x += 10;
}
if(mouse.x >this.x && this.x > this.size *10){
this.x -=10;
}
if(mouse.y < this.y && this.y < canvas.height - this.size * 10){
this.y += 10;
}
if(mouse.y > this.y && this.y > this.size * 10){
this.y -= 10;
}
}
this.x += this.directionX;
this.y += this.directionY;
this.draw();
}
}
function init() {
particlesArray = [];
let numberOfParticles = (canvas.height * canvas.width) / 9000;
for (let i = 0; i < numberOfParticles *2 ; i++){
let size = (Math.random() * 5 ) + 1;
let x = (Math.random() * ((innerWidth - size * 2) - (size * 2)) + size * 2);
let y = (Math.random() * ((innerHeight - size * 2) - (size * 2)) + size * 2);
let directionX = (Math.random() * 5) - 2.5;
let directionY = (Math.random() * 5) - 2.5;
let color = '#8C5523';
particlesArray.push(new Particle(x, y ,directionX, directionY, size, color));
}
}
function connect(){
let opacityValue = 1;
for( let a=0; a < particlesArray.length; a++ ){
for(let b = a; b < particlesArray.length; b++){
let distance = (( particlesArray[a].x - particlesArray[b].x)
* (particlesArray[a].x - particlesArray[b].x))
+ ((particlesArray[a].y - particlesArray[b].y) *
(particlesArray[a] .y - particlesArray[b].y));
if(distance < (canvas.width/7) * (canvas.height/7)){
opacityValue = 1 - (distance/20000);
ctx.strokeStyle='rgba(140,85,31,' + opacityValue + ')';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(particlesArray[a].x, particlesArray[a].y);
ctx.lineTo(particlesArray[b].x, particlesArray[b].y);
ctx.stroke();
}
}
}
}
function animate(){
requestAnimationFrame (animate);
ctx.clearReact(0, 0, innerWidth, innerHeight)
for (let i = 0; i < particlesArray.length; i++) {
particlesArray[i].update();
}
connect();
}
window.addEventListener('resize',
function(){
canvas.width = innerWidth;
canvas.height = innerHeight;
mouse.radius = ((canvas.height/80) * (canvas.height/80));
init();
}
);
window.addEventListener('mouseout',
function(){
mouse.x = undefined;
mouse.x = undefined;
}
)
init();
animate();
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
#canvas1 {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: radial-gradient(#ffc38c,#ff9b40);
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MyWebsitePLAX</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<canvas id="canvas1"></canvas>
<script src="script.js"></script>
</body>
</html>
I am making progress up to this point and then I have a lot of errors in my console stacking and saying: "Uncaught TypeError: ctx.clearReact is not a function
at animate"
When I open this project nothing is rendering on website. I Find out that here is some kind of problem
function animate(){
requestAnimationFrame (animate);
ctx.clearReact(0, 0, innerWidth, innerHeight)
for (let i = 0; i < particlesArray.length; i++) {
particlesArray[i].update();
}
connect();
}
Could some1 possible help to solve this problem and explain some part of it to :)?

Adding time elapsed to a canvas game in Javascript

I am having a problem with integrating a simple timer to a game I am trying to build for a practice exercise. I have attempted to search for this solution on the internet but my basic knowledge of .js leaves me stuck at this problem. Can anyone help me with a solution to this?
My codepen is located here: https://codepen.io/jankyvision/pen/PoqYEXV
var c = document.createElement("canvas");
var ctx = c.getContext("2d");
c.width = 720;
c.height = 480;
document.body.appendChild(c);
var perm = [];
while (perm.length < 255){
while(perm.includes(val = Math.floor(Math.random()*255)));
perm.push(val);
}
var lerp = (a,b,t) => a + (b-a) * (1-Math.cos(t*Math.PI))/2;
var noise = x=>{
x = x * 0.01 % 254;
return lerp(perm[Math.floor(x)], perm[Math.ceil(x)], x - Math.floor(x));
}
var Player = function(){
this.x = c.width/2;
this.y = 0;
this.ySpeed = 0;
this.rot = 0;
this.rSpeed = 0;
this.img = new Image();
this.img.src = "./images/moto.png";
this.draw = function(){
var p1 = c.height - noise(t + this.x) * 0.25;
var p2 = c.height - noise(t+5 + this.x) * 0.25;
var grounded = 0;
if(p1-12 > this.y){
this.ySpeed += 0.1;
}else{
this.ySpeed -= this.y - (p1-12);
this.y = p1 - 12;
grounded = 1;
}
var angle = Math.atan2((p2-12) - this.y, (this.x+5) - this.x);
this.y += this.ySpeed;
if(!playing || grounded && Math.abs(this.rot) > Math.PI * 0.5){
playing = false;
this.rSpeed = 5;
k.ArrowUp = 1;
this.x -= speed * 5;
}
if(grounded && playing){
this.rot -= (this.rot - angle) * 0.65;
this.rSpeed = this.rSpeed - (angle - this.rot);
}
this.rSpeed += (k.ArrowLeft - k.ArrowRight) * 0.05;
this.rot -= this.rSpeed * 0.1;
if(this.rot > Math.PI) this.rot = -Math.PI;
if(this.rot < -Math.PI) this.rot = Math.PI;
ctx.save();
ctx.translate(this.x, this.y - 3);
ctx.rotate(this.rot);
ctx.drawImage(this.img, -15, -15, 60, 30);
ctx.restore();
}
}
var player = new Player();
var t = 0;
var speed = 0;
var playing = true;
var k = {ArrowUp:0, ArrowDown:0, ArrowLeft:0, ArrowRight:0};
function loop(){
speed -= (speed - (k.ArrowUp - k.ArrowDown)) * 0.01;
t += 10 * speed;
ctx.fillStyle = "#8D5BC2";
ctx.fillRect(0,0,c.width, c.height);
ctx.fillStyle = "rgba(0,0,0,0.1)";
ctx.beginPath();
ctx.moveTo(0, c.height);
for (let i = 0; i < c.width; i++)
ctx.lineTo(i, c.height*0.8 - noise(t + i*5) * 0.25);
ctx.lineTo(c.width, c.height);
ctx.fill();
ctx.fillStyle = "#444";
ctx.beginPath();
ctx.moveTo(0, c.height);
for (let i = 0; i < c.width; i++)
ctx.lineTo(i, c.height - noise(t + i) * 0.25);
ctx.lineTo(c.width, c.height);
ctx.fill();
player.draw();
if(player.x < 0)
restart();
requestAnimationFrame(loop);
}
onkeydown = d=> k[d.key] = 1;
onkeyup = d=> k[d.key] = 0;
function restart(){
player = new Player();
t = 0;
speed = 0;
playing = true;
k = {ArrowUp:0, ArrowDown:0, ArrowLeft:0, ArrowRight:0};
}
loop();
var instructions = document.createElement("div");
instructions.innerHTML += "[up] [down] = accelerate <br> [Left] [Rigth] = rotate";
document.body.appendChild(instructions);
With some help from https://stackoverflow.com/a/16255190/1309377 you are looking at a pretty simple function that uses ctx.fillText() to draw the amount of elapsed seconds onto the canvas.
You want to use the Date() object when doing a timer as it is more accurate than setTimeout or setInterval.
On restart you just set startTime to the current time again.
var c = document.createElement("canvas");
var ctx = c.getContext("2d");
var startTime = new Date();
c.width = 720;
c.height = 480;
document.body.appendChild(c);
var perm = [];
while (perm.length < 255) {
while (perm.includes(val = Math.floor(Math.random() * 255)));
perm.push(val);
}
var lerp = (a, b, t) => a + (b - a) * (1 - Math.cos(t * Math.PI)) / 2;
var noise = x => {
x = x * 0.01 % 254;
return lerp(perm[Math.floor(x)], perm[Math.ceil(x)], x - Math.floor(x));
}
function drawElapsedTime() {
var elapsed = parseInt((new Date() - startTime) / 1000);
ctx.save();
ctx.beginPath();
ctx.fillStyle = "black";
ctx.font = "14px Verdana"
// draw the running time at half opacity
ctx.globalAlpha = 0.50;
ctx.fillText(elapsed + " seconds", 30,30);
ctx.restore();
}
var Player = function() {
this.x = c.width / 2;
this.y = 0;
this.ySpeed = 0;
this.rot = 0;
this.rSpeed = 0;
this.img = new Image();
this.img.src = "https://upload.wikimedia.org/wikipedia/en/3/3b/SpongeBob_SquarePants_character.svg";
this.draw = function() {
var p1 = c.height - noise(t + this.x) * 0.25;
var p2 = c.height - noise(t + 5 + this.x) * 0.25;
var grounded = 0;
if (p1 - 12 > this.y) {
this.ySpeed += 0.1;
} else {
this.ySpeed -= this.y - (p1 - 12);
this.y = p1 - 12;
grounded = 1;
}
var angle = Math.atan2((p2 - 12) - this.y, (this.x + 5) - this.x);
this.y += this.ySpeed;
if (!playing || grounded && Math.abs(this.rot) > Math.PI * 0.5) {
playing = false;
this.rSpeed = 5;
k.ArrowUp = 1;
this.x -= speed * 5;
}
if (grounded && playing) {
this.rot -= (this.rot - angle) * 0.65;
this.rSpeed = this.rSpeed - (angle - this.rot);
}
this.rSpeed += (k.ArrowLeft - k.ArrowRight) * 0.05;
this.rot -= this.rSpeed * 0.1;
if (this.rot > Math.PI) this.rot = -Math.PI;
if (this.rot < -Math.PI) this.rot = Math.PI;
ctx.save();
ctx.translate(this.x, this.y - 3);
ctx.rotate(this.rot);
ctx.drawImage(this.img, -15, -15, 60, 30);
ctx.restore();
}
}
var player = new Player();
var t = 0;
var speed = 0;
var playing = true;
var k = {
ArrowUp: 0,
ArrowDown: 0,
ArrowLeft: 0,
ArrowRight: 0
};
function loop() {
speed -= (speed - (k.ArrowUp - k.ArrowDown)) * 0.01;
t += 10 * speed;
ctx.fillStyle = "#8D5BC2";
ctx.fillRect(0, 0, c.width, c.height);
ctx.fillStyle = "rgba(0,0,0,0.1)";
ctx.beginPath();
ctx.moveTo(0, c.height);
for (let i = 0; i < c.width; i++)
ctx.lineTo(i, c.height * 0.8 - noise(t + i * 5) * 0.25);
ctx.lineTo(c.width, c.height);
ctx.fill();
ctx.fillStyle = "#444";
ctx.beginPath();
ctx.moveTo(0, c.height);
for (let i = 0; i < c.width; i++)
ctx.lineTo(i, c.height - noise(t + i) * 0.25);
ctx.lineTo(c.width, c.height);
ctx.fill();
player.draw();
drawElapsedTime();
if (player.x < 0)
restart();
requestAnimationFrame(loop);
}
onkeydown = d => k[d.key] = 1;
onkeyup = d => k[d.key] = 0;
function restart() {
player = new Player();
startTime = new Date();
t = 0;
speed = 0;
playing = true;
k = {
ArrowUp: 0,
ArrowDown: 0,
ArrowLeft: 0,
ArrowRight: 0
};
}
loop();
var instructions = document.createElement("div");
instructions.innerHTML += "[up] [down] = accelerate <br> [Left] [Rigth] = rotate";
document.body.appendChild(instructions);
var secs = 0;
setInterval(function(){secs++},1000);
this.draw = function(){
// Fill in a rectangle to put the time inside of
ctx.fillStyle="#000000";
ctx.fillRect(10,10,55,25);
ctx.fillStyle="#8D5BC2";
ctx.font = "12px";
// Format the number of seconds - 0:00
timestr = Math.floor(secs/60) + ":";
timestr += ((secs%60<10)&&"0") + secs%60;
// Write the time string over the rectangle
ctx.fillText(timestr,25,25);

adding custom animation in canvas html5

this might be somewhat difficult but i wil still ask, so i made a starfield ,now what i want to do is to have my stars( a pair each) connected to eachother by a line ,now this line will expand as the stars move forward and disappear when the stars move out of the canvas .any help would be appreciated here this is difficult i have the logic but i seem unable to follow the correct way to implement it
function randomRange(minVal, maxVal) {
return Math.floor(Math.random() * (maxVal - minVal - 1)) + minVal;
}
function initStars() {
for (var i = 0; i < stars.length; i++) {
stars[i] = {
x: randomRange(-25, 25),
y: randomRange(-25, 25),
z: randomRange(1, MAX_DEPTH)
}
}
}
function degToRad(deg) {
radians = (deg * Math.PI / 180) - Math.PI / 2;
return radians;
}
function animate() {
var halfWidth = canvas.width / 2;
var halfHeight = canvas.height / 2;
ctx.fillStyle = "rgb(0,0,0)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < stars.length; i++) {
stars[i].z -= 0.2;
if (stars[i].z <= 0) {
stars[i].x = randomRange(-25, 25);
stars[i].y = randomRange(-25, 25);
stars[i].z = MAX_DEPTH;
}
var k = 128.0 / stars[i].z;
var px = stars[i].x * k + halfWidth;
var py = stars[i].y * k + halfHeight;
if (px >= 0 && px <= 1500 && py >= 0 && py <= 1500) {
var size = (1 - stars[i].z / 32.0) * 5;
var shade = parseInt((1 - stars[i].z / 32.0) * 750);
ctx.fillStyle = "rgb(" + shade + "," + shade + "," + shade + ")";
ctx.beginPath();
ctx.arc(px, py, size, degToRad(0), degToRad(360));
ctx.fill();
}
}
}
function animate() {
var halfWidth = canvas.width / 2;
var halfHeight = canvas.height / 2;
ctx.fillStyle = "rgb(0,0,0)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < stars.length; i++) {
stars[i].z -= 0.2;
if (stars[i].z <= 0) {
stars[i].x = randomRange(-25, 25);
stars[i].y = randomRange(-25, 25);
stars[i].z = MAX_DEPTH;
}
var k = 128.0 / stars[i].z;
var px = stars[i].x * k + halfWidth;
var py = stars[i].y * k + halfHeight;
if (px >= 0 && px <= 1500 && py >= 0 && py <= 1500) {
var size = (1 - stars[i].z / 32.0) * 5;
var shade = parseInt((1 - stars[i].z / 32.0) * 750);
ctx.fillStyle = "rgb(" + shade + "," + shade + "," + shade + ")";
ctx.beginPath();
ctx.arc(px, py, size, degToRad(0), degToRad(360));
ctx.fill();
}
}
}
<!DOCTYPE html5>
<html>
<head>
<title>stars</title>
<script src="convergis.js"></script>
<script>
MAX_DEPTH = 32;
var canvas, ctx;
var stars = new Array(500);
window.onload = function() {
canvas = document.getElementById("tutorial");
if( canvas && canvas.getContext ) {
ctx = canvas.getContext("2d");
initStars();
setInterval(animate,17);
}
}
</script>
</head>
<body>
<canvas id='tutorial' width='1500' height='1500'>
</canvas>
</body>
</html>
You could just say you want a lightspeed effect!
One way very cheap way to do it is to paint the background with some transparency. You can also render a set of points close together in order to make the illusion of the effect.
The good way to do it is shaders since they will allow you to add glow and some other nice image trickery that will make it look better. Here is a good example: https://www.shadertoy.com/view/Xdl3D2
Below I used the canvas api lineTo and even with a fixed line width, it's a pretty good final result.
var MAX_DEPTH = 64;
var LINELENGTH = 0.1;
var stars = new Array(500);
var canvas = document.getElementById("tutorial");
canvas.width = innerWidth;
canvas.height = innerHeight;
var ctx = canvas.getContext("2d");
initStars();
setInterval(animate,17);
function randomRange(minVal, maxVal) {
return Math.floor(Math.random() * (maxVal - minVal - 1)) + minVal;
}
function initStars() {
for (var i = 0; i < stars.length; i++) {
stars[i] = {
x: randomRange(-25, 25),
y: randomRange(-25, 25),
z: randomRange(1, MAX_DEPTH)
}
}
}
function degToRad(deg) {
radians = (deg * Math.PI / 180) - Math.PI / 2;
return radians;
}
function animate() {
var halfWidth = canvas.width / 2;
var halfHeight = canvas.height / 2;
ctx.fillStyle = "rgba(0,0,0,1)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < stars.length; i++) {
stars[i].z -= 0.5;
if (stars[i].z <= 0) {
stars[i].x = randomRange(-25, 25);
stars[i].y = randomRange(-25, 25);
stars[i].z = MAX_DEPTH;
}
var k = 254.0 / stars[i].z;
var px = stars[i].x * k + halfWidth;
var py = stars[i].y * k + halfHeight;
if (px >= 0 && px <= 1500 && py >= 0 && py <= 1500) {
var size = (1 - stars[i].z / 32.0) * 2;
var shade = parseInt((1 - stars[i].z / 32.0) * 750);
ctx.strokeStyle = "rgb(" + shade + "," + shade + "," + shade + ")";
ctx.lineWidth = size;
ctx.beginPath();
ctx.moveTo(px,py);
var ox = size * (px - halfWidth) * LINELENGTH;
var oy = size * (py - halfHeight) * LINELENGTH;
ctx.lineTo(px + ox, py + oy);
ctx.stroke();
}
}
}
<canvas id='tutorial' width='1500' height='1500'></canvas>

how can i make two objects belonging to the same array move independently of each other using javascript and the canvas tag?

I am trying to create a blackhole simulation, where all the balls that are outside of it go away from it at a given speed and those that fall on it are dragged towards the circle until they reach the center of it, where they would stop and disappear, here is my code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>blackhole simulation escape velocity</title>
<script>
var canvas, ctx;
var blackhole;
var circle;
var circles = new Array();
var G = 6.67e-11, //gravitational constant
pixel_G = G / 1e-11,
c = 3e8, //speed of light (m/s)
M = 12e31, // masseof the blackhole in kg (60 solar masses)
pixel_M = M / 1e32
Rs = (2 * G * M) / 9e16, //Schwarzchild radius
pixel_Rs = Rs / 1e3, // scaled radius
ccolor = 128;
function update() {
var pos, i, distance, somethingMoved = false;
for (i = 0; i < circles.length; i++) {
pos = circles[i].position;
distance = Math.sqrt(((pos.x - 700) * (pos.x - 700)) + ((pos.y - 400) * (pos.y - 400)));
if (distance > pixel_Rs-5 ) {
var delta = new Vector2D(0, 0);
var forceDirection = Math.atan2(pos.y - 400, pos.x - 700);
var evelocity = Math.sqrt((2 * pixel_G * pixel_M) / (distance * 1e-2));
delta.x += Math.cos(forceDirection) * evelocity;
delta.y += Math.sin(forceDirection) * evelocity;
pos.x += delta.x;
pos.y += delta.y;
somethingMoved = true;
} else {
var delta2 = new Vector2D (0,0);
var forceDirection2 = Math.atan2(pos.y - 400, pos.x - 700);
var g = (pixel_G*pixel_M)/(distance*distance*1e2);
delta2.x += Math.cos(forceDirection2)*g;
delta2.y += Math.sin(forceDirection2)*g;
pos.x -= delta2.x;
pos.y -= delta2.y;
somethingMoved = true;
circles[i].color -= 1;
if (pos.x == 700 && pos.y == 400){
somethingMoved = false;
};
}
}
if (somethingMoved) {
drawEverything();
requestAnimationFrame(update);
};
}
function drawEverything() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
blackhole.draw(ctx);
for (var i = 0; i < circles.length; i++) {
circles[i].draw(ctx);
}
}
function init(event) {
canvas = document.getElementById("space");
ctx = canvas.getContext('2d');
blackhole = new Ball(pixel_Rs, { x: 700,
y: 400 }, 0);
for (var i = 0; i < 200; i++) {
var vec2D = new Vector2D(Math.floor(Math.random() * 1400), Math.floor(Math.random() * 800));
circle = new Ball(5, vec2D, ccolor);
circles.push(circle);
}
drawEverything();
requestAnimationFrame(update);
}
function Ball(radius, position, color) {
this.radius = radius;
this.position = position;
this.color = color;
}
Ball.prototype.draw = function(ctx) {
var c=parseInt(this.color);
ctx.fillStyle = 'rgba(' + c + ',' + c + ',' + c + ',1)';
ctx.beginPath();
ctx.arc(this.position.x, this.position.y, this.radius, 0, 2 * Math.PI);
ctx.closePath();
ctx.fill();
};
function Vector2D(x, y) {
this.x = x;
this.y = y;
}
function onClick (){
canvas = document.getElementById ('space');
ctx = canvas.getContext ('2d')
canvas.addEventListener ("mousedown", init, false)
blackhole = new Ball (5, {x: 700,
y: 400 }, 0);
blackhole.draw (ctx) ;
}
window.onload = onClick;
</script>
<style>
body {
background-color:#021c36 ;
margin: 0px;
}
</style>
</head>
<body>
<canvas id = "space", width = "1400", height = "800">
</canvas>
</body>
</html>
Now as you can see, I created a second variable called delta2, but the problem is that it can't update the position of the circles, which in term makes it impossible to move the circle, can someone tell me what is wrong. Also, how can I make the big black circle after a certain amount of time, i know i probably should create a timer, but i don't know how they work
The gravity is too weak. I put a pseudo gravity to demonstrate.
var canvas, ctx;
var blackhole;
var circle;
var circles = new Array();
var bh = {
w:500,
h:300
};
bh.cx = Math.floor(bh.w/2);
bh.cy = Math.floor(bh.h/2)
var G = 6.67e-11, //gravitational constant
pixel_G = G / 1e-11,
c = 3e8, //speed of light (m/s)
M = 12e31, // masseof the blackhole in kg (60 solar masses)
pixel_M = M / 1e32
Rs = (2 * G * M) / 9e16, //Schwarzchild radius
pixel_Rs = Rs / 1e3, // scaled radius
ccolor = 128;
function update() {
var pos, i, distance, somethingMoved = false;
for (i = 0; i < circles.length; i++) {
pos = circles[i].position;
distance = Math.sqrt(((pos.x - bh.cx) * (pos.x - bh.cx)) + ((pos.y - bh.cy) * (pos.y - bh.cy)));
if (distance > pixel_Rs - 5) {
var delta = new Vector2D(0, 0);
var forceDirection = Math.atan2(pos.y - bh.cy, pos.x - bh.cx);
var evelocity = Math.sqrt((2 * pixel_G * pixel_M) / (distance * 1e-2));
delta.x += Math.cos(forceDirection) * evelocity;
delta.y += Math.sin(forceDirection) * evelocity;
pos.x += delta.x;
pos.y += delta.y;
somethingMoved = true;
} else {
var delta2 = new Vector2D(0, 0);
var forceDirection2 = Math.atan2(pos.y - bh.cy, pos.x - bh.cx);
// FIX THIS!!!
var g = 1;//(pixel_G * pixel_M) / (distance * distance * 1e2);
delta2.x += Math.cos(forceDirection2) * g;
delta2.y += Math.sin(forceDirection2) * g;
pos.x -= delta2.x;
pos.y -= delta2.y;
somethingMoved = true;
circles[i].color -= 1;
if (pos.x == bh.cx && pos.y == bh.cy) {
somethingMoved = false;
};
}
}
if (somethingMoved) {
drawEverything();
requestAnimationFrame(update);
};
}
function drawEverything() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
blackhole.draw(ctx);
for (var i = 0; i < circles.length; i++) {
circles[i].draw(ctx);
}
}
function init(event) {
canvas = document.getElementById("space");
canvas.width = bh.w;
canvas.height = bh.h;
ctx = canvas.getContext('2d');
blackhole = new Ball(5, { //pixel_Rs, {
x: bh.cx,
y: bh.cy
}, 0);
for (var i = 0; i < 200; i++) {
var vec2D = new Vector2D(Math.floor(Math.random() * bh.w), Math.floor(Math.random() * bh.h));
circle = new Ball(5, vec2D, ccolor);
circles.push(circle);
}
drawEverything();
requestAnimationFrame(update);
}
function Ball(radius, position, color) {
this.radius = radius;
this.position = position;
this.color = color;
}
Ball.prototype.draw = function(ctx) {
var c = parseInt(this.color);
ctx.fillStyle = 'rgba(' + c + ',' + c + ',' + c + ',1)';
ctx.beginPath();
ctx.arc(this.position.x, this.position.y, this.radius, 0, 2 * Math.PI);
ctx.closePath();
ctx.fill();
};
function Vector2D(x, y) {
this.x = x;
this.y = y;
}
function onClick() {
canvas = document.getElementById('space');
ctx = canvas.getContext('2d')
canvas.addEventListener("mousedown", init, false)
blackhole = new Ball(5, {
x: bh.cx,
y: bh.cy
}, 0);
blackhole.draw(ctx);
}
window.onload = onClick;
body {
background-color: #021c36;
margin: 0px;
}
<canvas id="space" , width="700" , height="400"></canvas>

Circle animation random color

Hi I try to create an animation with a circle. The function drawRandom(drawFunctions) should pic one of the three drawcircle functions and should bring it on the canvas. Now the problem is that this function become executed every second (main loop) and the circle change his colour. How can I fix that?
window.onload = window.onresize = function() {
var C = 1; // canvas width to viewport width ratio
var el = document.getElementById("myCanvas");
var viewportWidth = window.innerWidth;
var viewportHeight = window.innerHeight;
var canvasWidth = viewportWidth * C;
var canvasHeight = viewportHeight;
el.style.position = "fixed";
el.setAttribute("width", canvasWidth);
el.setAttribute("height", canvasHeight);
var x = canvasWidth / 100;
var y = canvasHeight / 100;
var ballx = canvasWidth / 100;
var n;
window.ctx = el.getContext("2d");
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
// draw triangles
function init() {
ballx;
return setInterval(main_loop, 1000);
}
function drawcircle1()
{
var radius = x * 5;
ctx.beginPath();
ctx.arc(ballx * 108, canvasHeight / 2, radius, 0, 2 * Math.PI, false);
ctx.fillStyle = 'yellow';
ctx.fill();
}
function drawcircle2()
{
var radius = x * 5;
ctx.beginPath();
ctx.arc(ballx * 108, canvasHeight / 2, radius, 0, 2 * Math.PI, false);
ctx.fillStyle = 'blue';
ctx.fill();
}
function drawcircle3()
{
var radius = x * 5;
ctx.beginPath();
ctx.arc(ballx * 105, canvasHeight / 2, radius, 0, 2 * Math.PI, false);
ctx.fillStyle = 'orange';
ctx.fill();
}
function draw() {
var counterClockwise = false;
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
//first halfarc
ctx.beginPath();
ctx.arc(x * 80, y * 80, y * 10, 0 * Math.PI, 1 * Math.PI, counterClockwise);
ctx.lineWidth = y * 1;
ctx.strokeStyle = 'black';
ctx.stroke();
// draw stop button
ctx.beginPath();
ctx.moveTo(x * 87, y * 2);
ctx.lineTo(x * 87, y * 10);
ctx.lineWidth = x;
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x * 95, y * 2);
ctx.lineTo(x * 95, y * 10);
ctx.lineWidth = x;
ctx.stroke();
function drawRandom(drawFunctions){
//generate a random index
var randomIndex = Math.floor(Math.random() * drawFunctions.length);
//call the function
drawFunctions[randomIndex]();
}
drawRandom([drawcircle1, drawcircle2, drawcircle3]);
}
function update() {
ballx -= 0.1;
if (ballx < 0) {
ballx = -radius;
}
}
function main_loop() {
draw();
update();
collisiondetection();
}
init();
function initi() {
console.log('init');
// Get a reference to our touch-sensitive element
var touchzone = document.getElementById("myCanvas");
// Add an event handler for the touchstart event
touchzone.addEventListener("mousedown", touchHandler, false);
}
function touchHandler(event) {
// Get a reference to our coordinates div
var can = document.getElementById("myCanvas");
// Write the coordinates of the touch to the div
if (event.pageX < x * 50 && event.pageY > y * 10) {
ballx += 1;
} else if (event.pageX > x * 50 && event.pageY > y * 10 ) {
ballx -= 1;
}
console.log(event, x, ballx);
draw();
}
initi();
draw();
}
Take a look at my code that I wrote:
var lastTime = 0;
function requestMyAnimationFrame(callback, time)
{
var t = time || 16;
var currTime = new Date().getTime();
var timeToCall = Math.max(0, t - (currTime - lastTime));
var id = window.setTimeout(function(){ callback(currTime + timeToCall); }, timeToCall);
lastTime = currTime + timeToCall;
return id;
}
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
canvas.width = window.innerWidth - 20;
canvas.height = window.innerHeight - 20;
canvas.style.width = canvas.width + "px";
canvas.style.height = canvas.height + "px";
var circles = [];
var mouse =
{
x: 0,
y: 0
}
function getCoordinates(x, y)
{
return "(" + x + ", " + y + ")";
}
function getRatio(n, d)
{
// prevent division by 0
if (d === 0 || n === 0)
{
return 0;
}
else
{
return n/d;
}
}
function Circle(x,y,d,b,s,c)
{
this.x = x;
this.y = y;
this.diameter = Math.round(d);
this.radius = Math.round(d/2);
this.bounciness = b;
this.speed = s;
this.color = c;
this.deltaX = 0;
this.deltaY = 0;
this.drawnPosition = "";
this.fill = function()
{
context.beginPath();
context.arc(this.x+this.radius,this.y+this.radius,this.radius,0,Math.PI*2,false);
context.closePath();
context.fill();
}
this.clear = function()
{
context.fillStyle = "#ffffff";
this.fill();
}
this.draw = function()
{
if (this.drawnPosition !== getCoordinates(this.x, this.y))
{
context.fillStyle = this.color;
// if commented, the circle will be drawn if it is in the same position
//this.drawnPosition = getCoordinates(this.x, this.y);
this.fill();
}
}
this.keepInBounds = function()
{
if (this.x < 0)
{
this.x = 0;
this.deltaX *= -1 * this.bounciness;
}
else if (this.x + this.diameter > canvas.width)
{
this.x = canvas.width - this.diameter;
this.deltaX *= -1 * this.bounciness;
}
if (this.y < 0)
{
this.y = 0;
this.deltaY *= -1 * this.bounciness;
}
else if (this.y+this.diameter > canvas.height)
{
this.y = canvas.height - this.diameter;
this.deltaY *= -1 * this.bounciness;
}
}
this.followMouse = function()
{
// deltaX/deltaY will currently cause the circles to "orbit" around the cursor forever unless it hits a wall
var centerX = Math.round(this.x + this.radius);
var centerY = Math.round(this.y + this.radius);
if (centerX < mouse.x)
{
// circle is to the left of the mouse, so move the circle to the right
this.deltaX += this.speed;
}
else if (centerX > mouse.x)
{
// circle is to the right of the mouse, so move the circle to the left
this.deltaX -= this.speed;
}
else
{
//this.deltaX = 0;
}
if (centerY < mouse.y)
{
// circle is above the mouse, so move the circle downwards
this.deltaY += this.speed;
}
else if (centerY > mouse.y)
{
// circle is under the mouse, so move the circle upwards
this.deltaY -= this.speed;
}
else
{
//this.deltaY = 0;
}
this.x += this.deltaX;
this.y += this.deltaY;
this.x = Math.round(this.x);
this.y = Math.round(this.y);
}
}
function getRandomDecimal(min, max)
{
return Math.random() * (max-min) + min;
}
function getRoundedNum(min, max)
{
return Math.round(getRandomDecimal(min, max));
}
function getRandomColor()
{
// array of three colors
var colors = [];
// go through loop and add three integers between 0 and 255 (min and max color values)
for (var i = 0; i < 3; i++)
{
colors[i] = getRoundedNum(0, 255);
}
// return rgb value (RED, GREEN, BLUE)
return "rgb(" + colors[0] + "," + colors[1] + ", " + colors[2] + ")";
}
function createCircle(i)
{
// diameter of circle
var minDiameter = 25;
var maxDiameter = 50;
// bounciness of circle (changes speed if it hits a wall)
var minBounciness = 0.2;
var maxBounciness = 0.65;
// speed of circle (how fast it moves)
var minSpeed = 0.3;
var maxSpeed = 0.45;
// getRoundedNum returns a random integer and getRandomDecimal returns a random decimal
var x = getRoundedNum(0, canvas.width);
var y = getRoundedNum(0, canvas.height);
var d = getRoundedNum(minDiameter, maxDiameter);
var c = getRandomColor();
var b = getRandomDecimal(minBounciness, maxBounciness);
var s = getRandomDecimal(minSpeed, maxSpeed);
// create the circle with x, y, diameter, bounciness, speed, and color
circles[i] = new Circle(x,y,d,b,s,c);
}
function makeCircles()
{
var maxCircles = getRoundedNum(2, 5);
for (var i = 0; i < maxCircles; i++)
{
createCircle(i);
}
}
function drawCircles()
{
var ii = 0;
for (var i = 0; ii < circles.length; i++)
{
if (circles[i])
{
circles[i].draw();
ii++;
}
}
}
function clearCircles()
{
var ii = 0;
for (var i = 0; ii < circles.length; i++)
{
if (circles[i])
{
circles[i].clear();
ii++;
}
}
}
function updateCircles()
{
var ii = 0;
for (var i = 0; ii < circles.length; i++)
{
if (circles[i])
{
circles[i].keepInBounds();
circles[i].followMouse();
ii++;
}
}
}
function update()
{
requestMyAnimationFrame(update,10);
updateCircles();
}
function draw()
{
requestMyAnimationFrame(draw,1000/60);
context.clearRect(0,0,canvas.width,canvas.height);
drawCircles();
}
window.addEventListener("load", function()
{
window.addEventListener("mousemove", function(e)
{
mouse.x = e.layerX || e.offsetX;
mouse.y = e.layerY || e.offsetY;
});
makeCircles();
update();
draw();
});

Categories