Sprite Animation with Parallax effect - through mootools
Change night and day, click
here
This is just an experiment that will probably turn into the basis of my blog's header (once I add
methods such as blowUpLemming(); and so forth). Although--in principle--the class used to support and run countless
little lemmings, having added the parallax effect through the background grass and stars movement means it
can only have the effect on one lemming at a time.
Changelog
24/11/2009 - Enabled blow-up lemming, fireworks effect based on work by crisp
24/11/2009 - Fixed IE7 support and made parallax effect smoother
24/11/2009 - Added options for size of container element / parallax layer
23/11/2009 - Added basic interaction with lemming and lemming 'chat'
23/11/2009 - Added support for daylight and night time themes, detected automatically as per client's PC clock
var Lemmings = new Class({
Implements: [Options, Events],
lemmings: [],
fireworks: [],
options: {
spriteWidth: 32,
spriteHeight: 32,
spriteContainerWidth: 256,
spriteExplodeContainerWidth: 512,
drawSpeed: 50,
marginTop: 52,
spriteZindex: 1000,
daytime: "auto",
daylightDetect: true,
parallaxLayerSize: "100%"
},
parallax: {},
setTheme: function() {
this.container.setStyles({
overflow: "hidden",
background: "url(http://fragged.org/images/parallax-bottom"+this.theme+".gif)"
});
this.parallax.topLayer.setStyles({
background: "url(http://fragged.org/images/parallax-top"+this.theme+".gif) no-repeat -400px top"
});
if (this.themeNotice)
this.themeNotice.dispose();
this.themeNotice = new Element("div", {
styles: {
fontSize: "12px",
padding: 5,
fontWeight: "bold",
zIndex: 100000,
position: "relative",
float: "right"
},
html: (this.theme == "D") ? "Daytime Lemmings" : "Lemmings in the Dark",
opacity: 0
}).injectTop(this.container).fade(1);
(function() {
this.themeNotice.fade(0);
}).delay(2000, this);
},
initialize: function(el, options) {
this.setOptions(options);
// daytime / night time...
var hours = new Date().get("hr");
this.theme = (this.options.daytime == "auto") ? (hours > 8 && hours < 16) ? "D" : "" : this.options.daytime;
this.container = $(el);
this.container.addEvents({
click: function() {
return;
this.addLemming("r", 0, 2);
}.bind(this)
}).setStyles({
overflow: "hidden"
});
this.coords = this.container.getCoordinates();
this.width = this.container.getSize().x;
this.parallax.bottomLayer = 0;
this.parallax.topLayer = new Element("div", {
styles: {
position: "absolute",
width: this.options.parallaxLayerSize,
height: 61,
zIndex: 1
}
}).inject(this.container);
/* this.parallax.fx = new Fx.Morph(this.parallax.topLayer, {
transition: Fx.Transitions.Sine.easeInOut
});
*/
this.setTheme();
window.addEvent("resize", function() {
this.width = this.container.getSize().x;
}.bind(this));
},
setSprite: function(lem) {
var url = (lem.exploding)
? "http://fragged.org/wp-content/themes/envy/images/lemming_explode_s.gif"
: "http://fragged.org/wp-content/themes/envy/images/lemming_walk_" +lem.direction+".gif"
lem.el.setStyles({
marginLeft: lem.pos,
background: "url("+url+") no-repeat 0 0"
});
},
addLemming: function(direction, pos, step) {
var lem = {
el: new Element("div", {
styles: {
backgroundRepeat: "no-repeat",
width: this.options.spriteWidth,
height: this.options.spriteHeight,
position: "absolute",
float: "left",
marginTop: this.options.marginTop,
zIndex: this.options.spriteZindex
}
}),
direction: direction || "r",
pos: pos || 0,
step: step || 1
};
lem.el.inject(this.container);
lem.el.addEvents({
mouseenter: function() {
lem.el.store("step", lem.step);
// experimental.
/*
lem.step++;
lem.direction = lem.direction == "r" ? "l" : "r";
this.setSprite(lem);
this.lemmingChat(lem);
return;*/
// end experimental
lem.step = 0;
this.lemmingChat(lem);
}.bind(this),
mouseleave: function() {
// experimental
/*
lem.step = lem.el.retrieve("step");
var chat = lem.el.getFirst();
chat.fade(0);
return;
*/
// end experimental
lem.direction = lem.direction == "r" ? "l" : "r";
this.setSprite(lem);
lem.step = lem.el.retrieve("step");
var chat = lem.el.getFirst();
chat.fade(0);
}.bind(this),
click: function() {
this.explodeLemming(lem);
}.bind(this)
});
this.setSprite(lem);
this.lemmings.push(lem);
},
lemmingChat: function(lem, what) {
what = what || ["?","!", "#", "@", "no"].getRandom();
lem.el.empty();
new Element("div", {
"class": "lemChat",
html: ""+what+"
",
opacity: 0
}).inject(lem.el).fade(1);
},
moveLemmings: function() {
this.timer = this.drawWorld.periodical(this.options.drawSpeed, this);
// this.startParallax();
},
explodeLemming: function(lem) {
lem.exploding = true;
this.setSprite(lem);
lem.el.removeEvents();
(function() {
lem.el.fade(0);
}).delay(1000, this);
return;
(function() {
this.addLemming("r", $random(0, this.width - this.options.spriteWidth), lem.step);
}).delay(3000, this);
},
firework: function(x, y) {
this.num_firework++;
var i = this.fireworks.length;
var colors = ['#ffffff','#ffff00','#00ff00','#ff0000','#0000ff','#ff00ff','#00ffff'], fw = [];
var j = 20, f, a, s;
while(j--) {
a = Math.random() * 6.294;
s = (Math.random() >.6) ? 4 : Math.random() * 4;
f = new Element("div", {
"class": "spark",
styles: {
background: colors[Math.floor(Math.random()*7)],
top: y,
left: x
},
y: y,
x: x,
dx: s * Math.sin(a),
dy: s * Math.cos(a) - 4
}).inject(this.container);
fw[j] = f;
}
this.fireworks.push(fw);
this.running = false;
this.runFirework(fw);
},
runFirework: function(fw) {
fw.each(function(f) {
if ($type(f) !== "element")
return;
var y = f.get("y").toFloat(), x = f.get("x").toFloat(), dx = f.get("dx").toFloat(), dy = f.get("dy").toFloat();
// console.log(y, x, dy, dx);
y += dy += 0.18;
x += dx;
f.set({
y: y,
x: x
});
var op = f.getStyle("opacity").toFloat() - .05;
if (y < this.coords.top || y > this.coords.bottom || x < this.coords.left || x > this.coords.right || op <= 0) {
// console.info("disposing of ", f, y);
f.dispose();
fw.erase(f);
}
else {
f.set({
styles: {
top: y,
left: x
},
opacity: op
});
}
}, this);
if (fw.length)
(function() {
this.runFirework(fw);
}).delay(this.options.drawSpeed, this);
},
/*
startParallax: function() {
this.parallax.bounds = {
start: Math.round(this.width / 3 - 20)
};
this.parallax.bounds.end = Math.round(this.parallax.bounds.start + 140);
this.parallax.fx.setOptions({
duration: this.options.drawSpeed * this.width / this.lemmings[0].step
});
console.log(this.options.drawSpeed * this.width / this.lemmings[0].step);
console.log([((this.lemmings[0].direction == "r") ? this.parallax.bounds.start : this.parallax.bounds.end) + "px 0", ((this.lemmings[0].direction == "r") ? this.parallax.bounds.end : this.parallax.bounds.start) + "px 0"]);
this.parallax.fx.start({
"background-position": [((this.lemmings[0].direction == "r") ? this.parallax.bounds.start : this.parallax.bounds.end) + "px 0", ((this.lemmings[0].direction == "r") ? this.parallax.bounds.end : this.parallax.bounds.start) + "px 0"]
});
},*/
drawWorld: function() {
this.lemmings.each(function(lem, i) {
// is the lemming exploding?
if (lem.exploding) {
var spriteOffset = lem.el.getStyle("backgroundPosition") || 0, spriteOffsetX = spriteOffset.split(" ")[0].toInt();
var newOffset = (spriteOffsetX - this.options.spriteWidth <= this.options.spriteExplodeContainerWidth.neg()) ? 0 : spriteOffsetX - this.options.spriteWidth;
if (newOffset == 0 && spriteOffsetX != 0) {
var coords = lem.el.getPosition();
this.firework(coords.x + this.options.spriteWidth / 2, coords.y + this.options.spriteHeight / 2);
this.lemmings[i].exploding = false;
return;
}
lem.el.setStyles({
backgroundPosition: newOffset + "px 0px"
});
return;
}
// walking animation...
var spriteOffset = lem.el.getStyle("backgroundPosition") || 0, spriteOffsetX = spriteOffset.split(" ")[0].toInt();
var newOffset = (spriteOffsetX - this.options.spriteWidth <= this.options.spriteContainerWidth.neg()) ? 0 : spriteOffsetX - this.options.spriteWidth;
if (lem.pos > this.width - this.options.spriteWidth)
lem.pos = this.width - this.options.spriteWidth;
// moving on-screen
if (lem.direction == "r" && lem.step != 0) {
this.parallax.bottomLayer++;
if (lem.pos + this.options.spriteWidth < this.width) {
var nl = lem.pos + lem.step;
// actually move the sprite here
lem.el.setStyles({
marginLeft: nl,
backgroundPosition: newOffset + "px 0px"
});
this.lemmings[i].pos = nl;
}
else {
this.fireEvent("turn", "left");
this.lemmings[i].direction = "l";
this.setSprite(lem);
// console.log("turning...");
}
}
else if (lem.step != 0) {
// console.log(pos.left);
var newOffset = (spriteOffsetX - this.options.spriteWidth <= this.options.spriteContainerWidth.neg()) ? 0 : spriteOffsetX - this.options.spriteWidth;
this.parallax.bottomLayer--;
if (lem.pos > 0) {
var nl = lem.pos - lem.step;
// actually move the sprite here
lem.el.setStyles({
marginLeft: nl,
backgroundPosition: newOffset + "px 0px"
});
this.lemmings[i].pos = nl;
}
else {
this.fireEvent("turn", "right");
this.lemmings[i].direction = "r";
this.setSprite(lem);
}
}
// parallax effect on background
this.container.setStyle("background-position", this.parallax.bottomLayer + "px 0");
// progress is relational to sun.
if (nl != 0)
var progress = (this.width / 2) - nl / 2;
if (!isNaN(progress))
this.parallax.topLayer.setStyle("background-position", progress +"px 0");
var hours = new Date().get("hr");
// auto swap between themes
if (this.options.daylightDetect) {
if (hours > 8 && hours < 16 && this.theme != "D") {
// 8am, daylight has come!
this.theme = "D";
this.changeDaylight();
}
if (hours < 8 && hours > 16 && this.theme == "D") {
this.theme = "";
this.changeDaylight();
}
};
}, this);
},
changeDaylight: function() {
if (Browser.Engine.trident)
this.parallax.topLayer.setOpacity(0);
this.container.fade(.3);
(function() {
this.setTheme();
}).delay(300, this);
(function() {
this.container.fade(1);
}).delay(600, this);
if (Browser.Engine.trident)
(function() {
this.parallax.topLayer.fade(1);
}).delay(900, this);
}
});
Number.implement({
neg: function() {
return this * -1;
}
});