|
// 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(); |
|
|