-
Notifications
You must be signed in to change notification settings - Fork 0
tick&draw
何波 edited this page Feb 22, 2019
·
14 revisions
每次tick更新Stage.prototype.update()方法
let stage = new createjs.Stage("canvasElementId");
let image = new createjs.Bitmap("imagePath.png");
stage.addChild(image);
createjs.Ticker.addEventListener("tick", handleTick);
function handleTick(event) {
image.x += 10;
stage.update();
}Stage源码分析
p.update = function(props) {
if (!this.canvas) { return; }
// 执行tick方法
if (this.tickOnUpdate) { this.tick(props); }
if (this.dispatchEvent("drawstart", false, true) === false) { return; }
createjs.DisplayObject._snapToPixelEnabled = this.snapToPixelEnabled;
var r = this.drawRect, ctx = this.canvas.getContext("2d");
ctx.setTransform(1, 0, 0, 1, 0, 0);
if (this.autoClear) {
if (r) { ctx.clearRect(r.x, r.y, r.width, r.height); }
else { ctx.clearRect(0, 0, this.canvas.width+1, this.canvas.height+1); }
}
ctx.save();
if (this.drawRect) {
ctx.beginPath();
ctx.rect(r.x, r.y, r.width, r.height);
ctx.clip();
}
// 本身没有updateContext方法,执行父类Container的父类DisplayObject的updateContext方法
this.updateContext(ctx);
// 执行父类Container的draw方法
this.draw(ctx, false);
ctx.restore();
this.dispatchEvent("drawend");
};
// 在display list当中自动调用,除非TickOnUpdate设置为false,否则`update`方法将自动调用此函数。
// 如果将props对象传递给tick(),则它的所有属性都将复制到传播到侦听器的事件对象。
// easeljs中的一些基于时间的特性(例如sprite/framerate)要求将tick事件对象(或具有delta属性的等效对象)作为props参数传递给tick()
p.tick = function(props) {
if (!this.tickEnabled || this.dispatchEvent("tickstart", false, true) === false) { return; }
var evtObj = new createjs.Event("tick");
if (props) {
for (var n in props) {
if (props.hasOwnProperty(n)) { evtObj[n] = props[n]; }
}
}
// 注意,Stage自身无_tick方法,这里会调用父类Container的_tick方法。
this._tick(evtObj);
this.dispatchEvent("tickend");
};p.draw = function(ctx, ignoreCache) {
if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; }
// this ensures we don't have issues with display list changes that occur during a draw:
var list = this.children.slice(); // 复制this.children数组
// 这里为什么只循环Container中的一层子元素,因为整个Stage就是多个Container互相包裹而成,就是Stage->Container->Container->Shape等
for (var i=0,l=list.length; i<l; i++) {
var child = list[i];
if (!child.isVisible()) { continue; }
// draw the child:
ctx.save();
child.updateContext(ctx);
// 遍历子元素,调用子元素的draw方法,子元素如果是Shape或Bitmap,则调用它们自身的draw方法
child.draw(ctx);
ctx.restore();
}
return true;
};
// evtObj事件对象在所有的tick事件中都会触发,evtObj对象在dispatchers中重复使用,以减少构造和垃圾回收的花费。
// 此方法在Stage的tick方法中被调用
p._tick = function(evtObj) {
if (this.tickChildren) {
for (var i=this.children.length-1; i>=0; i--) {
var child = this.children[i];
if (child.tickEnabled && child._tick) { child._tick(evtObj); }
}
}
// 调用DisplayObject的_tick方法
this.DisplayObject__tick(evtObj);
};// 将显示对象绘制到指定的上下文中,忽略其可见visible、alpha、阴影shadow和转换transform。
// 如果draw方法调用返回true,(对于重写功能很有用)
// 注:此方法主要用于内部使用,但可能用于高级用途。
p.draw = function(ctx, ignoreCache) {
var cache = this.bitmapCache;
if(cache && !ignoreCache) {
return cache.draw(ctx);
}
return false;
};
// 将此显示对象的转换、alpha、globalcompositeoperation、剪切路径 clipping path(mask)和阴影shadow应用于**指定的上下文**。
// 此方法通常在`DisplayObject.draw()`方法之前调用
p.updateContext = function(ctx) {
var o=this, mask=o.mask, mtx= o._props.matrix;
if (mask && mask.graphics && !mask.graphics.isEmpty()) {
mask.getMatrix(mtx);
ctx.transform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty);
mask.graphics.drawAsPath(ctx);
ctx.clip();
mtx.invert();
ctx.transform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty);
}
this.getMatrix(mtx);
var tx = mtx.tx, ty = mtx.ty;
if (DisplayObject._snapToPixelEnabled && o.snapToPixel) {
tx = tx + (tx < 0 ? -0.5 : 0.5) | 0;
ty = ty + (ty < 0 ? -0.5 : 0.5) | 0;
}
ctx.transform(mtx.a, mtx.b, mtx.c, mtx.d, tx, ty);
ctx.globalAlpha *= o.alpha;
if (o.compositeOperation) { ctx.globalCompositeOperation = o.compositeOperation; }
if (o.shadow) { this._applyShadow(ctx, o.shadow); }
};
p._tick = function(evtObj) {
// because tick can be really performance sensitive, check for listeners before calling dispatchEvent.
var ls = this._listeners;
if (ls && ls["tick"]) {
// reset & reuse the event object to avoid construction / GC costs:
evtObj.target = null;
evtObj.propagationStopped = evtObj.immediatePropagationStopped = false;
this.dispatchEvent(evtObj);
}
};