paintForegroundBars(hours: Array<number>) {
let barColor = "#b3bec9";
let numBars = hours.length;
let barWidth = Math.floor((this.canvasWidth / numBars) - this.barsSpacing);
let maxBarHeight = this.canvasHeight - (this.timesHeight + (this.timesSpacing * 2));
let barLeft = 0 + (this.barsSpacing / 2);
this.canvasContext.fillStyle = barColor;
this.canvasContext.strokeStyle = barColor;
this.canvasContext.lineJoin = "round";
this.canvasContext.lineWidth = this.cornerRadius;
for (let i = 0; i < numBars; i++) {
let barHeight = Math.round(maxBarHeight * hours[i]);
if (barHeight > 0) {
let barTop = maxBarHeight - barHeight;
let roundedBarTop = barTop + (this.cornerRadius / 2);
let roundedBarLeft = barLeft + (this.cornerRadius / 2);
let roundedBarWidth = barWidth - this.cornerRadius;
let roundedBarHeight = barHeight - this.cornerRadius;
this.canvasContext.strokeRect(roundedBarLeft, roundedBarTop, roundedBarWidth, roundedBarHeight);
this.canvasContext.fillRect(roundedBarLeft, roundedBarTop, roundedBarWidth, roundedBarHeight);
}
barLeft = Math.floor(barLeft + barWidth) + (this.barsSpacing);
}
}
At the moment I am drawing the height of a bar chart with the below code:
this.canvasContext.strokeRect(roundedBarLeft, roundedBarTop, roundedBarWidth, roundedBarHeight);
this.canvasContext.fillRect(roundedBarLeft, roundedBarTop, roundedBarWidth, roundedBarHeight);
Instead of when this runs it just being a fixed height I want it to animate from 0 to the height that has been calculated in my JS. How do you go about doing this?
Many thanks
Below is a simple example of how this kind of animation works. The thing you are looking for is the easing value - once you have that, you are set! In this case I store the start time inside the start variable, and then you simply take the current time, remove the start time and divide it by the time you want to pass. This will give you a number between 0 and 1, and multiplying any other number by that will give you the number in the range 0 to n. If you want to add a base value to this, your general formula is basically this:
fromValue + (nowTime - sinceTime) / duration * (toValue - fromValue);
The reason the easing value is so important is that it allows tweening. For example, you can create a smooth curve by multiplying this easing value by itself:
var easing = (nowTime - sinceTime) / duration;
easing = easing * easing;
fromValue + easing * (toValue - fromValue);
Use a graphing application to learn more about these curves :)
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
var start = Date.now();
var duration = 5000;
var animationFrame = false;
canvas.width = 40;
canvas.height = 400;
function drawBar(){
var progress = (Date.now() - start) / duration;
if( progress >= 1 ){
// Final state before stopping any drawing function
ctx.clearRect( 0, 0, canvas.width, canvas.height );
ctx.fillRect( 0, 0, canvas.width, canvas.height );
window.cancelAnimationFrame( drawBar );
} else {
// In progress
ctx.clearRect( 0, 0, canvas.width, canvas.height );
ctx.fillRect( 0, 0, canvas.width, canvas.height * progress );
window.requestAnimationFrame( drawBar );
}
}
animationFrame = setInterval( drawBar, 1000 / 60 );
document.body.addEventListener('click', function( event ){
start = Date.now();
window.cancelAnimationFrame( drawBar );
window.requestAnimationFrame( drawBar );
});
<canvas></canvas>
Related
I have this portion of JavaScript code which draws a bar (for let's say a bar diagram):
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
canvas.width = window.innerHeight / 3 * 4;
canvas.height = window.innerHeight;
const xCharCnt = window.innerHeight / 3 * 4 / 48;
const yCharCnt = window.innerHeight / 34;
const foreground1 = "#AF5BEC";
let offset = 0;
const barHeight = 20;
const speed = 4;
const limit = yCharCnt * barHeight;
let requestId = window.requestAnimationFrame(render);
function render() {
requestId = window.requestAnimationFrame(render);
// Clear screen
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw bar
ctx.fillStyle = foreground1;
ctx.fillRect(xCharCnt * 7, yCharCnt * 28, xCharCnt, -offset);
offset = offset + speed;
// Cancel animation
if (offset >= limit) window.cancelAnimationFrame(requestId);
}
Questions:
a) Is it the right way to do an animation?
b) Does the animation run always with the same speed, regardless the resolution of the device it's running on?
It will be framerate dependent, so it will differ between devices.
The number of callbacks is usually 60 times per second, but will generally match the display refresh rate in most web browsers as per W3C recommendation. requestAnimationFrame() calls are paused in most browsers when running in background tabs or hidden s in order to improve performance and battery life.
https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
The above link also gives a suggestion on how you should code your animations so they do always run at the same speed. Translated to your code that would look like:
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
canvas.width = window.innerHeight / 3 * 4;
canvas.height = window.innerHeight;
const xCharCnt = window.innerHeight / 3 * 4 / 48;
const yCharCnt = window.innerHeight / 34;
const foreground1 = "#AF5BEC";
let offset = 0;
const barHeight = 20;
const speed = 4;
const limit = yCharCnt * barHeight;
let requestId = window.requestAnimationFrame(render);
let previousTimestamp;
function render(timestamp) {
if(previousTimestamp === undefined) {
previousTimestmap = timestamp;
}
const delta = timestamp - previousTimestamp;
offset = Math.min(offset + (speed * delta), limit);
// Clear screen
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw bar
ctx.fillStyle = foreground1;
ctx.fillRect(xCharCnt * 7, yCharCnt * 28, xCharCnt, -offset);
// Cancel animation
requestId = window.requestAnimationFrame(render);
if (offset >= limit) window.cancelAnimationFrame(requestId);
previousTimestmap = timestamp;
}
The project that i have put together here called Swellcloud. The animation was forked from here. This code connects to a wave buoy just off my local beach and the animation is relevant to the live conditions. If the 'swell' is high, I would like the peaks & troughs to be large, the swell data has a range of min 0.1m = smallest swell so low troughs & peaks in the animation. And maximum 10m large waves so large troughs & peaks... then the surf 'period' data which has a range of 0s to 20s would reflect the 'smoothness' of the animation, so high period nice straight lines on the animation, and low period would be choppy/ragged lines.
I have managed to get the data to 'speed' up the animation if the swell data is large but i cant control the height of the waves on the animation or the period
Does anyone have any pointers?
We make these variables global so we know when they have loaded:
let surfheight, surfperiod;
fetch(
"https://data.channelcoast.org/observations/waves/latest?key='my key"
)
.then(function (resp) {
return resp.text();
})
.then(function (data) {
//console.log(data);
let parser = new DOMParser(),
xmlDoc = parser.parseFromString(data, "text/xml");
//console.log(xmlDoc.getElementsByTagName('ms:hs')[36].innerHTML); //76=Perran,36 Porthleven
surfheight = xmlDoc.getElementsByTagName("ms:hs")[36].innerHTML;
surfperiod = xmlDoc.getElementsByTagName("ms:tp")[36].innerHTML;
// you can set the surf variable here, because the sketch will start only after the data loads,
// also make sure to first convert it to a number like "Number(surfheight)" otherwise it won't work
surfht = Number(surfheight);
surfpd = Number(surfperiod);
document.getElementById("surfheight").textContent = surfheight;
document.getElementById("surfperiod").textContent = surfperiod;
});
var yoff = 0; // 2nd dimension of perlin noise
var waveColor, waveColor2, waveColor3;
var waveColorArr;
var controls, waveSpeed;
var canvas;
let surfht;
let surfpd;
function setup() {
canvas = createCanvas(windowWidth, windowHeight);
waveColor = color(0, 50, 120, 100);
waveColor2 = color(0, 100, 150, 100);
waveColor3 = color(0, 200, 250, 100);
noiseDetail(2, 0.2);
waveColorArr = [waveColor, waveColor, waveColor2, waveColor2, waveColor3, waveColor3];
}
function draw() {
// after these load, the sketch starts
if (!surfperiod && !surfheight) {
return;
}
background(0);
noStroke();
const amp = map(surfht, 0, 10, 0, 1);
//const amp = map(surfpd, 0, 10, 0, 1);
for (var i = 0; i <= 5; i++) {
// We are going to draw a polygon out of the wave points
beginShape();
fill(waveColorArr[i]);
var xoff = 0;
for (var x = 0; x <= width + 500; x += 100) {
var y = map(
noise(xoff, yoff - 0.5 * i),
0,
1,
(height / 10) * (i + 1),
height - height / 10 + (height / 10) * i
);
vertex(x, y);
// i've extracted this into a variable for cleaner code
const inc = map(surfpd, 0, 20, 0.01, 0.5);
xoff += inc + 0.5 / 10000.0;
}
vertex(width, height);
vertex(0, height);
endShape(CLOSE);
}
const inc = map(surfht, 0, 10, 0, 0.025);
yoff += 0.007 + inc + 0.5 / 10000.0;
}
function windowResized() {
resizeCanvas(window.innerWidth, window.innerHeight);
}
This is a the bit of code that mainly draws a single wave:
// We are going to draw a polygon out of the wave points
beginShape();
fill(waveColorArr[i]);
var xoff = 0;
for (var x = 0; x <= width + 500; x += 100) {
var y = map(
noise(xoff, yoff - 0.5 * i),
0,
1,
(height / 10) * (i + 1),
height - height / 10 + (height / 10) * i
);
vertex(x, y);
// i've extracted this into a variable for cleaner code
const inc = map(surfpd, 0, 20, 0.01, 0.5);
xoff += inc + 0.5 / 10000.0;
}
vertex(width, height);
vertex(0, height);
endShape(CLOSE);
You've already figured out how to map() the inc value.
Similar notice y is mapped as well, from 0.0 -> 1.0 range to (height / 10) * (i + 1)
to height - height / 10 + (height / 10) * i range.
A quick and hacky way to do it is to multiply those values by a value which would scale the wave height.
Better yet, you could encapsulate the instructions into a re-usable function, configurable with parameters.
You can also have a look at this detailed answer on drawing sine waves and remember that you can add/multiply waves together to get different shapes.
My script is animating the changing of the color of the ball. The 1 complete sequence takes 1 second and changes the color from green to blue using gradient. I am trying to add a move() method to move the ball.
However I have a problem where to execute my move() method.
Right now the animation of the ball moving is executed witihin the animation of the color but it shouldn't be. The color should change diffrently and the ball should be moving ininitely. I want the ball to move infinitely and the chaning of the color has to take 1 second like it is now.
Am I doing this right?
<-- Edit - I changed interval to 2000ms, just to visualize the problem better ( the moving of the ball stops but it shouldn't)
var canvas = document.querySelector('canvas');
var c = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var i = 0;
var ec;
var direction = 0;
var x = 100;
var dx = 10;
var y = 100;
var radius = 100;
function move() {
if(x + radius > window.innerWidth || x - radius < 0) {
dx = -dx;
}
x += dx;
}
function animate() {
c.clearRect(0, 0, innerWidth, innerHeight)
move();
var gradient = c.createLinearGradient(0, 0, 170, 0);
ec = 255 - i;
gradient.addColorStop(1, "rgba(" + 0 + "," + i + "," + ec + ")", 1);
c.fillStyle = gradient;
c.beginPath();
c.arc(x, y, radius, 0, 2 * Math.PI);
c.fill();
console.log(i, ec);
if(i == 255) direction = 1;
if(direction == 1 && i == 0) direction = 2;
if(direction == 0 ) i+=5;
else if(direction == 1 && direction != 2) i -= 5;
if(direction != 2) requestAnimationFrame(animate);
}
setInterval(function draw() {
direction = 0;
animate();
}, 2000);
canvas {
border: 1px solid black;
}
body {
margin: 0;
}
<canvas></canvas>
I got everything working as desired. But it was complicated, is there any way to make it simpler?
Could someone teach me how to use snippet?
var canvas = document.querySelector('canvas');
var c = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var i = 0;
var ec;
var direction = 0;
var x = 300;
var y = 300;
var radius = 200;
var speed = 5;
var dateDiff;
var date1;
var executedTimer = false;
var executedColor = false;
function draw() {
c.clearRect(0, 0, innerWidth, innerHeight)
c.beginPath();
c.arc(x, y, radius, 0, 2 * Math.PI);
c.fill();
}
function move() {
if(x + radius > window.innerWidth || x - radius < 0) {
speed = -speed;
}
x += speed;
}
function color() {
var gradient = c.createLinearGradient(0, 0, 170, 0);
ec = 255 - i;
gradient.addColorStop(1, "rgba(" + 0 + "," + i + "," + ec + ")", 1);
c.fillStyle = gradient;
//console.log(i, ec);
if(i == 255) direction = 1;
if(direction == 1 && i == 0) direction = 2;
if(direction == 0 ) i += 5;
else if(direction == 1 && direction != 2) i -= 5;
}
function timerStart() {
date1 = new Date();
executedTimer = true;
}
function timerCheck() {
var date2 = new Date();
dateDiff = Math.abs(date1 - date2);
if(dateDiff >= 1000)date1 = date2;
}
function animate() {
draw();
move();
if(executedTimer == false) timerStart();
if(executedColor == false) {
executedColor = true;
color();
}
if(dateDiff < 1000) color();
if(dateDiff >= 1000 && direction == 2) {
i = 0;
direction = 0;
//console.log(dateDiff);
color();
}
timerCheck();
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
canvas {
border: 1px solid black;
}
body {
margin: 0;
}
<canvas></canvas>
Treat both the position and the color as two different components, each with its own duration and its update method.
Then start a single animation loop in which you will
update the color component
update the position component
draw the scene
To ensure your animations last the duration you want, use a delta-time, which will let you know where you are relatively to the beginning of the animation.
This avoids issues with monitor using different refresh-rates (simply incrementing a value by a fixed amount will make your objects move twice faster on a 120Hz monitor than on a 60Hz one, generally not what you want), and it also allows to not care about the fact your animation is being paused by the browser when offscreen.
{
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const w = canvas.width = window.innerWidth;
const h = canvas.height = window.innerHeight;
const radius = Math.min( w, h ) / 3;
const color = {
duration: 1000,
update( elapsed ) {
const delta = (elapsed % this.duration) / this.duration;
const direction = (elapsed % (this.duration * 2)) - this.duration;
const val = (direction < 0) ?
255 - (delta * 255) :
delta * 255 ;
this.val = `rgba(0, ${ val }, ${ 255 - val })`;
}
};
const position = {
duration: 3000,
y: h / 2,
update( elapsed ) {
const delta = (elapsed % this.duration) / this.duration;
const direction = (elapsed % (this.duration * 2)) - this.duration;
this.x = direction < 0 ?
delta * w :
w - (delta * w);
}
};
function draw() {
ctx.clearRect( 0, 0, w, h );
ctx.beginPath();
ctx.arc( position.x, position.y, radius, 0, Math.PI *2 );
ctx.fillStyle = color.val;
ctx.fill();
}
// We record the time we started the animation
// We will base our time relative animation on that
const start = performance.now();
function anim( timestamp ) {
const elapsed = timestamp - start;
color.update( elapsed );
position.update( elapsed );
draw();
requestAnimationFrame( anim );
}
requestAnimationFrame( anim );
}
body {
margin: 0;
}
<canvas></canvas>
And of course if you are going to have a lot of such both-directions animations, you could write an helper function to just get the value based on the elapsed time:
{
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const w = canvas.width = window.innerWidth;
const h = canvas.height = window.innerHeight;
const radius = Math.min( w, h ) / 3;
function getValueAtTime( elapsed, duration, max_val ) {
const delta = (elapsed % duration) / duration;
const direction = (elapsed % (duration * 2)) - duration;
const val = delta * max_val;
return direction < 0 ? val : max_val - val;
}
const color = {
duration: 1000,
update( elapsed ) {
const val = getValueAtTime( elapsed, this.duration, 255 );
this.val = `rgba(0, ${ val }, ${ 255 - val })`;
}
};
const position = {
duration: 3000,
y: h / 2,
update( elapsed ) {
this.x = getValueAtTime( elapsed, this.duration, w );
}
};
function draw() {
ctx.clearRect( 0, 0, w, h );
ctx.beginPath();
ctx.arc( position.x, position.y, radius, 0, Math.PI *2 );
ctx.fillStyle = color.val;
ctx.fill();
}
// We record the time we started the animation
// We will base our time relative animation on that
const start = performance.now();
function anim( timestamp ) {
const elapsed = timestamp - start;
color.update( elapsed );
position.update( elapsed );
draw();
requestAnimationFrame( anim );
}
requestAnimationFrame( anim );
}
body {
margin: 0;
}
<canvas></canvas>
I am having trouble trying to set the canvas to fill the whole window. It is currently set to 500x500.. I am new to coding and would really appreciate any help! Thank you.
Here is the code:
<!DOCTYPE html>
<html>
<head>
<title>Starfield effect done in HTML 5</title>
<script>
window.onload = function() {
/* --- config start --- */
var starfieldCanvasId = "starfieldCanvas", // id of the canvas to use
framerate = 60, // frames per second this animation runs at (this is not exact)
numberOfStarsModifier = 0.15, // Number of stars, higher numbers have performance impact
flightSpeed = 0.003; // speed of the flight, the higher this number the faster
/* ---- config end ---- */
var canvas = document.getElementById(starfieldCanvasId),
context = canvas.getContext("2d"),
width = canvas.width,
height = canvas.height,
numberOfStars = width * height / 1000 * numberOfStarsModifier,
dirX = width / 2,
dirY = height / 2,
stars = [],
TWO_PI = Math.PI * 2;
// initialize starfield
for(var x = 0; x < numberOfStars; x++) {
stars[x] = {
x: range(0, width),
y: range(0, height),
size: range(0, 1)
};
}
// when mouse moves over canvas change middle point of animation
canvas.onmousemove = function(event) {
dirX = event.offsetX,
dirY = event.offsetY;
}
// start tick at specified fps
window.setInterval(tick, Math.floor(1000 / framerate));
// main routine
function tick() {
var oldX,
oldY;
// reset canvas for next frame
context.clearRect(0, 0, width, height);
for(var x = 0; x < numberOfStars; x++) {
// save old status
oldX = stars[x].x;
oldY = stars[x].y;
// calculate changes to star
stars[x].x += (stars[x].x - dirX) * stars[x].size * flightSpeed;
stars[x].y += (stars[x].y - dirY) * stars[x].size * flightSpeed;
stars[x].size += flightSpeed;
// if star is out of bounds, reset
if(stars[x].x < 0 || stars[x].x > width || stars[x].y < 0 || stars[x].y > height) {
stars[x] = {
x: range(0, width),
y: range(0, height),
size: 0
};
}
// draw star
context.strokeStyle = "rgba(255, 255, 255, " + Math.min(stars[x].size, 1) + ")";
context.lineWidth = stars[x].size;
context.beginPath();
context.moveTo(oldX, oldY);
context.lineTo(stars[x].x, stars[x].y);
context.stroke();
}
}
// get a random number inside a range
function range(start, end) {
return Math.random() * (end - start) + start;
}
};
</script>
</head>
<body style="background:#000;">
<canvas width="500" height="500" id="starfieldCanvas"></canvas>
</body>
</html>
Original code and credit to: https://www.timewasters-place.com/starfield-animation-done-in-html-5/
I assume you mean that you want your script to determine the screen size automatically, and then to set the canvas to be the full screen instead of the hardcoded 500 x 500 currently.
You can determine the viewport size programatically using the following where the width and height are as follows respectively. (Source):
var width = window.innerWidth
|| document.documentElement.clientWidth
|| document.body.clientWidth;
var height = window.innerHeight
|| document.documentElement.clientHeight
|| document.body.clientHeight;
Then you can set the dimensions of your canvas as follows:
canvas.width = width;
canvas.height = height;
So, this would be the full code in your case:
<!DOCTYPE html>
<html>
<head>
<title>Starfield effect done in HTML 5</title>
<script>
window.onload = function() {
/* --- config start --- */
var starfieldCanvasId = "starfieldCanvas", // id of the canvas to use
framerate = 60, // frames per second this animation runs at (this is not exact)
numberOfStarsModifier = 0.15, // Number of stars, higher numbers have performance impact
flightSpeed = 0.003; // speed of the flight, the higher this number the faster
var width = window.innerWidth
|| document.documentElement.clientWidth
|| document.body.clientWidth;
var height = window.innerHeight
|| document.documentElement.clientHeight
|| document.body.clientHeight;
/* ---- config end ---- */
var canvas = document.getElementById(starfieldCanvasId),
context = canvas.getContext("2d"),
stars = [],
TWO_PI = Math.PI * 2;
canvas.width = width;
canvas.height = height;
numberOfStars = width * height / 1000 * numberOfStarsModifier;
dirX = width / 2;
dirY = height / 2;
// initialize starfield
for(var x = 0; x < numberOfStars; x++) {
stars[x] = {
x: range(0, width),
y: range(0, height),
size: range(0, 1)
};
}
// when mouse moves over canvas change middle point of animation
canvas.onmousemove = function(event) {
dirX = event.offsetX,
dirY = event.offsetY;
}
// start tick at specified fps
window.setInterval(tick, Math.floor(1000 / framerate));
// main routine
function tick() {
var oldX,
oldY;
// reset canvas for next frame
context.clearRect(0, 0, width, height);
for(var x = 0; x < numberOfStars; x++) {
// save old status
oldX = stars[x].x;
oldY = stars[x].y;
// calculate changes to star
stars[x].x += (stars[x].x - dirX) * stars[x].size * flightSpeed;
stars[x].y += (stars[x].y - dirY) * stars[x].size * flightSpeed;
stars[x].size += flightSpeed;
// if star is out of bounds, reset
if(stars[x].x < 0 || stars[x].x > width || stars[x].y < 0 || stars[x].y > height) {
stars[x] = {
x: range(0, width),
y: range(0, height),
size: 0
};
}
// draw star
context.strokeStyle = "rgba(255, 255, 255, " + Math.min(stars[x].size, 1) + ")";
context.lineWidth = stars[x].size;
context.beginPath();
context.moveTo(oldX, oldY);
context.lineTo(stars[x].x, stars[x].y);
context.stroke();
}
}
// get a random number inside a range
function range(start, end) {
return Math.random() * (end - start) + start;
}
};
</script>
</head>
<body style="background:#000;">
<canvas id="starfieldCanvas"></canvas>
</body>
</html>
I also tested this on Fiddle. Here's the link: Fiddle
Hope this helps! Let me know if it works.
I have a little trouble. I would like to use animation from:
http://codepen.io/chuckeles/pen/mJeaNJ
The main question, how to edit it, so instead of dots, there would be small image? In which part it should be changed? I have no idea where to start so I need a little guidance, in which part I should edit code to change dots to image. Any help or advice would be appreciated.
Full Javascript code here:
// --- CANVAS ---
var canvas = document.getElementById("bg");
var ctx = canvas.getContext("2d");
// --- UTILS ---
// request frame
var requestFrame =
window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
// window resizing
(window.onresize = function() {
// set new canvas size
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
})();
// --- STARS ---
var Star = function(x, y) {
this.x = x || 0;
this.y = y || 0;
this.mx = 0;
this.my = 0;
this.distance = 0;
this.mult = Math.random() * 0.4 + 0.6;
};
// --- GLOBALS ---
// the star array
var stars = [];
// stars to remove from the array
var starsToRemove = [];
// create random stars
for (var i = 0; i < 60; ++i) {
// pos
var x = Math.random() * canvas.width;
var y = Math.random() * canvas.height;
// create
stars.push(new Star(x, y));
}
// --- SETUP ---
// disable smoothing
ctx.imageSmoothingEnabled = false;
// --- MAIN LOOP ---
// loop function
var loop = function() {
// --- UPDATE ---
// create random stars
var chance = 0.2;
var asp = canvas.width / canvas.height;
if (Math.random() < chance)
stars.push(
new Star(0, Math.random() * canvas.height) // left
);
if (Math.random() < chance)
stars.push(
new Star(canvas.width, Math.random() * canvas.height) // right
);
if (Math.random() < chance * asp)
stars.push(
new Star(Math.random() * canvas.width, 0) // top
);
if (Math.random() < chance * asp)
stars.push(
new Star(Math.random() * canvas.width, canvas.height) // botton
);
// update stars
stars.forEach(function(star) {
// update motion
star.mx = -(star.x - canvas.width / 2) / 300;
star.my = -(star.y - canvas.height / 2) / 300;
// apply motion
star.x += star.mx * star.mult;
star.y += star.my * star.mult;
// update distance
star.distance = Math.sqrt(
Math.pow((star.x - canvas.width / 2), 2) +
Math.pow((star.y - canvas.height / 2), 2)
);
// remove if close to the center
if (star.distance < 40 * star.mult)
starsToRemove.push(star);
});
// remove stars
starsToRemove.forEach(function(toRemove) {
stars.splice(stars.indexOf(toRemove), 1);
});
starsToRemove = [];
// --- DRAW ---
// clear
ctx.clearRect(0, 0, canvas.width, canvas.height);
// get image data
var data = ctx.getImageData(0, 0, canvas.width, canvas.height);
// for each star
stars.forEach(function(star) {
// get pos
var x = Math.floor(star.x);
var y = Math.floor(star.y);
// draw a pixel
var i = y * data.width + x;
data.data[i * 4 + 0] = 255;
data.data[i * 4 + 1] = 255;
data.data[i * 4 + 2] = 255;
// apply alpha based on distance
var a = (star.distance - 40 * star.mult) / (canvas.width / 2 - 40 * star.mult);
data.data[i * 4 + 3] = 255 * a * star.mult;
});
// put back
ctx.putImageData(data, 0, 0);
// new loop
requestFrame(loop);
};
// start loop
requestFrame(loop);
You might want to edit it after the comment:
// --- DRAW ---
There will be canvas drawing methods you might want to change. For example, the line
ctx.clearRect(0, 0, canvas.width, canvas.height);
might clear all pixels drawn in the size of canvas.
Mights return a array from canvas pixels on its size.
ctx.getImageData(0, 0, canvas.width, canvas.height);
Mights draw each pixel (star).
// for each star
stars.forEach(function(star) {
// get pos
var x = Math.floor(star.x);
var y = Math.floor(star.y);
// draw a pixel
var i = y * data.width + x;
// change pixel color
data.data[i * 4 + 0] = 255;//Red = 255
data.data[i * 4 + 1] = 255;//Green = 255
data.data[i * 4 + 2] = 255;//Blue = 255
// ^ color (rgb)
// apply alpha based on distance
var a = (star.distance - 40 * star.mult) / (canvas.width / 2 - 40 * star.mult);
data.data[i * 4 + 3] = 255 * a * star.mult;
});
// put back
ctx.putImageData(data, 0, 0);
// ^ this line update the canvas pixels edited as above
With DOM methods, you can draw span or div instead of pixels in canvas, but it's not good for performance do that.