jsx
Version:
a faster, safer, easier JavaScript
229 lines (183 loc) • 5.28 kB
JSX
import "js/web.jsx";
import "console.jsx";
class Config {
static const PROFILER_SERVER_API = "http://localhost:2012/post-profile";
static const quantity = 360;
static const size = 2.0;
static const decay = 0.98;
static const gravity = 2.0;
static const speed = 6.0;
static const canvasId = "night-sky";
static const fpsElementId = "fps";
}
final class Spark {
static const rad = Math.PI * 2;
var posX : number;
var posY : number;
var velX : number;
var velY : number;
var size : number;
var color : string;
var state = 0;
function constructor(posX : number, posY : number, size : number, color : string) {
this.posX = posX;
this.posY = posY;
this.size = size;
this.color = color;
var angle = Math.random() * Spark.rad;
var velocity = Math.random() * Config.speed;
this.velX = Math.cos(angle) * velocity;
this.velY = Math.sin(angle) * velocity;
}
function _decay() : void {
this.velX *= Config.decay;
this.velY *= Config.decay;
this.size *= Config.decay;
if(this.size < 0.5 && this.state == 0) {
this.color = Firework.randomColor();
this.size = Config.size;
this.state++;
}
}
function _move() : void {
this.posX += this.velX + (Math.random() - 0.5);
this.posY += this.velY + (Math.random() - 0.5) + Config.gravity;
}
function _render(view : FireworkView) : void {
view.cx.beginPath();
view.cx.arc(this.posX, this.posY, this.size, 0, Spark.rad, true);
view.cx.fillStyle = Math.random() > 0.2 ? this.color : "white";
view.cx.fill();
}
function _isLiving(view : FireworkView) : boolean {
if(this.size <= 0.01) return false;
if(this.posX <= 0) return false;
if(this.posX >= view.width || this.posY >= view.height) return false;
return true;
}
function draw(view : FireworkView) : boolean {
this._decay();
this._move();
this._render(view);
return this._isLiving(view);
}
}
final class Firework {
var sparks = [] : Spark[];
var view : FireworkView;
static function randomColor() : string {
var blightness = 60;
var rgb = [] : int[];
for (var i = 0; i < 3; ++i) {
rgb[i] = Math.min( (Math.random() * 0xFF + blightness) as int, 255 );
}
return "rgb(" +
rgb[0] as string + "," +
rgb[1] as string + "," +
rgb[2] as string + ")";
}
function constructor(view : FireworkView, x : int, y : int) {
this.view = view;
var color = "lime";
for (var i = 0; i < Config.quantity; ++i) {
this.sparks.push(new Spark(x, y, Config.size, color));
}
}
function update() : boolean {
for(var i = 0; i < this.sparks.length; ++i) {
var s = this.sparks[i];
if (! s.draw(this.view)) {
this.sparks.splice(i, 1);
}
}
return this.sparks.length > 0;
}
}
final class FireworkView {
var cx : CanvasRenderingContext2D;
var width : int;
var height : int;
var left : int;
var top : int;
var fireworks = [] : Firework[];
var numSparks = 0;
function constructor(canvas : HTMLCanvasElement) {
this.cx = canvas.getContext("2d") as CanvasRenderingContext2D;
this.width = canvas.width;
this.height = canvas.height;
var rect = canvas.getBoundingClientRect();
this.left = rect.left;
this.top = rect.top;
canvas.addEventListener("mousedown", function (e : Event) : void {
var me = e as MouseEvent;
assert me != null;
this.explode(me.clientX, me.clientY);
});
canvas.addEventListener("touchstart", function (e : Event) : void {
var te = e as TouchEvent;
assert te != null;
this.explode(te.touches[0].pageX, te.touches[0].pageY);
});
}
function explode(x : int, y : int) : void {
this.fireworks.push(new Firework(this, x - this.left, y - this.top));
}
function update() : void {
if (this.fireworks.length == 0) {
// first one
this.explode(this.width / 2 + this.left, this.height / 3);
}
this.numSparks = 0;
for (var i = 0; i < this.fireworks.length; ++i) {
var fw = this.fireworks[i];
if(fw.update()) {
this.numSparks += fw.sparks.length;
}
else {
this.fireworks.splice(i, 1);
}
}
this.cx.fillStyle = "rgba(0, 0, 0, 0.3)";
this.cx.fillRect(0, 0, this.width, this.height);
}
}
final class FPSWatcher {
var elementId : string;
var start = Date.now();
var frameCount = 0;
function constructor(elementId : string) {
this.elementId = elementId;
}
function update(numSparks : int) : void {
++this.frameCount;
if(this.frameCount % 100 == 0) {
var message = "FPS: " + ((this.frameCount / (Date.now() - this.start) * 1000) as int) as string +
" (sparks: " + numSparks as string + ")";
dom.id(this.elementId).innerHTML = message;
}
}
}
final class _Main {
static function main(args : string[]) : void {
var canvas = dom.id(Config.canvasId) as HTMLCanvasElement;
var view = new FireworkView(canvas);
var watcher = new FPSWatcher(Config.fpsElementId);
dom.window.setInterval( function() : void {
view.update();
watcher.update(view.numSparks);
}, 0);
if (JSX.profilerIsRunning()) {
log "profiler is running";
dom.window.setTimeout(function () : void {
JSX.postProfileResults(Config.PROFILER_SERVER_API, function (e, resultUrl) {
if (e) {
throw e;
}
else {
console.log(resultUrl);
}
});
}, 10 * 1000);
}
}
}