I now have a customized Shape, and this shape is controlled by one global variable. Thus I assume I just need to change this global variable due to frame.time, erase the old shape, and create the new one.
But however it seems not working. The following is the simplified code.
<script>
var toControlShape;
var myDrawFunction(context) {
// toControlShape will be used here.
}
window.onload = function() {
var stage = new Kinetic.Stage({...});
var layer = new Kinetic.Layer();
var myShape = new Kinetic.Shape({
drawFunc: myDrawFunction,
...
});
layer.add(myShape);
stage.add(layer);
var animation = new Kinetic.Animation(function(frame) {
toControlShape = someFunction(frame.time);
myShape.remove();
myShape = new Kinetic.Shape({
drawFunc: myDrawFunction,
...
});
layer.add(myShape);
}, layer);
animation.start();
};
</script>
The shape displays properly as its initial state. But there is no animation.
I am pretty new to Javascript and HTML5. So there might be a lot of anti-patterns in this code. Pointing out them to me is also appreciated.
The complete code is here on jsFiddle
I think your doing it wrong. From the docs:
"Kinetic.Animation which modifies the shape's position with each
animation frame."
So i think you should stop remove the shape and instead just update the shape. Then it should probably the animation part work for you.
html ---
<!DOCTYPE html>
<html>
<head>
<title>Strath</title>
<meta http-equiv="X-UA-Compatible" content="IE=9" />
<script type="text/javascript" src="kinetic-v4.1.2.min.js"></script>
<script src="fiddle.js"></script>
<style type="text/css">
</style>
</head>
<body>
<div id="PureHTML5"></div>
</body>
</html>
js ---
var segsToSkip = 0;
var triangle;
window.onload = function() {
var stage = new Kinetic.Stage({
container: "PureHTML5",
width: 300,
height: 300
});
var layer = new Kinetic.Layer();
/*
* create a triangle shape by defining a
* drawing function which draws a triangle
*/
var box = new Kinetic.Shape({
drawFunc: myDrawFunction,
stroke: "white",
strokeWidth: 8
});
var bbox= new Kinetic.Rect({
x:10,
y:10,
width:10,
height:10,
fill: 'green'
});
triangle = new Kinetic.Shape({
drawFunc: function(context) {
context.beginPath();
context.moveTo(10, 10);
context.lineTo(20, 80);
context.quadraticCurveTo(30, 10, 26, 17);
context.closePath();
context.stroke();
},
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 4
});
layer.add(bbox);
// add the triangle shape to the layer
layer.add(box);
layer.add(triangle);
// add the layer to the stage
stage.add(layer);
var animation = new Kinetic.Animation(function(frame) {
console.log(frame.time);
var newSegsToSkip = Math.round(frame.time / 200);
triangle.setX(newSegsToSkip);
triangle.setDrawFunc(function(context) {
context.beginPath();
context.moveTo(newSegsToSkip, 10);
context.lineTo(newSegsToSkip+20, 80);
context.quadraticCurveTo(30, 10, 26, 17);
context.closePath();
context.stroke();
});
}, layer);
var animation2 = new Kinetic.Animation(function(frame) {
var newSegsToSkip = Math.round(frame.time / 200);
if (newSegsToSkip == segsToSkip) return;
else {
segsToSkip = newSegsToSkip;
box.remove();
box = new Kinetic.Shape({
drawFunc: myDrawFunction,
stroke: "black",
strokeWidth: 8
});
layer.add(box);
}
}, layer);
animation.start();
animation2.start();
};
var myDrawFunction = function(context) {
var x = 50;
var y = 50;
var width = 200;
var height = 200;
var radius = 20;
var dashedLength = 6;
var segsToDraw = 58;
context.beginPath();
context.drawDashedBox(x, y, width, height, radius, dashedLength, segsToSkip, segsToDraw);
context.closePath();
// context.stroke();
//this.fill(context);
this.stroke(context);
}
var CP = window.CanvasRenderingContext2D && CanvasRenderingContext2D.prototype;
CP.drawDashedBox = function(x, y, width, height, radius, dashedLength, segsToSkip, segsToDraw) {
// init
var env = getStratEnvForRCBox(x, y, width, height, radius, dashedLength, segsToSkip, segsToDraw);
// 'right'->'upper-right'->'down'->'bottom-right'->'left'->'bottom-left'->'up'->'upper-left'->'right'->...
while (env.segsToDraw > 0) {
console.log('drawing direction: ', env.direction, 'segsToDraw:', env.segsToDraw);
env.putToConsole();
if (env.direction == 'right') {
env = drawDashedLineNew(env, x + width - radius, y + height, dashedLength, this);
env.direction = 'bottom-right';
}
else if (env.direction == 'upper-right') {
env = drawDashedArcNew(env, x + width - radius, y + radius, radius, 0, Math.PI / 2, dashedLength, this);
env.direction = 'left';
}
else if (env.direction == 'down') {
env = drawDashedLineNew(env, x, y + height - radius, dashedLength, this);
env.direction = 'bottom-left';
}
else if (env.direction == 'bottom-right') {
env = drawDashedArcNew(env, x + width - radius, y + height - radius, radius, 3 * Math.PI / 2, 2 * Math.PI, dashedLength, this);
env.direction = 'up';
}
else if (env.direction == 'left') {
env = drawDashedLineNew(env, x + radius, y, dashedLength, this);
env.direction = 'upper-left';
}
else if (env.direction == 'bottom-left') {
env = drawDashedArcNew(env, x + radius, y + height - radius, radius, Math.PI, 3 * Math.PI / 2, dashedLength, this);
env.direction = 'right';
}
else if (env.direction == 'up') {
env = drawDashedLineNew(env, x + width, y + radius, dashedLength, this);
env.direction = 'upper-right';
}
else if (env.direction == 'upper-left') {
env = drawDashedArcNew(env, x + radius, y + radius, radius, Math.PI / 2, Math.PI, dashedLength, this);
env.direction = 'down';
}
}
}
function getStratEnvForRCBox(x, y, width, height, radius, dashLength, segsToSkip, segsToDraw) {
var direction = 'right';
var startX, startY;
if (direction == 'down') {
startX = x; startY = y + radius;
} else if (direction == 'right') {
startX = x + radius; startY = y + height;
} else if (direction == 'up') {
startX = x + width; startY = y + height - radius;
} else if (direction == 'left') {
startX = x + width - radius; startY = y;
}
var env = new Environment(startX, startY, 'gap', 0, direction, segsToSkip, segsToDraw);
return env;
}
function drawDashedLineNew(env, endX, endY, dashedLength, context) {
var dx = (endX - env.x), dy = (endY - env.y);
var angle = Math.atan2(dy, dx);
console.log('drawing line: angle =', angle, ' , ', env.gapOrDash, ' =', env.remainingLengthFromLastDraw);
var fromX = env.x, fromY = env.y;
// deal with remining
// we start loop from a fresh dash
if (env.gapOrDash == 'dash') {
// check if we need to skip
if (env.segsToSkip > 0) {
env.segsToSkip --;
} else {
context.moveTo(env.x, env.y);
context.lineTo(env.x + env.remainingLengthFromLastDraw * Math.cos(angle), env.y + env.remainingLengthFromLastDraw * Math.sin(angle));
// check if we quit
env.segsToDraw --;
if (env.segsToDraw == 0) return env;
}
// a full gap
fromX = env.x + (env.remainingLengthFromLastDraw + dashedLength) * Math.cos(angle);
fromY = env.y + (env.remainingLengthFromLastDraw + dashedLength) * Math.sin(angle);
} else if (env.gapOrDash == 'gap') {
fromX = env.x + env.remainingLengthFromLastDraw * Math.cos(angle);
fromY = env.y + env.remainingLengthFromLastDraw * Math.sin(angle);
}
var length = (endX - fromX) / Math.cos(angle);
if (endX - fromX == 0) length = Math.abs(endY - fromY);
var n = length / dashedLength;
var draw = true;
var x = fromX, y = fromY;
context.moveTo(x, y);
for (var i = 0; i < n; i++) {
x += dashedLength * Math.cos(angle);
y += dashedLength * Math.sin(angle);
if (draw) {
// check if we need to skip
if (env.segsToSkip > 0) {
env.segsToSkip --;
} else {
context.lineTo(x,y);
// check if we quit
env.segsToDraw --;
if (env.segsToDraw == 0) return env;
}
} else context.moveTo(x, y);
draw = !draw;
}
// deal with remaining
if (draw) {
// check if we need to skip
if (env.segsToSkip > 0) {
env.segsToSkip --;
} else
context.lineTo(endX, endY);
}
env.x = endX;
env.y = endY;
draw ? env.gapOrDash = 'dash' : env.gapOrDash = 'gap';
env.remainingLengthFromLastDraw = dashedLength - (endX - x) / Math.cos(angle);
return env;
}
function drawDashedArcNew(env, x, y, radius, startAngle, endAngle, dashedLength, context) {
var points = [];
var n = radius * Math.PI * 2/ dashedLength;
var stepAngle = Math.PI * 2 / n;
// deal with remaining
var angle = Math.asin(env.remainingLengthFromLastDraw / 2 / radius) * 2;
if (env.gapOrDash == 'dash') {
var angle = Math.asin(env.remainingLengthFromLastDraw / 2 / radius) * 2;
points.push({
x : (Math.cos(startAngle) * radius) + x,
y : - (Math.sin(startAngle) * radius) + y,
ex : (Math.cos(startAngle + angle) * radius) + x,
ey : - (Math.sin(startAngle + angle) * radius) + y
});
startAngle += stepAngle + angle;
} else {
startAngle += angle;
}
var draw = true;
while(startAngle + stepAngle <= endAngle) {
if (draw) {
points.push({
x : (Math.cos(startAngle) * radius) + x,
y : - (Math.sin(startAngle) * radius) + y,
ex : (Math.cos(startAngle + stepAngle) * radius) + x,
ey : - (Math.sin(startAngle + stepAngle) * radius) + y
});
}
startAngle += stepAngle;
draw = !draw;
}
// deal with the remaining
var endX = (Math.cos(endAngle) * radius) + x;
var endY = - (Math.sin(endAngle) * radius) + y;
//console.log('drawing arc: end-x:', endX, ',end-y:', endY);
if (draw) {
points.push({
x : (Math.cos(startAngle) * radius) + x,
y : - (Math.sin(startAngle) * radius) + y,
ex : endX,
ey : endY
});
}
env.x = endX;
env.y = endY;
draw ? env.gapOrDash = 'dash' : env.gapOrDash = 'gap';
env.remainingLengthFromLastDraw = dashedLength - radius * Math.sin( (endAngle - startAngle) / 2) * 2;
for(p = 0; p < points.length; p++){
//console.log('draw arc seg: from(', points[p].x, ',', points[p].y, ') to (', points[p].ex, ',', points[p].ey, ')');
// check if we need to skip
if (env.segsToSkip > 0) {
env.segsToSkip --;
} else {
context.moveTo(points[p].x, points[p].y);
context.lineTo(points[p].ex, points[p].ey);
// check if we quit
env.segsToDraw --;
if (env.segsToDraw == 0) return env;
}
}
return env;
}
function Environment(x, y, gapOrDash, remainingLengthFromLastDraw, direction, segsToSkip, segsToDraw) {
this.x = x;
this.y = y;
this.gapOrDash = gapOrDash;
this.remainingLengthFromLastDraw = remainingLengthFromLastDraw;
this.direction = direction;
this.segsToSkip = segsToSkip;
this.segsToDraw = segsToDraw;
}
Environment.prototype.putToConsole = function() {
//console.log('Environment:');
//console.log('x:', this.x, ',y:', this.y, 'direction:', this.direction);
//console.log('toSkip:', this.segsToSkip, 'toDraw:', this.segsToDraw);
}
Related
I'm writing a simple raycast in htm5l and the main issue with the raycast is that the line goes off in a direction of probably infinite length.. I would like to limit that length to a specific radius but I'm not having any luck. If someone could guide me that'd be great.
window.addEventListener('DOMContentLoaded', (event) => {
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');
let coord = {
x: 0,
y: 0
}
function line(x1, y1, x2, y2) {
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
}
class Vector {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
class Boundery {
constructor(x1, y1, x2, y2) {
this.a = new Vector(x1, y1);
this.b = new Vector(x2, y2);
}
show() {
ctx.strokeStyle = '#000000'
line(this.a.x, this.a.y, this.b.x, this.b.y);
}
}
class Ray {
constructor(x, y) {
this.pos = new Vector(x, y);
this.dir = new Vector(Math.cos(1), Math.sin(0));
}
show() {
ctx.strokeStyle = '#000000';
ctx.beginPath();
ctx.ellipse(this.pos.x, this.pos.y, 5, 5, 0, 0, Math.PI * 2);
ctx.stroke();
}
cast(wall) {
let x1 = wall.a.x;
let y1 = wall.a.y;
let x2 = wall.b.x;
let y2 = wall.b.y;
let x3 = this.pos.x;
let y3 = this.pos.y;
let x4 = this.pos.x + this.dir.x;
let y4 = this.pos.y + this.dir.y;
let den = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
if (den == 0) {
return;
}
let t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / den;
let u = -((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / den;
if (t > 0 && t < 1 && u > 0) {
let point = new Vector(x1 + t * (x2 - x1), y1 + t * (y2 - y1));
return point;
} else {
return;
}
}
}
let wall = new Boundery(300, 100, 300, 300);
let ray = new Ray(100, 200);
function tick(timestamp) {
ctx.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight);
wall.show();
ray.show();
let r = ray.cast(wall);
if (r) {
ctx.fillStyle = 'red';
ctx.ellipse(r.x, r.y, 10, 10, 0, 0, 2 * Math.PI);
ctx.fill();
}
requestAnimationFrame(tick);
}
requestAnimationFrame(tick);
});
<canvas id="canvas" width="2000" height="1000"></canvas>
So the ray currently fires to the right (1,0) of the small red circle but it's distance just goes on forever so I'm trying to limit that distance. In the example the position the ray hits the wall is the red circle that's drawn on the wall
Ray length and direction
Modify the ray to have the start position, a direction, and a length. as follows
class Ray {
constructor(pos, direction, length) {
this.pos = pos;
this.dir = direction;
this.length = length;
}
You can get the end point with
get end() {
return new Vector(
this.pos.x + Math.cos(this.dir) * this.length,
this.pos.y + Math.sin(this.dir) * this.length
);
}
When you cast the ray you convert the ray to a line segment and then check against any wall segments for the intercept. Only points withing the length of the ray will be found.
Example.
The example uses a ray to check against many walls. It finds the closest intercept to the to the start of the ray and within the rays length.
Note (FOR example only) the walls are random so if a wall gets to close to the ray, click the canvas to randomize the walls.
I have re-organised it somewhat with Vector as a point, Line (2 vectors) as a line segment with an intercept function (also represents a wall), And Ray as a vector, direction and length. The Ray.cast finds the intercept of an array of line, returning undefined if no intercept found.
const ctx = canvas.getContext("2d");
Math.TAU = Math.PI * 2;
Math.rand = (min, max) => Math.random() * (max - min) + min;
var myRay;
const WALL_COUNT = 30;
const WALL_STYLE = {radius: 0, lineWidth: 1, strokeStyle: "#000"};
const RAY_STYLE_A = {radius: 2, lineWidth: 1, strokeStyle: "#0FF", fillStyle: "#F00"};
const RAY_STYLE_B = {radius: 5, lineWidth: 3, strokeStyle: "#00F", fillStyle: "#F00"};
const RAY_INTERCEPT_STYLE = {radius: 5, lineWidth: 1, strokeStyle: "#000", fillStyle: "#FF0"};
const ROTATE_RAY = 10; // seconds per rotation
const walls = [];
setTimeout(init, 0);
canvas.addEventListener("click",init);
class Vector {
constructor(x, y) {
this.x = x;
this.y = y;
}
draw(ctx, {radius = 5, lineWidth = 2, strokeStyle = "#000", fillStyle = "#F00"} = {}) {
ctx.strokeStyle = strokeStyle;
ctx.fillStyle = fillStyle;
ctx.lineWidth = lineWidth;
ctx.beginPath();
ctx.arc(this.x, this.y, radius, 0, Math.TAU);
ctx.fill();
ctx.stroke();
}
}
class Line {
constructor(start, end) {
this.start = start;
this.end = end;
}
draw(ctx, {radius = 5, lineWidth = 2, strokeStyle = "#000", fillStyle = "#F00"} = {}) {
if (radius > 0) {
this.start.draw(ctx, {radius, lineWidth, strokeStyle, fillStyle});
this.end.draw(ctx, {radius, lineWidth, strokeStyle, fillStyle});
}
ctx.strokeStyle = strokeStyle;
ctx.lineWidth = lineWidth;
ctx.beginPath();
ctx.moveTo(this.start.x, this.start.y);
ctx.lineTo(this.end.x, this.end.y);
ctx.stroke();
}
intercept(line) {
var x1, y1, x2, y2, x3, y3, c, u;
x1 = line.end.x - line.start.x;
y1 = line.end.y - line.start.y;
x2 = this.end.x - this.start.x;
y2 = this.end.y - this.start.y;
c = x1 * y2 - y1 * x2;
if (c) {
x3 = line.start.x - this.start.x;
y3 = line.start.y - this.start.y;
u = (x1 * y3 - y1 * x3) / c;
if (u >= 0 && u <= 1) {
u = (x2 * y3 - y2 *x3) / c;
if (u >= 0 && u <= 1) { return [u, line.start.x + x1 * u, line.start.y + y1 * u] }
}
}
}
}
class Ray {
constructor(pos, direction, length) {
this.pos = pos;
this.dir = direction;
this.length = length;
}
draw(ctx, {radius = 5, lineWidth = 2, strokeStyle = "#000", fillStyle = "#F00"} = {}) {
this.pos.draw(ctx, {radius, lineWidth, strokeStyle, fillStyle});
ctx.strokeStyle = strokeStyle;
ctx.lineWidth = lineWidth;
ctx.beginPath();
ctx.moveTo(this.pos.x, this.pos.y);
ctx.lineTo(
this.pos.x + Math.cos(this.dir) * this.length,
this.pos.y + Math.sin(this.dir) * this.length
);
ctx.stroke();
}
get end() {
return new Vector(
this.pos.x + Math.cos(this.dir) * this.length,
this.pos.y + Math.sin(this.dir) * this.length
);
}
get line() {
return new Line(this.pos, this.end);
}
cast(lines) {
const tLine = this.line;
var minDist = 1, point;
for (const line of lines) {
const result = line.intercept(tLine);
if (result) {
const [u, x, y] = result;
if (u <= minDist) {
minDist = u;
if (!point) { point = new Vector(x, y) }
else {
point.x = x;
point.y = y;
}
point.u = u;
}
}
}
return point;
}
}
function init() {
walls.length = 0;
for (let i = 0; i < WALL_COUNT / 2; i++) {
walls.push(new Ray(
new Vector(Math.rand(0, canvas.width * 0.4), Math.rand(0, canvas.height)),
(Math.rand(0, 8) | 0) / 4 * Math.PI, 100
).line);
walls.push(new Ray(
new Vector(Math.rand(canvas.width * 0.6, canvas.width), Math.rand(0, canvas.height)),
(Math.rand(0, 8) | 0) / 4 * Math.PI, 100
).line);
}
if(!myRay) {
myRay = new Ray(new Vector(canvas.width / 2, canvas.height / 2), 0, Math.max(canvas.width, canvas.height) * 0.485);
requestAnimationFrame(mainLoop);
}
}
function mainLoop(time) {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
myRay.dir = (time / (ROTATE_RAY * 1000)) * Math.TAU;
const point = myRay.cast(walls)
myRay.draw(ctx, RAY_STYLE_A);
for(const w of walls) { w.draw(ctx, WALL_STYLE) }
if (point) {
const len = myRay.length;
myRay.length = point.u * len;
myRay.draw(ctx, RAY_STYLE_B);
myRay.length = len;
point.draw(ctx, RAY_INTERCEPT_STYLE);
}
requestAnimationFrame(mainLoop);
}
#canvas {
border: 2px solid black;
}
<canvas id="canvas" width="500" height="500"> </canvas>
I'm working on a custom button animation for my Wix website it is written in JavaScript and I get this error when I plug the code into an onclick event:
"cannot read property 'requestAnimationFrame' of undefined"
I've tried removing the return and I think the problem has to do with some sort of iframe issue because the original code was built to run in an html <iframe> element but that dident end up working out.
If someone could rewrite this code so that it dosent give this error that would be super helpful.
export function button7_click(event) {
$w.window.requestAnimFrame = (function() {
return $w.window.requestAnimationFrame ||
$w.window.webkitRequestAnimationFrame ||
$w.window.mozRequestAnimationFrame ||
$w.window.oRequestAnimationFrame ||
$w.window.msRequestAnimationFrame ||
function(callback) {
$w.window.setTimeout(callback, 1000 / 60);
};
})();
Math.randMinMax = function(min, max, round) {
var val = min + (Math.random() * (max - min));
if (round) val = Math.round(val);
return val;
};
Math.TO_RAD = Math.PI / 180;
Math.getAngle = function(x1, y1, x2, y2) {
var dx = x1 - x2,
dy = y1 - y2;
return Math.atan2(dy, dx);
};
Math.getDistance = function(x1, y1, x2, y2) {
var xs = x2 - x1,
ys = y2 - y1;
xs *= xs;
ys *= ys;
return Math.sqrt(xs + ys);
};
var FX = {};
(function() {
var canvas = $w.document.getElementById('myCanvas'),
ctx = canvas.getContext('2d'),
lastUpdate = new Date(),
mouseUpdate = new Date(),
lastMouse = [],
width, height;
FX.particles = [];
setFullscreen();
$w.document.getElementById('button').addEventListener('mousedown', buttonEffect);
function buttonEffect() {
var button = $w.document.getElementById('button'),
height = button.offsetHeight,
left = button.offsetLeft,
top = button.offsetTop,
width = button.offsetWidth,
x, y, degree;
for (var i = 0; i < 40; i = i + 1) {
if (Math.random() < 0.5) {
y = Math.randMinMax(top, top + height);
if (Math.random() < 0.5) {
x = left;
degree = Math.randMinMax(-45, 45);
} else {
x = left + width;
degree = Math.randMinMax(135, 225);
}
} else {
x = Math.randMinMax(left, left + width);
if (Math.random() < 0.5) {
y = top;
degree = Math.randMinMax(45, 135);
} else {
y = top + height;
degree = Math.randMinMax(-135, -45);
}
}
createParticle({
x: x,
y: y,
degree: degree,
speed: Math.randMinMax(100, 150),
vs: Math.randMinMax(-4, -1)
});
}
}
$w.window.setTimeout(buttonEffect, 100);
loop();
$w.window.addEventListener('resize', setFullscreen);
function createParticle(args) {
var options = {
x: width / 2,
y: height / 2,
color: 'hsla(' + Math.randMinMax(160, 290) + ', 100%, 50%, ' + (Math.random().toFixed(2)) + ')',
degree: Math.randMinMax(0, 360),
speed: Math.randMinMax(300, 350),
vd: Math.randMinMax(-90, 90),
vs: Math.randMinMax(-8, -5)
};
for ($w.key in args) {
options[$w.key] = args[$w.key];
}
FX.particles.push(options);
}
function loop() {
var thisUpdate = new Date(),
delta = (lastUpdate - thisUpdate) / 1000,
amount = FX.particles.length,
size = 2,
i = 0,
p;
ctx.fillStyle = 'rgba(15,15,15,0.25)';
ctx.fillRect(0, 0, width, height);
ctx.globalCompositeStyle = 'lighter';
for (; i < amount; i = i + 1) {
p = FX.particles[i];
p.degree += (p.vd * delta);
p.speed += (p.vs); // * delta);
if (p.speed < 0) continue;
p.x += Math.cos(p.degree * Math.TO_RAD) * (p.speed * delta);
p.y += Math.sin(p.degree * Math.TO_RAD) * (p.speed * delta);
ctx.save();
ctx.translate(p.x, p.y);
ctx.rotate(p.degree * Math.TO_RAD);
ctx.fillStyle = p.color;
ctx.fillRect(-size, -size, size * 2, size * 2);
ctx.restore();
}
lastUpdate = thisUpdate;
$w.requestAnimFrame(loop);
}
function setFullscreen() {
width = canvas.width = $w.window.innerWidth;
height = canvas.height = $w.window.innerHeight;
};
})();
}
The button once clicked should run the JavaScript code and play the animation. Thanks for your help.
i'm trying to use this Fireworks javascript. But when i write some text in the middle of the HTML page, the fireworks will not go over it and is limited by the text position...how can i override this and keep the fireworks go up to the top of the page ?
i tryed to fix the SCREEN_WIDTH and SCREEN_HEIGHT position but it doesn't work...
var SCREEN_WIDTH = window.innerWidth,
SCREEN_HEIGHT = window.innerHeight,
mousePos = {
x: 400,
y: 300
},
// create canvas
canvas = document.createElement('canvas'),
context = canvas.getContext('2d'),
particles = [],
rockets = [],
MAX_PARTICLES = 400,
colorCode = 0;
// init
$(document).ready(function() {
document.body.appendChild(canvas);
canvas.width = SCREEN_WIDTH;
canvas.height = SCREEN_HEIGHT;
setInterval(launch, 800);
setInterval(loop, 1000 / 50);
});
// update mouse position
$(document).mousemove(function(e) {
e.preventDefault();
mousePos = {
x: e.clientX,
y: e.clientY
};
});
// launch more rockets!!!
$(document).mousedown(function(e) {
for (var i = 0; i < 5; i++) {
launchFrom(Math.random() * SCREEN_WIDTH * 2 / 3 + SCREEN_WIDTH / 6);
}
});
function launch() {
launchFrom(mousePos.x);
}
function launchFrom(x) {
if (rockets.length < 10) {
var rocket = new Rocket(x);
rocket.explosionColor = Math.floor(Math.random() * 360 / 10) * 10;
rocket.vel.y = Math.random() * -3 - 4;
rocket.vel.x = Math.random() * 6 - 3;
rocket.size = 8;
rocket.shrink = 0.999;
rocket.gravity = 0.01;
rockets.push(rocket);
}
}
function loop() {
// update screen size
if (SCREEN_WIDTH != window.innerWidth) {
canvas.width = SCREEN_WIDTH = window.innerWidth;
}
if (SCREEN_HEIGHT != window.innerHeight) {
canvas.height = SCREEN_HEIGHT = window.innerHeight;
}
// clear canvas
context.fillStyle = "rgba(0, 0, 0, 0.05)";
context.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
var existingRockets = [];
for (var i = 0; i < rockets.length; i++) {
// update and render
rockets[i].update();
rockets[i].render(context);
// calculate distance with Pythagoras
var distance = Math.sqrt(Math.pow(mousePos.x - rockets[i].pos.x, 2) + Math.pow(mousePos.y - rockets[i].pos.y, 2));
// random chance of 1% if rockets is above the middle
var randomChance = rockets[i].pos.y < (SCREEN_HEIGHT * 2 / 3) ? (Math.random() * 100 <= 1) : false;
/* Explosion rules
- 80% of screen
- going down
- close to the mouse
- 1% chance of random explosion
*/
if (rockets[i].pos.y < SCREEN_HEIGHT / 5 || rockets[i].vel.y >= 0 || distance < 50 || randomChance) {
rockets[i].explode();
} else {
existingRockets.push(rockets[i]);
}
}
rockets = existingRockets;
var existingParticles = [];
for (var i = 0; i < particles.length; i++) {
particles[i].update();
// render and save particles that can be rendered
if (particles[i].exists()) {
particles[i].render(context);
existingParticles.push(particles[i]);
}
}
// update array with existing particles - old particles should be garbage collected
particles = existingParticles;
while (particles.length > MAX_PARTICLES) {
particles.shift();
}
}
function Particle(pos) {
this.pos = {
x: pos ? pos.x : 0,
y: pos ? pos.y : 0
};
this.vel = {
x: 0,
y: 0
};
this.shrink = .97;
this.size = 2;
this.resistance = 1;
this.gravity = 0;
this.flick = false;
this.alpha = 1;
this.fade = 0;
this.color = 0;
}
Particle.prototype.update = function() {
// apply resistance
this.vel.x *= this.resistance;
this.vel.y *= this.resistance;
// gravity down
this.vel.y += this.gravity;
// update position based on speed
this.pos.x += this.vel.x;
this.pos.y += this.vel.y;
// shrink
this.size *= this.shrink;
// fade out
this.alpha -= this.fade;
};
Particle.prototype.render = function(c) {
if (!this.exists()) {
return;
}
c.save();
c.globalCompositeOperation = 'lighter';
var x = this.pos.x,
y = this.pos.y,
r = this.size / 2;
var gradient = c.createRadialGradient(x, y, 0.1, x, y, r);
gradient.addColorStop(0.1, "rgba(255,255,255," + this.alpha + ")");
gradient.addColorStop(0.8, "hsla(" + this.color + ", 100%, 50%, " + this.alpha + ")");
gradient.addColorStop(1, "hsla(" + this.color + ", 100%, 50%, 0.1)");
c.fillStyle = gradient;
c.beginPath();
c.arc(this.pos.x, this.pos.y, this.flick ? Math.random() * this.size : this.size, 0, Math.PI * 2, true);
c.closePath();
c.fill();
c.restore();
};
Particle.prototype.exists = function() {
return this.alpha >= 0.1 && this.size >= 1;
};
function Rocket(x) {
Particle.apply(this, [{
x: x,
y: SCREEN_HEIGHT}]);
this.explosionColor = 0;
}
Rocket.prototype = new Particle();
Rocket.prototype.constructor = Rocket;
Rocket.prototype.explode = function() {
var count = Math.random() * 10 + 80;
for (var i = 0; i < count; i++) {
var particle = new Particle(this.pos);
var angle = Math.random() * Math.PI * 2;
// emulate 3D effect by using cosine and put more particles in the middle
var speed = Math.cos(Math.random() * Math.PI / 2) * 15;
particle.vel.x = Math.cos(angle) * speed;
particle.vel.y = Math.sin(angle) * speed;
particle.size = 10;
particle.gravity = 0.2;
particle.resistance = 0.92;
particle.shrink = Math.random() * 0.05 + 0.93;
particle.flick = true;
particle.color = this.explosionColor;
particles.push(particle);
}
};
Rocket.prototype.render = function(c) {
if (!this.exists()) {
return;
}
c.save();
c.globalCompositeOperation = 'lighter';
var x = this.pos.x,
y = this.pos.y,
r = this.size / 2;
var gradient = c.createRadialGradient(x, y, 0.1, x, y, r);
gradient.addColorStop(0.1, "rgba(255, 255, 255 ," + this.alpha + ")");
gradient.addColorStop(1, "rgba(0, 0, 0, " + this.alpha + ")");
c.fillStyle = gradient;
c.beginPath();
c.arc(this.pos.x, this.pos.y, this.flick ? Math.random() * this.size / 2 + this.size / 2 : this.size, 0, Math.PI * 2, true);
c.closePath();
c.fill();
c.restore();
};
body {
background-color: #000000;
margin: 0px;
overflow: hidden;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Fireworks!</title>
</head>
<body/>
</html>
The problem is the canvas used by the script is positioned relative by default. To make it always visible completely on screen we have to make it fixed and set the top and left CSS values to 0.
Now because its fixed the canvas renders on top of everything. To get it in the background set z-index to -1.
All additions together:
canvas.style.position="fixed";
canvas.style.top="0";
canvas.style.left="0";
canvas.style.zIndex="-1";
Complete Source:
var SCREEN_WIDTH = window.innerWidth,
SCREEN_HEIGHT = window.innerHeight,
mousePos = {
x: 400,
y: 300
},
// create canvas
canvas = document.createElement('canvas'),
context = canvas.getContext('2d'),
particles = [],
rockets = [],
MAX_PARTICLES = 400,
colorCode = 0;
// init
$(document).ready(function() {
document.body.appendChild(canvas);
canvas.width = SCREEN_WIDTH;
canvas.height = SCREEN_HEIGHT;
canvas.style.position = "fixed";
canvas.style.top = "0";
canvas.style.left = "0";
canvas.style.zIndex = "-1";
setInterval(launch, 800);
setInterval(loop, 1000 / 50);
});
// update mouse position
$(document).mousemove(function(e) {
e.preventDefault();
mousePos = {
x: e.clientX,
y: e.clientY
};
});
// launch more rockets!!!
$(document).mousedown(function(e) {
for (var i = 0; i < 5; i++) {
launchFrom(Math.random() * SCREEN_WIDTH * 2 / 3 + SCREEN_WIDTH / 6);
}
});
function launch() {
launchFrom(mousePos.x);
}
function launchFrom(x) {
if (rockets.length < 10) {
var rocket = new Rocket(x);
rocket.explosionColor = Math.floor(Math.random() * 360 / 10) * 10;
rocket.vel.y = Math.random() * -3 - 4;
rocket.vel.x = Math.random() * 6 - 3;
rocket.size = 8;
rocket.shrink = 0.999;
rocket.gravity = 0.01;
rockets.push(rocket);
}
}
function loop() {
// update screen size
if (SCREEN_WIDTH != window.innerWidth) {
canvas.width = SCREEN_WIDTH = window.innerWidth;
}
if (SCREEN_HEIGHT != window.innerHeight) {
canvas.height = SCREEN_HEIGHT = window.innerHeight;
}
// clear canvas
context.fillStyle = "rgba(0, 0, 0, 0.05)";
context.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
var existingRockets = [];
for (var i = 0; i < rockets.length; i++) {
// update and render
rockets[i].update();
rockets[i].render(context);
// calculate distance with Pythagoras
var distance = Math.sqrt(Math.pow(mousePos.x - rockets[i].pos.x, 2) + Math.pow(mousePos.y - rockets[i].pos.y, 2));
// random chance of 1% if rockets is above the middle
var randomChance = rockets[i].pos.y < (SCREEN_HEIGHT * 2 / 3) ? (Math.random() * 100 <= 1) : false;
/* Explosion rules
- 80% of screen
- going down
- close to the mouse
- 1% chance of random explosion
*/
if (rockets[i].pos.y < SCREEN_HEIGHT / 5 || rockets[i].vel.y >= 0 || distance < 50 || randomChance) {
rockets[i].explode();
} else {
existingRockets.push(rockets[i]);
}
}
rockets = existingRockets;
var existingParticles = [];
for (var i = 0; i < particles.length; i++) {
particles[i].update();
// render and save particles that can be rendered
if (particles[i].exists()) {
particles[i].render(context);
existingParticles.push(particles[i]);
}
}
// update array with existing particles - old particles should be garbage collected
particles = existingParticles;
while (particles.length > MAX_PARTICLES) {
particles.shift();
}
}
function Particle(pos) {
this.pos = {
x: pos ? pos.x : 0,
y: pos ? pos.y : 0
};
this.vel = {
x: 0,
y: 0
};
this.shrink = .97;
this.size = 2;
this.resistance = 1;
this.gravity = 0;
this.flick = false;
this.alpha = 1;
this.fade = 0;
this.color = 0;
}
Particle.prototype.update = function() {
// apply resistance
this.vel.x *= this.resistance;
this.vel.y *= this.resistance;
// gravity down
this.vel.y += this.gravity;
// update position based on speed
this.pos.x += this.vel.x;
this.pos.y += this.vel.y;
// shrink
this.size *= this.shrink;
// fade out
this.alpha -= this.fade;
};
Particle.prototype.render = function(c) {
if (!this.exists()) {
return;
}
c.save();
c.globalCompositeOperation = 'lighter';
var x = this.pos.x,
y = this.pos.y,
r = this.size / 2;
var gradient = c.createRadialGradient(x, y, 0.1, x, y, r);
gradient.addColorStop(0.1, "rgba(255,255,255," + this.alpha + ")");
gradient.addColorStop(0.8, "hsla(" + this.color + ", 100%, 50%, " + this.alpha + ")");
gradient.addColorStop(1, "hsla(" + this.color + ", 100%, 50%, 0.1)");
c.fillStyle = gradient;
c.beginPath();
c.arc(this.pos.x, this.pos.y, this.flick ? Math.random() * this.size : this.size, 0, Math.PI * 2, true);
c.closePath();
c.fill();
c.restore();
};
Particle.prototype.exists = function() {
return this.alpha >= 0.1 && this.size >= 1;
};
function Rocket(x) {
Particle.apply(this, [{
x: x,
y: SCREEN_HEIGHT
}]);
this.explosionColor = 0;
}
Rocket.prototype = new Particle();
Rocket.prototype.constructor = Rocket;
Rocket.prototype.explode = function() {
var count = Math.random() * 10 + 80;
for (var i = 0; i < count; i++) {
var particle = new Particle(this.pos);
var angle = Math.random() * Math.PI * 2;
// emulate 3D effect by using cosine and put more particles in the middle
var speed = Math.cos(Math.random() * Math.PI / 2) * 15;
particle.vel.x = Math.cos(angle) * speed;
particle.vel.y = Math.sin(angle) * speed;
particle.size = 10;
particle.gravity = 0.2;
particle.resistance = 0.92;
particle.shrink = Math.random() * 0.05 + 0.93;
particle.flick = true;
particle.color = this.explosionColor;
particles.push(particle);
}
};
Rocket.prototype.render = function(c) {
if (!this.exists()) {
return;
}
c.save();
c.globalCompositeOperation = 'lighter';
var x = this.pos.x,
y = this.pos.y,
r = this.size / 2;
var gradient = c.createRadialGradient(x, y, 0.1, x, y, r);
gradient.addColorStop(0.1, "rgba(255, 255, 255 ," + this.alpha + ")");
gradient.addColorStop(1, "rgba(0, 0, 0, " + this.alpha + ")");
c.fillStyle = gradient;
c.beginPath();
c.arc(this.pos.x, this.pos.y, this.flick ? Math.random() * this.size / 2 + this.size / 2 : this.size, 0, Math.PI * 2, true);
c.closePath();
c.fill();
c.restore();
};
body {
background-color: #000000;
margin: 0px;
overflow: hidden;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Fireworks!</title>
</head>
<body>
<p>
test
</p>
<br />
<p>
test2
</p>
</body>
</html>
I am trying to create a blackhole simulation, here is the code, now the problem is that my code doesn't display anything, I tried a code beautifier, but it did not work, so what is wrong with this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title></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) {
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 Vector (0,0);
var forceDirection2 = Math.atan2(pos.y - 400, pos.x - 700);
var g = (pixel_G*pixel_M)/(distance*distance*1e3);
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 -= 0.50;
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() {
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 the problem as you can see, is that i can't make it display anything, so if someone can tell me why it'd be great
Ps: I think the problem comes from the update function but i am not sure
You had a ReferenceError: invalid assignment left-hand side on line 44:
Your code:
if (pos.x = 700 && pos.y = 400)
Just change this to:
if (pos.x == 700 && pos.y == 400){
That should work.
I'm trying to make a blackhole simulation, and although I'm almost done, I wish to make disappear the dots that are drawn on the blackhole progressively, here is my code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>test trou noir</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 && visible(circles[i])) {
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;
}
}
if (somethingMoved) {
drawEverything();
requestAnimationFrame(update);
} else {
ccolor -=10;
};
}
function visible(ball) {
return ball.position.x > ball.radius && ball.position.x < canvas.width - ball.radius &&
ball.position.y > ball.radius && ball.position.y < canvas.height - ball.radius;
}
function drawEverything() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
blackhole.draw(ctx);
for (var i = 0; i < circles.length; i++) {
if (visible(circles[i])) {
circles[i].draw(ctx);
}
}
}
function init() {
canvas = document.getElementById("space");
ctx = canvas.getContext('2d');
blackhole = new Ball(pixel_Rs, {
x: 700,
y: 400
}, "black");
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, 'rgba('+ccolor+','+ccolor+','+ccolor+',1)');
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) {
ctx.fillStyle = this.color;
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;
}
window.onload = init;
</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 variable called ccolor which is integrated in the rgba code, but I don't know why the colors don't tend to zero, so the circles that are inside the blackhole gradually disappear, if someone could lend me a hand it'd be great
In update, if any circles[i] is captured by the Blackhole you decrement its this.color. It might be easier if you change this.color to an integer that you use to create a fillStyle:
ctx.fillStyle='rgba(' + this.color + ',' + this.color + ',' + this.color + ',1)'.
Here's a quick demo:
View this demo Full Page or the black hole is off-screen
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 && visible(circles[i])) {
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 {
circles[i].color -= 0.50;
}
}
if (somethingMoved) {
drawEverything();
requestAnimationFrame(update);
};
}
function visible(ball) {
return ball.position.x > ball.radius && ball.position.x < canvas.width - ball.radius &&
ball.position.y > ball.radius && ball.position.y < canvas.height - ball.radius;
}
function drawEverything() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
blackhole.draw(ctx);
for (var i = 0; i < circles.length; i++) {
if (visible(circles[i])) {
circles[i].draw(ctx);
}
}
}
function init() {
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;
}
init();
body{ background-color: #021c36; margin: 0px; }
<canvas id="space" width=1400 height=800></canvas>
Simple solution:
In drawEverything() function move blackhole.draw(ctx) to be the last step
Not so simple: Use one of the many JS particle systems