bak
This commit is contained in:
1 parent
cc6b11e61d
commit
b977ee35a8
11 files changed
+1641
-310
No files matched your search
+81
-37
@@ -89,6 +89,19 @@ const ENGINE_SOURCE = `(function(){
|
||||
var kfs=scene.layers[i].keyframes;
|
||||
for(var j=0;j<kfs.length;j++){ if(kfs[j].time>max) max=kfs[j].time; }
|
||||
}
|
||||
var instances=scene.clipInstances||[];
|
||||
var clips=scene.clips||[];
|
||||
var clipById={};
|
||||
for(var ci=0;ci<clips.length;ci++) clipById[clips[ci].id]=clips[ci];
|
||||
for(var ii=0;ii<instances.length;ii++){
|
||||
var inst=instances[ii];
|
||||
var cl=clipById[inst.clipId];
|
||||
if(!cl) continue;
|
||||
var trimS=inst.trimStart||0;
|
||||
var trimE=inst.trimEnd||0;
|
||||
var end=inst.startTime+Math.max(0,cl.duration-trimS-trimE);
|
||||
if(end>max) max=end;
|
||||
}
|
||||
if(max<=0) return scene.duration;
|
||||
return Math.min(max,scene.duration);
|
||||
}
|
||||
@@ -127,6 +140,44 @@ const ENGINE_SOURCE = `(function(){
|
||||
draw(t);
|
||||
requestAnimationFrame(frame);
|
||||
}
|
||||
function drawLayer(layer,time){
|
||||
var asset=assetMap[layer.assetId];
|
||||
if(!asset||!layer.visible)return;
|
||||
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);
|
||||
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=p.fill||asset.fill;
|
||||
ctx.fillRect(-rOffX,-rOffY,asset.width,asset.height);
|
||||
}else if(asset.kind==='circle'){
|
||||
var cOffX=layer.offsetX!=null?layer.offsetX:0;
|
||||
var cOffY=layer.offsetY!=null?layer.offsetY:0;
|
||||
ctx.fillStyle=p.fill||asset.fill;
|
||||
ctx.beginPath();
|
||||
ctx.arc(-cOffX,-cOffY,asset.radius,0,Math.PI*2);
|
||||
ctx.fill();
|
||||
}else if(asset.kind==='text'){
|
||||
var tOffX=layer.offsetX!=null?layer.offsetX:0;
|
||||
var tOffY=layer.offsetY!=null?layer.offsetY:0;
|
||||
ctx.fillStyle=p.fill||asset.fill;
|
||||
ctx.font=asset.fontSize+'px sans-serif';
|
||||
ctx.textBaseline='top';
|
||||
ctx.fillText(asset.text,-tOffX,-tOffY);
|
||||
}else if(asset.kind==='image'){
|
||||
var img=imgs[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();
|
||||
}
|
||||
function draw(time){
|
||||
var lw=scene.canvas.width,lh=scene.canvas.height;
|
||||
var cw=size.w,ch=size.h;
|
||||
@@ -144,45 +195,38 @@ const ENGINE_SOURCE = `(function(){
|
||||
ctx.clip();
|
||||
ctx.fillStyle=scene.canvas.background;
|
||||
ctx.fillRect(0,0,lw,lh);
|
||||
for(var i=scene.layers.length-1;i>=0;i--){
|
||||
var layer=scene.layers[i];
|
||||
if(!layer.visible)continue;
|
||||
var asset=assetMap[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);
|
||||
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=p.fill||asset.fill;
|
||||
ctx.fillRect(-rOffX,-rOffY,asset.width,asset.height);
|
||||
}else if(asset.kind==='circle'){
|
||||
var cOffX=layer.offsetX!=null?layer.offsetX:0;
|
||||
var cOffY=layer.offsetY!=null?layer.offsetY:0;
|
||||
ctx.fillStyle=p.fill||asset.fill;
|
||||
ctx.beginPath();
|
||||
ctx.arc(-cOffX,-cOffY,asset.radius,0,Math.PI*2);
|
||||
ctx.fill();
|
||||
}else if(asset.kind==='text'){
|
||||
var tOffX=layer.offsetX!=null?layer.offsetX:0;
|
||||
var tOffY=layer.offsetY!=null?layer.offsetY:0;
|
||||
ctx.fillStyle=p.fill||asset.fill;
|
||||
ctx.font=asset.fontSize+'px sans-serif';
|
||||
ctx.textBaseline='top';
|
||||
ctx.fillText(asset.text,-tOffX,-tOffY);
|
||||
}else if(asset.kind==='image'){
|
||||
var img=imgs[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);
|
||||
var layerById={};
|
||||
for(var li=0;li<scene.layers.length;li++)layerById[scene.layers[li].id]=scene.layers[li];
|
||||
var clipById2={};
|
||||
var clips2=scene.clips||[];
|
||||
for(var ci2=0;ci2<clips2.length;ci2++)clipById2[clips2[ci2].id]=clips2[ci2];
|
||||
var instByTrack={};
|
||||
var instances=scene.clipInstances||[];
|
||||
for(var iInst=0;iInst<instances.length;iInst++){
|
||||
var iE=instances[iInst];
|
||||
(instByTrack[iE.clipTrackId]=instByTrack[iE.clipTrackId]||[]).push(iE);
|
||||
}
|
||||
var order=scene.trackOrder||[];
|
||||
if(!order.length){for(var lj=0;lj<scene.layers.length;lj++)order.push(scene.layers[lj].id);}
|
||||
for(var oi=order.length-1;oi>=0;oi--){
|
||||
var id=order[oi];
|
||||
var lyr=layerById[id];
|
||||
if(lyr){ drawLayer(lyr,time); continue; }
|
||||
var tInsts=instByTrack[id]||[];
|
||||
for(var ti=0;ti<tInsts.length;ti++){
|
||||
var inst2=tInsts[ti];
|
||||
var clip2=clipById2[inst2.clipId];
|
||||
if(!clip2)continue;
|
||||
var tS=inst2.trimStart||0;
|
||||
var tE=inst2.trimEnd||0;
|
||||
var visible=Math.max(0,clip2.duration-tS-tE);
|
||||
var offset=time-inst2.startTime;
|
||||
if(offset<0||offset>visible)continue;
|
||||
var localT=tS+offset;
|
||||
for(var cl2=clip2.layers.length-1;cl2>=0;cl2--){
|
||||
drawLayer(clip2.layers[cl2],localT);
|
||||
}
|
||||
}
|
||||
ctx.restore();
|
||||
}
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
+59
-19
@@ -1,4 +1,4 @@
|
||||
import type { Asset, Scene } from '@/types/scene'
|
||||
import type { Asset, Layer, Scene } from '@/types/scene'
|
||||
import { sampleLayer } from './interpolate'
|
||||
|
||||
export async function loadSceneImages(scene: Scene): Promise<Record<string, HTMLImageElement>> {
|
||||
@@ -24,27 +24,15 @@ export async function loadSceneImages(scene: Scene): Promise<Record<string, HTML
|
||||
return result
|
||||
}
|
||||
|
||||
export function drawSceneFrame(
|
||||
function drawLayers(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
scene: Scene,
|
||||
time: number,
|
||||
layers: Layer[],
|
||||
assetMap: Map<string, Asset>,
|
||||
images: Record<string, HTMLImageElement>,
|
||||
time: number,
|
||||
) {
|
||||
const lw = scene.canvas.width
|
||||
const 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)
|
||||
|
||||
const assetMap = new Map<string, Asset>()
|
||||
for (const a of scene.assets) assetMap.set(a.id, a)
|
||||
|
||||
for (let i = scene.layers.length - 1; i >= 0; i--) {
|
||||
const layer = scene.layers[i]
|
||||
for (let i = layers.length - 1; i >= 0; i--) {
|
||||
const layer = layers[i]
|
||||
if (!layer.visible) continue
|
||||
const asset = assetMap.get(layer.assetId)
|
||||
if (!asset) continue
|
||||
@@ -86,6 +74,58 @@ export function drawSceneFrame(
|
||||
|
||||
ctx.restore()
|
||||
}
|
||||
}
|
||||
|
||||
export function drawSceneFrame(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
scene: Scene,
|
||||
time: number,
|
||||
images: Record<string, HTMLImageElement>,
|
||||
) {
|
||||
const lw = scene.canvas.width
|
||||
const 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)
|
||||
|
||||
const assetMap = new Map<string, Asset>()
|
||||
for (const a of scene.assets) assetMap.set(a.id, a)
|
||||
|
||||
const layerById = new Map<string, Layer>()
|
||||
for (const l of scene.layers) layerById.set(l.id, l)
|
||||
const instancesByTrack = new Map<string, typeof scene.clipInstances>()
|
||||
for (const ci of scene.clipInstances ?? []) {
|
||||
const arr = instancesByTrack.get(ci.clipTrackId) ?? []
|
||||
arr.push(ci)
|
||||
instancesByTrack.set(ci.clipTrackId, arr)
|
||||
}
|
||||
const clipById = new Map<string, NonNullable<typeof scene.clips>[number]>()
|
||||
for (const c of scene.clips ?? []) clipById.set(c.id, c)
|
||||
|
||||
const order = scene.trackOrder ?? scene.layers.map((l) => l.id)
|
||||
for (let i = order.length - 1; i >= 0; i--) {
|
||||
const id = order[i]
|
||||
const layer = layerById.get(id)
|
||||
if (layer) {
|
||||
drawLayers(ctx, [layer], assetMap, images, time)
|
||||
continue
|
||||
}
|
||||
const instances = instancesByTrack.get(id) ?? []
|
||||
for (const inst of instances) {
|
||||
const clip = clipById.get(inst.clipId)
|
||||
if (!clip) continue
|
||||
const trimStart = inst.trimStart ?? 0
|
||||
const trimEnd = inst.trimEnd ?? 0
|
||||
const visible = Math.max(0, clip.duration - trimStart - trimEnd)
|
||||
const offsetIntoBlock = time - inst.startTime
|
||||
if (offsetIntoBlock < 0 || offsetIntoBlock > visible) continue
|
||||
drawLayers(ctx, clip.layers, assetMap, images, trimStart + offsetIntoBlock)
|
||||
}
|
||||
}
|
||||
|
||||
ctx.restore()
|
||||
}
|
||||
Reference in new issue
Block a user