2011-01-27 02:45:34 +00:00
|
|
|
function FizzyText(message) {
|
|
|
|
|
2011-02-11 19:25:33 +00:00
|
|
|
var that = this;
|
2011-01-29 01:23:20 +00:00
|
|
|
|
2011-02-11 19:25:33 +00:00
|
|
|
// These are the variables that we manipulate with gui-dat.
|
|
|
|
// Notice they're all defined with "this". That makes them public.
|
|
|
|
// Otherwise, gui-dat can't see them.
|
2011-01-29 01:23:20 +00:00
|
|
|
|
2011-02-11 19:25:33 +00:00
|
|
|
this.growthSpeed = 0.5; // how fast do particles change size?
|
|
|
|
this.maxSize = 3.2; // how big can they get?
|
|
|
|
this.noiseStrength = 10; // how turbulent is the flow?
|
|
|
|
this.speed = 0.4; // how fast do particles move?
|
2011-01-28 20:26:38 +00:00
|
|
|
this.displayOutline = false; // should we draw the message as a stroke?
|
2011-01-29 01:23:20 +00:00
|
|
|
this.framesRendered = 0;
|
|
|
|
|
2011-01-28 20:26:38 +00:00
|
|
|
// __defineGetter__ and __defineSetter__ makes JavaScript believe that
|
|
|
|
// we've defined a variable 'this.message'. This way, whenever we
|
|
|
|
// change the message variable, we can call some more functions.
|
|
|
|
|
|
|
|
this.__defineGetter__("message", function () {
|
|
|
|
return message;
|
|
|
|
});
|
2011-01-27 02:45:34 +00:00
|
|
|
|
2011-01-28 20:26:38 +00:00
|
|
|
this.__defineSetter__("message", function (m) {
|
|
|
|
message = m;
|
|
|
|
createBitmap(message);
|
|
|
|
});
|
2011-01-27 02:45:34 +00:00
|
|
|
|
2011-02-11 19:25:33 +00:00
|
|
|
// We can even add functions to the GUI! As long as they have
|
|
|
|
// 0 arguments, we can call them from the dat-gui panel.
|
|
|
|
|
|
|
|
this.explode = function() {
|
|
|
|
var mag = Math.random()*30+30;
|
|
|
|
for (var i in particles) {
|
|
|
|
var angle= Math.random()*Math.PI*2;
|
|
|
|
particles[i].vx = Math.cos(angle)*mag;
|
|
|
|
particles[i].vy = Math.sin(angle)*mag;
|
|
|
|
}
|
|
|
|
};
|
2011-01-27 02:45:34 +00:00
|
|
|
|
2011-02-11 19:25:33 +00:00
|
|
|
////////////////////////////////////////////////////////////////
|
2011-01-27 02:45:34 +00:00
|
|
|
|
2011-01-28 20:26:38 +00:00
|
|
|
var _this = this;
|
2011-01-27 02:45:34 +00:00
|
|
|
|
2011-02-11 19:25:33 +00:00
|
|
|
var width = 550;
|
|
|
|
var height = 200;
|
|
|
|
var textAscent = 82;
|
2011-01-28 20:26:38 +00:00
|
|
|
var textOffsetLeft = 80;
|
2011-02-11 19:25:33 +00:00
|
|
|
var noiseScale = 300;
|
|
|
|
var frameTime = 30;
|
|
|
|
|
2011-01-27 02:45:34 +00:00
|
|
|
var colors = ["#00aeff", "#0fa954", "#54396e", "#e61d5f"];
|
2011-02-11 19:25:33 +00:00
|
|
|
|
|
|
|
// This is the context we use to get a bitmap of text using
|
|
|
|
// the getImageData function.
|
2011-01-26 01:36:47 +00:00
|
|
|
var r = document.createElement('canvas');
|
|
|
|
var s = r.getContext('2d');
|
2011-01-27 02:45:34 +00:00
|
|
|
|
2011-02-11 19:25:33 +00:00
|
|
|
// This is the context we actually use to draw.
|
2011-01-26 01:36:47 +00:00
|
|
|
var c = document.createElement('canvas');
|
|
|
|
var g = c.getContext('2d');
|
2011-01-27 02:45:34 +00:00
|
|
|
|
|
|
|
r.setAttribute('width', width);
|
|
|
|
c.setAttribute('width', width);
|
2011-01-26 01:36:47 +00:00
|
|
|
r.setAttribute('height', height);
|
|
|
|
c.setAttribute('height', height);
|
2011-01-27 02:45:34 +00:00
|
|
|
|
2011-02-11 19:25:33 +00:00
|
|
|
// Add our demo to the HTML
|
2011-01-26 01:36:47 +00:00
|
|
|
document.getElementById('helvetica-demo').appendChild(c);
|
2011-01-27 02:45:34 +00:00
|
|
|
|
2011-02-11 19:25:33 +00:00
|
|
|
// Stores bitmap image
|
2011-01-27 02:45:34 +00:00
|
|
|
var pixels = [];
|
2011-01-28 20:26:38 +00:00
|
|
|
|
|
|
|
// Stores a list of particles
|
2011-01-26 02:30:05 +00:00
|
|
|
var particles = [];
|
2011-01-27 02:45:34 +00:00
|
|
|
|
2011-02-11 19:25:33 +00:00
|
|
|
// Set g.font to the same font as the bitmap canvas, incase we
|
|
|
|
// want to draw some outlines.
|
2011-01-27 02:45:34 +00:00
|
|
|
s.font = g.font = "800 " + textAscent + "px helvetica, arial, sans-serif";
|
|
|
|
|
2011-01-28 20:26:38 +00:00
|
|
|
// Instantiate some particles
|
2011-01-27 02:45:34 +00:00
|
|
|
for (var i = 0; i < 1000; i++) {
|
|
|
|
particles.push(new Particle(Math.random() * width, Math.random() * height));
|
|
|
|
}
|
|
|
|
|
2011-02-11 19:25:33 +00:00
|
|
|
// This function creates a bitmap of pixels based on your message
|
|
|
|
// It's called every time we change the message property.
|
2011-01-28 20:26:38 +00:00
|
|
|
var createBitmap = function (msg) {
|
|
|
|
|
2011-01-26 01:36:47 +00:00
|
|
|
s.fillStyle = "#fff";
|
2011-01-26 03:14:15 +00:00
|
|
|
s.fillRect(0, 0, width, height);
|
2011-01-27 02:45:34 +00:00
|
|
|
|
2011-01-26 03:14:15 +00:00
|
|
|
s.fillStyle = "#222";
|
2011-01-28 20:26:38 +00:00
|
|
|
s.fillText(msg, textOffsetLeft, textAscent);
|
2011-01-27 02:45:34 +00:00
|
|
|
|
2011-01-26 01:36:47 +00:00
|
|
|
// Pull reference
|
2011-01-26 02:30:05 +00:00
|
|
|
var imageData = s.getImageData(0, 0, width, height);
|
|
|
|
pixels = imageData.data;
|
2011-01-27 02:45:34 +00:00
|
|
|
|
2011-01-26 01:36:47 +00:00
|
|
|
};
|
2011-01-27 02:45:34 +00:00
|
|
|
|
2011-02-11 19:25:33 +00:00
|
|
|
// Called once per frame, updates the animation.
|
2011-01-27 02:45:34 +00:00
|
|
|
var render = function () {
|
|
|
|
|
2011-02-11 19:25:33 +00:00
|
|
|
that.framesRendered ++;
|
2011-01-29 01:23:20 +00:00
|
|
|
|
2011-01-26 03:14:15 +00:00
|
|
|
g.clearRect(0, 0, width, height);
|
2011-01-27 02:45:34 +00:00
|
|
|
|
|
|
|
if (_this.displayOutline) {
|
2011-01-26 23:25:40 +00:00
|
|
|
g.globalCompositeOperation = "source-over";
|
|
|
|
g.strokeStyle = "#000";
|
2011-01-27 02:45:34 +00:00
|
|
|
g.lineWidth = .5;
|
2011-01-28 20:26:38 +00:00
|
|
|
g.strokeText(message, textOffsetLeft, textAscent);
|
2011-01-26 23:25:40 +00:00
|
|
|
}
|
2011-01-27 02:45:34 +00:00
|
|
|
|
2011-01-26 23:25:40 +00:00
|
|
|
g.globalCompositeOperation = "darker";
|
2011-01-27 02:45:34 +00:00
|
|
|
|
2011-01-26 21:33:54 +00:00
|
|
|
for (var i = 0; i < particles.length; i++) {
|
2011-01-27 02:45:34 +00:00
|
|
|
g.fillStyle = colors[i % colors.length];
|
2011-01-26 23:25:40 +00:00
|
|
|
particles[i].render();
|
2011-01-26 02:30:05 +00:00
|
|
|
}
|
2011-01-26 23:25:40 +00:00
|
|
|
|
2011-01-26 02:30:05 +00:00
|
|
|
};
|
2011-01-27 02:45:34 +00:00
|
|
|
|
2011-02-11 19:25:33 +00:00
|
|
|
// Returns x, y coordinates for a given index in the pixel array.
|
2011-01-27 02:45:34 +00:00
|
|
|
var getPosition = function (i) {
|
|
|
|
return {
|
|
|
|
x: (i - (width * 4) * Math.floor(i / (width * 4))) / 4,
|
|
|
|
y: Math.floor(i / (width * 4))
|
|
|
|
};
|
2011-01-26 01:36:47 +00:00
|
|
|
};
|
2011-01-27 02:45:34 +00:00
|
|
|
|
2011-02-11 19:25:33 +00:00
|
|
|
// Returns a color for a given pixel in the pixel array.
|
2011-01-27 02:45:34 +00:00
|
|
|
var getColor = function (x, y) {
|
2011-01-26 21:33:54 +00:00
|
|
|
var base = (Math.floor(y) * width + Math.floor(x)) * 4;
|
2011-01-27 02:45:34 +00:00
|
|
|
var c = {
|
|
|
|
r: pixels[base + 0],
|
|
|
|
g: pixels[base + 1],
|
|
|
|
b: pixels[base + 2],
|
|
|
|
a: pixels[base + 3]
|
|
|
|
};
|
2011-01-26 21:33:54 +00:00
|
|
|
|
2011-01-27 02:45:34 +00:00
|
|
|
return "rgb(" + c.r + "," + c.g + "," + c.b + ")";
|
2011-01-26 02:30:05 +00:00
|
|
|
};
|
2011-02-11 19:25:33 +00:00
|
|
|
|
|
|
|
// This calls the setter we've defined above, so it also calls
|
|
|
|
// the createBitmap function.
|
|
|
|
this.message = message;
|
|
|
|
|
|
|
|
var loop = function() {
|
|
|
|
// Don't render if we don't see it.
|
|
|
|
// Would be cleaner if I dynamically acquired the top of the canvas.
|
|
|
|
if (document.body.scrollTop < height + 20) {
|
|
|
|
render();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This calls the render function every 30 milliseconds.
|
|
|
|
setInterval(loop, frameTime);
|
|
|
|
|
|
|
|
// This class is responsible for drawing and moving those little
|
|
|
|
// colored dots.
|
2011-01-27 02:45:34 +00:00
|
|
|
function Particle(x, y, c) {
|
2011-01-28 20:26:38 +00:00
|
|
|
|
2011-02-11 19:25:33 +00:00
|
|
|
// Position
|
2011-01-27 02:45:34 +00:00
|
|
|
this.x = x;
|
|
|
|
this.y = y;
|
2011-02-11 19:25:33 +00:00
|
|
|
|
2011-01-28 20:26:38 +00:00
|
|
|
// Size of particle
|
|
|
|
this.r = 0;
|
2011-02-11 19:25:33 +00:00
|
|
|
|
2011-01-28 20:26:38 +00:00
|
|
|
// This velocity is used by the explode function.
|
2011-01-27 02:45:34 +00:00
|
|
|
this.vx = 0;
|
|
|
|
this.vy = 0;
|
2011-01-28 20:26:38 +00:00
|
|
|
|
|
|
|
// Called every frame
|
2011-01-27 02:45:34 +00:00
|
|
|
this.render = function () {
|
2011-01-28 20:26:38 +00:00
|
|
|
|
|
|
|
// What color is the pixel we're sitting on top of?
|
2011-01-27 02:45:34 +00:00
|
|
|
var c = getColor(this.x, this.y);
|
|
|
|
|
2011-01-28 20:26:38 +00:00
|
|
|
// Where should we move?
|
|
|
|
var angle = noise(this.x / noiseScale, this.y / noiseScale) * _this.noiseStrength;
|
|
|
|
|
|
|
|
// Are we within the boundaries of the image?
|
2011-01-27 02:45:34 +00:00
|
|
|
var onScreen = this.x > 0 && this.x < width &&
|
2011-02-11 19:25:33 +00:00
|
|
|
this.y > 0 && this.y < height;
|
|
|
|
|
2011-01-28 20:26:38 +00:00
|
|
|
var isBlack = c != "rgb(255,255,255)" && onScreen;
|
2011-02-11 19:25:33 +00:00
|
|
|
|
|
|
|
// If we're on top of a black pixel, grow.
|
|
|
|
// If not, shrink.
|
2011-01-27 02:45:34 +00:00
|
|
|
if (isBlack) {
|
|
|
|
this.r += _this.growthSpeed;
|
|
|
|
} else {
|
|
|
|
this.r -= _this.growthSpeed;
|
|
|
|
}
|
2011-01-28 20:26:38 +00:00
|
|
|
|
|
|
|
// This velocity is used by the explode function.
|
2011-01-27 02:45:34 +00:00
|
|
|
this.vx *= 0.5;
|
|
|
|
this.vy *= 0.5;
|
2011-01-28 20:26:38 +00:00
|
|
|
|
|
|
|
// Change our position based on the flow field and our
|
|
|
|
// explode velocity.
|
2011-01-27 02:45:34 +00:00
|
|
|
this.x += Math.cos(angle) * _this.speed + this.vx;
|
2011-01-28 20:26:38 +00:00
|
|
|
this.y += -Math.sin(angle) * _this.speed + this.vy;
|
|
|
|
|
2011-01-30 23:03:09 +00:00
|
|
|
this.r = GUI.constrain(this.r, 0, _this.maxSize);
|
2011-01-28 20:26:38 +00:00
|
|
|
|
|
|
|
// If we're tiny, keep moving around until we find a black
|
|
|
|
// pixel.
|
|
|
|
if (this.r <= 0) {
|
2011-01-27 02:45:34 +00:00
|
|
|
this.x = Math.random() * width;
|
|
|
|
this.y = Math.random() * height;
|
2011-01-28 20:26:38 +00:00
|
|
|
return; // Don't draw!
|
2011-01-27 02:45:34 +00:00
|
|
|
}
|
2011-01-28 20:26:38 +00:00
|
|
|
|
|
|
|
// Draw the circle.
|
2011-01-27 02:45:34 +00:00
|
|
|
g.beginPath();
|
|
|
|
g.arc(this.x, this.y, this.r, 0, Math.PI * 2, false);
|
|
|
|
g.fill();
|
2011-01-28 20:26:38 +00:00
|
|
|
|
2011-01-27 02:45:34 +00:00
|
|
|
}
|
2011-01-28 20:26:38 +00:00
|
|
|
|
2011-01-27 02:45:34 +00:00
|
|
|
}
|
|
|
|
|
2011-01-29 01:23:20 +00:00
|
|
|
}
|