145 lines
4.8 KiB
HTML
145 lines
4.8 KiB
HTML
<!doctype html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<style>
|
|
html,body { margin:0; padding:0; overflow:hidden; background:transparent; }
|
|
canvas { display:block; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<canvas id="c"></canvas>
|
|
<script>
|
|
(function(){
|
|
function easing(name,t){
|
|
if(name==='easeIn') return t*t;
|
|
if(name==='easeOut') return 1-(1-t)*(1-t);
|
|
if(name==='easeInOut') return t<0.5 ? 2*t*t : 1-Math.pow(-2*t+2,2)/2;
|
|
return t;
|
|
}
|
|
function merge(b,p){ var o={}; for(var k in b) o[k]=b[k]; if(p) for(var k2 in p) o[k2]=p[k2]; return o; }
|
|
function lerp(a,b,t){ return a+(b-a)*t; }
|
|
function parseHex(h){
|
|
if(!h) return null;
|
|
var s=String(h).trim().replace('#','');
|
|
if(s.length===3) s=s[0]+s[0]+s[1]+s[1]+s[2]+s[2];
|
|
if(s.length<6) return null;
|
|
var n=parseInt(s.slice(0,6),16);
|
|
if(isNaN(n)) return null;
|
|
return [(n>>16)&255,(n>>8)&255,n&255];
|
|
}
|
|
function formatHex(r,g,b){
|
|
function c(v){ v=Math.round(v); if(v<0)v=0; if(v>255)v=255; var s=v.toString(16); return s.length<2?'0'+s:s; }
|
|
return '#'+c(r)+c(g)+c(b);
|
|
}
|
|
function lerpColor(a,b,t){
|
|
var pa=parseHex(a), pb=parseHex(b);
|
|
if(!pa||!pb) return t<0.5?a:b;
|
|
return formatHex(lerp(pa[0],pb[0],t), lerp(pa[1],pb[1],t), lerp(pa[2],pb[2],t));
|
|
}
|
|
function lerpProps(a,b,t){
|
|
var o={
|
|
x:lerp(a.x,b.x,t),
|
|
y:lerp(a.y,b.y,t),
|
|
scaleX:lerp(a.scaleX,b.scaleX,t),
|
|
scaleY:lerp(a.scaleY,b.scaleY,t),
|
|
rotation:lerp(a.rotation,b.rotation,t),
|
|
opacity:lerp(a.opacity,b.opacity,t)
|
|
};
|
|
if(a.fill && b.fill) o.fill=lerpColor(a.fill,b.fill,t);
|
|
else if(b.fill) o.fill=b.fill;
|
|
else if(a.fill) o.fill=a.fill;
|
|
return o;
|
|
}
|
|
function sample(layer,time){
|
|
var kfs=layer.keyframes.slice().sort(function(a,b){return a.time-b.time;});
|
|
if(!kfs.length) return merge(layer.base);
|
|
if(time<=kfs[0].time) return merge(layer.base,kfs[0].props);
|
|
var last=kfs[kfs.length-1];
|
|
if(time>=last.time) return merge(layer.base,last.props);
|
|
for(var i=0;i<kfs.length-1;i++){
|
|
var a=kfs[i], b=kfs[i+1];
|
|
if(time>=a.time && time<=b.time){
|
|
var raw=(time-a.time)/(b.time-a.time);
|
|
var t=easing(b.easing,raw);
|
|
return lerpProps(merge(layer.base,a.props), merge(layer.base,b.props), t);
|
|
}
|
|
}
|
|
return merge(layer.base);
|
|
}
|
|
function loadImages(assets){
|
|
var imgs={};
|
|
var list=assets.filter(function(a){return a.kind==='image';});
|
|
return Promise.all(list.map(function(a){
|
|
return new Promise(function(res){
|
|
var img=new Image();
|
|
img.onload=function(){ imgs[a.id]=img; res(); };
|
|
img.onerror=function(){ res(); };
|
|
img.src=a.src;
|
|
});
|
|
})).then(function(){ return imgs; });
|
|
}
|
|
var canvas=document.getElementById('c');
|
|
var ctx=canvas.getContext('2d');
|
|
var scene=null, images={};
|
|
function drawFrame(time){
|
|
if(!scene) return;
|
|
var lw=scene.canvas.width, lh=scene.canvas.height;
|
|
ctx.save();
|
|
ctx.beginPath(); ctx.rect(0,0,lw,lh); ctx.clip();
|
|
ctx.fillStyle=scene.canvas.background;
|
|
ctx.fillRect(0,0,lw,lh);
|
|
var am={}; for(var i=0;i<scene.assets.length;i++) am[scene.assets[i].id]=scene.assets[i];
|
|
for(var j=0;j<scene.layers.length;j++){
|
|
var layer=scene.layers[j]; if(!layer.visible) continue;
|
|
var asset=am[layer.assetId]; if(!asset) continue;
|
|
var p=sample(layer,time);
|
|
ctx.save();
|
|
ctx.globalAlpha = ctx.globalAlpha * p.opacity;
|
|
ctx.translate(p.x,p.y);
|
|
ctx.rotate(p.rotation*Math.PI/180);
|
|
ctx.scale(p.scaleX,p.scaleY);
|
|
var fill = p.fill || asset.fill;
|
|
if(asset.kind==='rect'){
|
|
var rOffX=layer.offsetX!=null?layer.offsetX:asset.width/2;
|
|
var rOffY=layer.offsetY!=null?layer.offsetY:asset.height/2;
|
|
ctx.fillStyle=fill;
|
|
ctx.fillRect(-rOffX,-rOffY,asset.width,asset.height);
|
|
} else if(asset.kind==='circle'){
|
|
ctx.fillStyle=fill;
|
|
ctx.beginPath(); ctx.arc(0,0,asset.radius,0,Math.PI*2); ctx.fill();
|
|
} else if(asset.kind==='text'){
|
|
ctx.fillStyle=fill;
|
|
ctx.font=asset.fontSize+'px sans-serif';
|
|
ctx.textBaseline='top';
|
|
ctx.fillText(asset.text,0,0);
|
|
} else if(asset.kind==='image'){
|
|
var img=images[asset.id];
|
|
if(img){
|
|
var iOffX=layer.offsetX!=null?layer.offsetX:asset.width/2;
|
|
var iOffY=layer.offsetY!=null?layer.offsetY:asset.height/2;
|
|
ctx.drawImage(img,-iOffX,-iOffY,asset.width,asset.height);
|
|
}
|
|
}
|
|
ctx.restore();
|
|
}
|
|
ctx.restore();
|
|
}
|
|
window.__animatrInit=function(s){
|
|
scene=s;
|
|
canvas.width=s.canvas.width;
|
|
canvas.height=s.canvas.height;
|
|
document.body.style.width=s.canvas.width+'px';
|
|
document.body.style.height=s.canvas.height+'px';
|
|
return loadImages(s.assets).then(function(imgs){
|
|
images=imgs;
|
|
drawFrame(0);
|
|
return true;
|
|
});
|
|
};
|
|
window.__animatrSeek=function(time){ drawFrame(time); };
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|