Skip to content

Instantly share code, notes, and snippets.

@Bolloxim
Created July 16, 2014 05:25
Show Gist options
  • Save Bolloxim/566a34ab717e27fb02cb to your computer and use it in GitHub Desktop.
Save Bolloxim/566a34ab717e27fb02cb to your computer and use it in GitHub Desktop.
A Pen by Andi Smithers.
<body>
<canvas id="starfield"
></canvas>
</body>
// globals
var canvas, context, alpha;
var cX, cY, tX, tY, mouseX, mouseY, density;
var stars = [];
var cameraDepth=0;
var enterWarp, warpStartDepth, warpTime, velocity;
// define to 0 to brute force move all stars
const cameraTrick = 1;
// options
const starCount = 1024;
const initVelocity = -1.0;
const termVelocity = -10.0;
const topleft = 0;
const trackMouse = 1;
const focalPoint = 256;
const sparcity = 2.0;
function Star(index)
{
// randomize a field -1024 to 1024 and positive z
this.x = (Math.random() *2048-1024)*sparcity;
this.y = (Math.random() *2048-1024)*sparcity;
this.z = ((starCount-1)-index)/density;
if (topleft==1)
{
this.x = this.x + 1024;
this.y = this.y + 1024;
}
}
Star.prototype.move = function()
{
// dont really have to move all stars
this.z = (this.z + velocity) %1024
}
Star.prototype.draw = function()
{
// compute depth perspective effect, cameraDepth is used when cameraTrick = 1
var depth = ((this.z + cameraDepth) %1024)+1;
var x = this.x * focalPoint / depth + cX;
var y = this.y * focalPoint / depth + cY;
var sz = 5 * focalPoint / depth;
// fill a rect
context.beginPath();
context.rect(x, y, sz,sz);
context.fillStyle = 'white';
context.fill();
// use border edge for twinkle effect
context.lineWidth = 0;
context.strokeStyle = 'black';
context.stroke();
};
Star.prototype.warpline = function()
{
var depth = ((this.z + cameraDepth) %1024)+1;
var depthStart = ((this.z + warpStartDepth)%1024)+1
if (depth>depthStart) depth = 1;
var x = this.x * focalPoint / depth + cX;
var y = this.y * focalPoint / depth + cY;
var sz = 5 * focalPoint / depth ;
var wx = this.x * focalPoint / depthStart + cX;
var wy = this.y * focalPoint / depthStart + cY;
var wsz = 5 * focalPoint / depthStart;
if (depth>depthStart) return;
// fill a ray
context.beginPath();
context.moveTo(wx, wy);
context.lineTo(x+sz, y);
context.lineTo(x, y);
context.moveTo(wx, wy);
context.lineTo(x+sz, y+sz);
context.lineTo(x+sz, y);
context.closePath();
context.fillStyle = 'white';
context.fill();
// use border edge for twinkle effect
context.lineWidth = 0;
context.strokeStyle = 'black';
context.stroke();
};
function init()
{
// setup canvas and context
canvas = document.getElementById('starfield');
context = canvas.getContext('2d');
// set canvas to be window dimensions
resize();
canvas.addEventListener('mousemove', mousemove);
canvas.addEventListener('click', mouseclick);
window.addEventListener('resize', resize);
// compute center of screen (its really centre but for americans I change it)
tX = cX = canvas.width/2;
tY = cY = canvas.height/4;
if (topleft==1)
{
cX=0;
cY=0;
}
density = starCount/1024;
// allocate and init stars
for (i=0; i<starCount; i++)
{
stars[i] = new Star(i);
}
alpha = 6.0;
enterWarp = false;
velocity = initVelocity;
}
function animate()
{
// movement update
move();
// render update
render();
// trigger next frame
requestAnimationFrame(animate);
}
function move()
{
if (enterWarp)
{
velocity*=1.02;
if (velocity<termVelocity) velocity=termVelocity;
warpTime=warpTime+1;
if (warpTime>120) enterWarp = false;
}
else
{
// slow down
var dv = velocity - initVelocity;
velocity-= dv * 0.01;
}
// brute force move.. will replace with camera trick
if (cameraTrick==0)
{
for (i = 0; i < stars.length; i++)
{
stars[i].move();
};
}
else
{
// camera movement trick
cameraDepth=(cameraDepth+velocity)%1024;
if (cameraDepth<0) cameraDepth+=1024;
}
var dx = tX - cX;
var dy = tY - cY;
var dist = Math.sqrt(dx*dx + dy*dy);
if (dist!=0)
{
dx/=dist;
dy/=dist;
}
dist = Math.min(dist, 512.0);
cX = cX + (dist*dx*0.06125);
cY = cY + (dist*dy*0.06125);
}
function render()
{
// brute force clear
context.clearRect(0, 0, canvas.width, canvas.height);
// draw all stars
for (i = 0; i < stars.length; i++)
{
var index = cameraTrick==1 ?(i + 1 + Math.floor(cameraDepth)*density)%stars.length : i;
if (enterWarp) stars[index].warpline();
stars[index].draw();
};
// banner for a about 12 seconds
alpha -= 0.008;
if (alpha<=0) return;
context.font = '40pt Calibri';
context.fillStyle = 'rgba(255,255,255,'+alpha+')';
context.textAlign = "center";
context.fillText('Star Trek Field', canvas.width/2, 100);
context.font = '10pt Calibri';
context.fillText('(move mouse to change field options in script for effects)', canvas.width/2, 120);
context.fillText('* Left Click Warps *', canvas.width/2, 140);
}
function mousemove(event)
{
var rect = canvas.getBoundingClientRect();
mouseX = event.clientX - rect.left,
mouseY = event.clientY - rect.top
// just for fun lets just click on moving
if (trackMouse)
{
tX = mouseX;
tY = mouseY;
}
}
function mouseclick()
{
tX = mouseX;
tY = mouseY;
if (enterWarp) return;
enterWarp = true;
warpStartDepth = cameraDepth;
warpTime = 0;
}
function resize()
{
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
// entry point
init();
animate();

Starfield old school style

The illusion of movement without moving (although I move the stars if cameraTrick==0, basically we wrap the starfield around without ever changing the dataset).

versions

0.6 added WARP speed on the left click

0.5 added sparcity, focalpoint, fixed sort bug

0.4: mouse tracking on clicks

0.3: Sorted field for cameraMethod

0.2 :added moving cameraTrick now the field is working. added some options to change camera velocity. moved from integer math to float for micro speed increments added top left option.

A Pen by Andi Smithers on CodePen.

License.

body {
background: #000000
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment