Skip to content

Instantly share code, notes, and snippets.

@guileen
Created September 14, 2014 16:55
Show Gist options
  • Save guileen/4b388e56068d9c3d0594 to your computer and use it in GitHub Desktop.
Save guileen/4b388e56068d9c3d0594 to your computer and use it in GitHub Desktop.
Another stage js from some guy.
/**
* 全局变量
*/
var AG_debug = false;
var AG_time = 0;
var AG_startTime = 0;
var AG_running = false;
var AG_stages = [];
var AG_bufCanvas = null;
var AG_bufCtx = null;
/**
* 常量
*/
var AC_TWEEN_X = 0;// 缓动类型,位置x
var AC_TWEEN_Y = 1;// 缓动类型,位置y
var AC_TWEEN_ROTATE = 2;// 缓动类型,旋转角度
var AC_TWEEN_SCALEX = 3;// 缓动类型,x轴比例
var AC_TWEEN_SCALEY = 4;// 缓动类型,y轴比例
var AC_TWEEN_ALPHA = 5;// 缓动类型,透明度
var AC_MATH_PI_2 = Math.PI * 2;// 弧度(相当于角度的360度)
// ///////
var AC_FOREVER = 864000000;// 用10天的时间作为最大时间常量,用于定义持续显示的帧
var AC_IMG_ERROR;
/**
*
* 解决requestAnimationFrame兼容性问题,使不支持它的浏览器也能兼容
*
*/
(function() {
window.requestAnimationFrame = window.requestAnimationFrame
|| window.mozRequestAnimationFrame
|| window.webkitRequestAnimationFrame
|| window.msRequestAnimationFrame || window.oRequestAnimationFrame;
window.cancelAnimationFrame = window.cancelAnimationFrame
|| window.mozCancelAnimationFrame
|| window.webkitCancelAnimationFrame
|| window.msCancelAnimationFrame || window.oCancelAnimationFrame;
var time = 0;
// debug模式下,默认使用自定义的requestAnimationFrame
if (!window.requestAnimationFrame || AG_debug) {
window.requestAnimationFrame = function(callback, element) {
var current = Date.now();
var spend = current - time;
time = current;
// 最多25毫秒一次,每秒40帧,一般浏览器每秒60次
// 等待时间必须是1到25之间的值
var wt = 25 - spend;
wt = Math.max(wt, 1);
wt = Math.min(wt, 25);
time += wt;
var handle = window.setTimeout(callback, wt);
return handle;
};
}
if (!window.cancelAnimationFrame || AG_debug) {
window.cancelAnimationFrame = function(handle) {
clearTimeout(handle);
};
}
// 构造加载失败时的默认图片
var tCanvas = document.createElement("canvas");
var tCtx = tCanvas.getContext("2d");
tCanvas.width = 32;
tCanvas.height = 32;
tCtx.beginPath();
tCtx.lineWidth = 8;
tCtx.strokeStyle = "lightgray";
tCtx.arc(16, 16, 8, 0, AC_MATH_PI_2);
tCtx.closePath();
tCtx.stroke();
tCtx.beginPath();
tCtx.strokeStyle = "gray";
tCtx.arc(16, 16, 8, 0, AC_MATH_PI_2 * .5);
tCtx.stroke();
AC_IMG_ERROR = new Image();
AC_IMG_ERROR.src = tCanvas.toDataURL("image/png");
}());
function loadImages(srcs, callback) {
var count = srcs.length;
var loaded = 0;// 已经加载的数量(不论成功失败)
var imgs = new Array(count);
for (var i = 0, l = srcs.length; i < l; i++) {
imgs[i] = new Image();
imgs[i].onload = function() {
if (++loaded >= count) {
// 全部成功
callback(imgs);
}
};
imgs[i].onerror = function() {
this.onerror = function() {
// 已经重试过,使用默认的错误图片
imgs[i] = AC_IMG_ERROR;
if (++loaded >= count) {
callback(imgs);
}
};
// 重试一次,理论上有点用
this.src = this.src;
};
imgs[i].src = srcs[i];
}
}
/**
* 缓动算法
*
* @param t,当前时间
* @param b,初始值
* @param c,变化总量
* @param d,持续时间
*/
function EasingLinear(t, b, c, d) {
return c * t / d + b;
};
function EasingQuadIn(t, b, c, d) {
return c * (t /= d) * t + b;
};
function EasingQuadOut(t, b, c, d) {
return -c * (t /= d) * (t - 2) + b;
};
function EasingQuadInOut(t, b, c, d) {
if ((t /= d / 2) < 1)
return c / 2 * t * t + b;
return -c / 2 * ((--t) * (t - 2) - 1) + b;
};
function EasingCubicInfunction(t, b, c, d) {
return c * (t /= d) * t * t + b;
};
function EasingCubicOut(t, b, c, d) {
return c * ((t = t / d - 1) * t * t + 1) + b;
};
function EasingCubicInOut(t, b, c, d) {
if ((t /= d / 2) < 1)
return c / 2 * t * t * t + b;
return c / 2 * ((t -= 2) * t * t + 2) + b;
};
function EasingQuartIn(t, b, c, d) {
return c * (t /= d) * t * t * t + b;
};
function EasingQuartOut(t, b, c, d) {
return -c * ((t = t / d - 1) * t * t * t - 1) + b;
};
function EasingQuartInOut(t, b, c, d) {
if ((t /= d / 2) < 1)
return c / 2 * t * t * t * t + b;
return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
};
function EasingQuartInOut(t, b, c, d) {
if ((t /= d / 2) < 1)
return c / 2 * t * t * t * t + b;
return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
};
function EasingQuintIn(t, b, c, d) {
return c * (t /= d) * t * t * t * t + b;
};
function EasingQuintOut(t, b, c, d) {
return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
};
function EasingQuintInOut(t, b, c, d) {
if ((t /= d / 2) < 1)
return c / 2 * t * t * t * t * t + b;
return c / 2 * ((t -= 2) * t * t * t * t + 2) + b;
};
function EasingSineIn(t, b, c, d) {
return -c * Math.cos(t / d * (Math.PI / 2)) + c + b;
};
function EasingSineOut(t, b, c, d) {
return c * Math.sin(t / d * (Math.PI / 2)) + b;
};
function EasingSineInOut(t, b, c, d) {
return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b;
};
function EasingStrongIn(t, b, c, d) {
return c * (t /= d) * t * t * t * t + b;
};
function EasingStrongOut(t, b, c, d) {
return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
};
function EasingStrongInOut(t, b, c, d) {
if ((t /= d / 2) < 1)
return c / 2 * t * t * t * t * t + b;
return c / 2 * ((t -= 2) * t * t * t * t + 2) + b;
};
function EasingExpoIn(t, b, c, d) {
return (t == 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b;
};
function EasingExpoOut(t, b, c, d) {
return (t == d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;
};
function EasingExpoInOut(t, b, c, d) {
if (t == 0)
return b;
if (t == d)
return b + c;
if ((t /= d / 2) < 1)
return c / 2 * Math.pow(2, 10 * (t - 1)) + b;
return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b;
};
function EasingCircIn(t, b, c, d) {
return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b;
};
function EasingCircOut(t, b, c, d) {
return c * Math.sqrt(1 - (t = t / d - 1) * t) + b;
};
function EasingCircInOut(t, b, c, d) {
if ((t /= d / 2) < 1)
return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b;
return c / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b;
};
function EasingElasticIn(t, b, c, d) {
var s;
if (t == 0)
return b;
if ((t /= d) == 1)
return b + c;
var p = d * .3;
var s = p / 4;
var a = c;
return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s)
* (2 * Math.PI) / p))
+ b;
};
function EasingElasticOut(t, b, c, d) {
var s;
if (t == 0)
return b;
if ((t /= d) == 1)
return b + c;
var p = d * .3;
var a = c;
var s = p / 4;
return (a * Math.pow(2, -10 * t)
* Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b);
};
function EasingElasticInOut(t, b, c, d) {
var s;
if (t == 0)
return b;
if ((t /= d / 2) == 2)
return b + c;
var p = d * (.3 * 1.5);
var a = c;
var s = p / 4;
if (t < 1)
return -.5
* (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s)
* (2 * Math.PI) / p)) + b;
return a * Math.pow(2, -10 * (t -= 1))
* Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
};
function EasingBackIn(t, b, c, d) {
var s = 1.70158;
return c * (t /= d) * t * ((s + 1) * t - s) + b;
};
function EasingBackOut(t, b, c, d) {
var s = 1.70158;
return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
};
function EasingBackInOut(t, b, c, d) {
var s = 1.70158;
if ((t /= d / 2) < 1)
return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
};
function EasingBounceIn(t, b, c, d) {
return c - EasingBounceOut(d - t, 0, c, d) + b;
};
function EasingBounceOut(t, b, c, d) {
if ((t /= d) < (1 / 2.75)) {
return c * (7.5625 * t * t) + b;
} else if (t < (2 / 2.75)) {
return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
} else if (t < (2.5 / 2.75)) {
return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
} else {
return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
}
};
function EasingBounceInOut(t, b, c, d) {
if (t < d / 2) {
return EasingBounceIn(t * 2, 0, c, d) * .5 + b;
} else {
return EasingBounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
}
};
/**
* 缓动类
*/
function Tween() {
var s = this;
s.from = [];
s.to = [];
s.easing = [];
s.tag = [];
s.tDelay = [];
s.tDuration = [];
s.tExtra = [];
s.tTotal = [];
s.x = 0;
s.y = 0;
s.rotate = 0;
s.scalex = 1;
s.scaley = 1;
s.alpha = 100;
s.length = 0;
s.totalTime = 0;
};
/**
* 添加缓动动画
*/
Tween.prototype.add = function($from, $to, $easing, $duration, $timeDelay,
$timeExtra, $tag) {
var s = this;
// 如果duration小于零则修正为零(持续)
var dur = $duration <= 0 ? AC_FOREVER : $duration;
s.from.push($from);
s.to.push($to);
s.easing.push($easing);
s.tag.push($tag);
s.tDelay.push($timeDelay);
s.tDuration.push(dur);// 修正后的持续时间
s.tExtra.push($timeExtra);
s.tTotal.push($timeDelay + $duration + $timeExtra);// 3个时间之和
s.length++;
// 取时间最大的
var maxTime = 0;
for (var i = 0, l = s.length; i < l; i++) {
maxTime = Math.max(maxTime, s.tTotal[i]);
}
s.totalTime = maxTime;
};
/**
* 移除缓动动画
*/
Tween.prototype.del = function($ind) {
var s = this;
s.from.splice($ind, 1);
s.to.splice($ind, 1);
s.easing.splice($ind, 1);
s.tag.splice($ind, 1);
s.tDelay.splice($ind, 1);
s.tDuration.splice($ind, 1);
s.tExtra.splice($ind, 1);
s.tTotal.splice($ind, 1);
s.length--;
// 取时间最大的
var maxTime = 0;
for (var i = 0, l = s.length; i < l; i++) {
maxTime = Math.max(maxTime, s.tTotal[i]);
}
s.totalTime = maxTime;
};
/**
* 更新全部缓动动画
*
* @param $time,相对上级容器的时间
*/
Tween.prototype.update = function($time) {
var s = this;
for (var i = 0, l = s.length; i < l; i++) {
var v;
if ($time < (s.tDelay[i])) {
v = s.from[i];
} else if ($time > (s.tDelay[i] + s.tDuration[i])) {
v = s.to[i];
} else {
v = s.easing[i]($time, s.from[i], s.to[i] - s.from[i],
s.tDuration[i]);
}
var tag = s.tag[i];
switch (tag) {
case AC_TWEEN_X:
s.x = v;
break;
case AC_TWEEN_Y:
s.y = v;
break;
case AC_TWEEN_ROTATE:
s.rotate = v;
break;
case AC_TWEEN_SCALEX:
s.scalex = v;
break;
case AC_TWEEN_SCALEY:
s.scaley = v;
break;
case AC_TWEEN_ALPHA:
s.alpha = v;
break;
}
}
};
/**
* Frame帧,(将来可能会支持更小的block级别)
*/
function Frame() {
var s = this;
s.src = [];
s.w = [];
s.h = [];
s.offx = []; // 默认相对src图片的偏移在左上角
s.offy = [];
s.anchorx = []; // 默认锚点在左上角
s.anchory = [];
s.tDuration = [];
s.tChange = [];
s.length = 0;
s.totalTime = 0;
s.curFrame = 0;
};
Frame.prototype.add = function($duration, $src, $w, $h, $offx, $offy, $anchorx,
$anchory) {
var s = this;
// 如果duration小于零则修正为零(持续)
var dur = $duration <= 0 ? AC_FOREVER : $duration;
s.src.push($src);
s.w.push($w);
s.h.push($h);
s.offx.push($offx);
s.offy.push($offy);
s.anchorx.push($anchorx);
s.anchory.push($anchory);
s.tDuration.push(dur);
s.length++;
s.totalTime += dur;
s.tChange.push(s.totalTime);
};
Frame.prototype.del = function($ind) {
var s = this;
s.totalTime -= s.duration[$ind];
s.src.splice($ind, 1);
s.w.splice($ind, 1);
s.h.splice($ind, 1);
s.offx.splice($ind, 1);
s.offy.splice($ind, 1);
s.anchorx.splice($ind, 1);
s.anchory.splice($ind, 1);
s.tDuration.splice($ind, 1);
s.length--;
var temp = 0;
// 清空,重新计算
s.tChange.splice(0, s.tChange.length);
for (var i = 0, l = s.tDuration.length; i < l; i++) {
temp += s.tDuration[i];
s.tChange.push(temp);
}
};
Frame.prototype.update = function($time) {
var s = this;
if ($time < 0) {
s.curFrame = 0;
} else if ($time > s.totalTime) {
s.curFrame = s.length - 1;
} else {
// 寻找并更新当前帧
var tempTime = 0;
var ds = s.tChange;
for (var i = 0, l = ds.length; i < l; i++) {
var d = ds[i];
if ($time <= ds[i]) {
s.curFrame = i;
break;
}
}
}
};
/**
* Animation动画类
*/
function Anim($x, $y, $repeat) {
var s = this;
s.x = $x;
s.y = $y;
s.frame = new Frame();
s.tween = new Tween();
s.repeat = $repeat;
s.curRepeat = $repeat;
s.totalTime = 0;
};
Anim.prototype.update = function($stage, $ctx) {
var s = this;
var t = $stage.time;
if (s.totalTime < AC_FOREVER && s.curRepeat > 0) {
s.curRepeat = s.repeat - Math.floor(t / s.totalTime);
if (s.curRepeat > 0) {
// 如果时间计算出repeat次数合法,则求余计算出当前循环的时间
t = t % s.totalTime;
}
}
// 更新frame和tween
s.tween.update(t);
s.frame.update(t);
// 根据frame和tween的值进行绘画
$ctx.save();
$ctx.beginPath();
var tween = s.tween;
var frame = s.frame;
var cf = frame.curFrame;
$ctx.scale(tween.scalex, tween.scaley);
$ctx.globalAlpha = tween.alpha;
$ctx.translate(s.x + tween.x, s.y + tween.y);
$ctx.rotate(tween.rotate);
var ax = frame.anchorx[cf];
var ay = frame.anchory[cf];
$ctx.rect(-ax, -ay, frame.w[cf], frame.h[cf]);
if (AG_debug) {
// 绘制红色调试框
$ctx.strokeStyle = "red";
$ctx.stroke();
}
$ctx.clip();
$ctx.drawImage(frame.src[cf], -ax - frame.offx[cf], -ay - frame.offy[cf]);
$ctx.closePath();
$ctx.restore();
};
Anim.prototype.addFrame = function($duration, $src, $w, $h, $offx, $offy,
$anchorx, $anchory) {
this.frame.add($duration, $src, $w, $h, $offx, $offy, $anchorx, $anchory);
// fix total time
this.totalTime = Math.max(this.frame.totalTime, this.tween.totalTime);
};
Anim.prototype.delFrame = function($ind) {
this.frame.del($ind);
// fix total time
this.totalTime = Math.max(this.frame.totalTime, this.tween.totalTime);
};
Anim.prototype.addTween = function($from, $to, $easing, $duration, $timeDelay,
$timeExtra, $tag) {
this.tween
.add($from, $to, $easing, $duration, $timeDelay, $timeExtra, $tag);
// fix total time
this.totalTime = Math.max(this.frame.totalTime, this.tween.totalTime);
};
Anim.prototype.delTween = function($ind) {
this.tween.del($ind);
// fix total time
this.totalTime = Math.max(this.frame.totalTime, this.tween.totalTime);
};
function Stage($canvas) {
var s = this;
s.canvas = $canvas;
s.ctx = s.canvas.getContext("2d");
s.width = $canvas.width;
s.height = $canvas.height;
s.cx = s.width / 2;
s.cy = s.height / 2;
s.time = 0;
s.startTime = 0;
s.running = false;
s.loading = false;
s.anims = [];
// 创建通用缓冲区
if (!AG_bufCanvas) {
AG_bufCanvas = document.createElement("canvas");
AG_bufCtx = AG_bufCanvas.getContext("2d");
}
// 取最大的canvas尺寸来作为公用缓冲区的尺寸
AG_bufCanvas.width = Math.max(AG_bufCanvas.width, s.canvas.width);
AG_bufCanvas.height = Math.max(AG_bufCanvas.height, s.canvas.height);
}
Stage.prototype.addAnim = function($anim) {
this.anims.push($anim);
};
Stage.prototype.updateBackground = function($ctx) {
// 默认绘制白色背景
$ctx.save();
$ctx.beginPath();
$ctx.fillStyle = "white";
$ctx.fillRect(0, 0, this.width, this.height);
$ctx.closePath();
$ctx.restore();
};
Stage.prototype.updateFrountground = function($ctx) {
// 空方法,等待重载
};
/**
* 移除缓动动画
*
* @param $ind
*/
Stage.prototype.delAnim = function($ind) {
this.anims.splice($ind, 1);
};
Stage.prototype.update = function() {
var s = this;
if (s.running) {
s.time = Date.now() - s.startTime;
var gbc = AG_bufCtx;
// 画背景
s.updateBackground(gbc);
// 画调试参考线
if (AG_debug) {
gbc.save();
gbc.beginPath();
gbc.strokeStyle = "red";
gbc.moveTo(128, 0);
gbc.lineTo(128, 640);
gbc.moveTo(0, 128);
gbc.lineTo(640, 128);
gbc.stroke();
gbc.closePath();
gbc.restore();
}
for (var i = 0, l = s.anims.length; i < l; i++) {
s.anims[i].update(s, gbc);
}
s.updateFrountground(gbc);
if (s.waiting) {
s.updateWait(gbc);
}
// 绘制缓冲屏幕
s.ctx.drawImage(AG_bufCanvas, 0, 0);
}
};
Stage.prototype.start = function() {
var s = this;
s.running = true;
s.startTime = Date.now();
s.index = AG_stages.push(s);
// 如果Runtime没有启动,则启动之
if (!AG_running) {
RuntimeStart();
}
};
Stage.prototype.stop = function() {
var s = this;
s.running = false;
AG_stages.splice(s.index - 1, 1);
// 如果已经没有Stage实例在运行,则停掉Runtime
if (AG_stages.length <= 0) {
RuntimeStop();
}
};
Stage.prototype.pause = function() {
var s = this;
if (s.running) {
s.running = true;
}
};
Stage.prototype.showWait = function() {
var s = this;
s.waiting = true;
};
Stage.prototype.hideWait = function() {
var s = this;
s.waiting = false;
};
Stage.prototype.updateWait = function($ctx) {
var s = this;
var r = 16;
var x = s.cx - r;
var y = s.cy - r;
$ctx.beginPath();
$ctx.lineWidth = 8;
$ctx.strokeStyle = "lightgray";
// 画圆
$ctx.arc(x, y, r, 0, AC_MATH_PI_2);
$ctx.closePath();
$ctx.stroke();
// 画转动弧
$ctx.beginPath();
$ctx.strokeStyle = "gray";
var angle = AC_MATH_PI_2 * (s.time % 1000) / 1000;
$ctx.arc(x, y, r, angle, angle + AC_MATH_PI_2 * .15);
$ctx.stroke();
};
function RuntimeUpdate() {
for (var i = 0, l = AG_stages.length; i < l; i++) {
AG_stages[i].update();
}
window.requestAnimationFrame(RuntimeUpdate);
}
function RuntimeStart() {
AG_running = true;
AG_startTime = Date.now();
RuntimeUpdate();
}
function RuntimeStop() {
AG_running = false;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment