value: Number.MAX_VALUE\r
},\r
\r
+ addBeginList: function (num) {\r
+ return ( this.beginList = {\r
+ value: num,\r
+ next: this.beginList\r
+ } );\r
+ },\r
+ \r
+ addEndList: function (num) {\r
+ return ( this.endList = {\r
+ value: num,\r
+ next: this.endList\r
+ } );\r
+ },\r
+ \r
/*引数に渡された時刻リストの中から、現在フレームf以下の、最大値を求めるメソッド\r
* -1を返したときはリストの中にf以下の値がないことを示す*/\r
getMaxList: function (f, list) {\r
begin = /*this.BEGINNING*/ 1,\r
play = /*this.PLAYING*/ 2,\r
end = /*this.ENDING*/ 3,\r
- post = /*this.POSTWAITING*/ 4,\r
- isWait = (state === wait) || (state === post);\r
+ post = /*this.POSTWAITING*/ 4;\r
/*beginListプロパティと、endListプロパティの中で、\r
* 現在フレーム数 f より大きい数値は、更新できる条件と無関係なので、除外しておく\r
* また、f以下の値の中から、最大値を探して、\r
var startTime = this.getMaxList(f, this.beginList),\r
endTime = this.getMaxList(f, this.endList),\r
cacheBegin = this.begin;\r
- if (isWait) {\r
+ if ( (state === wait) || (state === post) ) {\r
if (startTime > cacheBegin) {\r
this.state = begin;\r
/*beginプロパティに開始時刻をキャッシュ用に保存*/\r
} else if (!f && !startTime) {\r
/*開始時刻が0ならば、アニメーションを開始*/\r
this.state = begin;\r
- } else {\r
- return this;\r
}\r
} else if (state === begin) {\r
this.state = play;\r
if ( (endTime >= cacheBegin) || (startTime > cacheBegin) ) {\r
/*終了時刻に到達したか、再び開始イベントが発火されたとき*/\r
this.state = end;\r
- } else {\r
- return this;\r
}\r
} else if (state === end) {\r
if (endTime >= cacheBegin) {\r
return this;\r
},\r
\r
+ /*addEventメソッドで使われるイベントリスト(開始時に登録されたリスナー関数が呼び出される)*/\r
+ _beginListenerList: [],\r
+ \r
+ /*addEventメソッドで使われるイベントリスト(終了時に登録されたリスナー関数が呼び出される)*/\r
+ _endListenerList: [],\r
+ \r
+ /*addEventメソッドで使われるイベントリスト(再生時に登録されたリスナー関数が呼び出される)*/\r
+ _playListenerList: [],\r
+ \r
+ /*開始と再生と終了時に発火されるイベントリスナーを登録するメソッド*/\r
+ addEvent: function ( /*string*/ eventName, /*fnction*/ listener) {\r
+ this["_" +eventName+ "ListenerList"].push(listener);\r
+ },\r
+ \r
/*入力されたフレーム数fの場面に切り替えるメソッド*/\r
setFrame: function( /*number*/ f) {\r
- var state = this.updateState(f);\r
- /*開始と終了状態のときに、beginイベントとendイベントを呼び出しておいて、\r
- * 次の状態に遷移する*/\r
- if (state === this.BEGINNING) {\r
+ this.currentFrame = f;\r
+ var state = this.updateState(f).state;\r
+ /*アニメーション開始と再生と、終了状態のときに、beginとplayとendイベントを呼び出しておいて、\r
+ * 次の状態(再生状態)に遷移する*/\r
+ if (state === /*this.PLAYING*/ 2) {\r
+ var list = this._playListenerList;\r
+ for (var i=0;i<list.length;++i) {\r
+ list[i](this);\r
+ }\r
+ } else if (state === /*this.BEGINNING*/ 1) {\r
+ list = this._beginListenerList;\r
+ for (var i=0;i<list.length;++i) {\r
+ list[i](this);\r
+ }\r
this.updateState(f);\r
- } else if (state === this.ENDING) {\r
- this.updataState(f);\r
+ } else if (state === /*this.ENDING*/ 3) {\r
+ list = this._endListenerList;\r
+ for (var i=0;i<list.length;++i) {\r
+ list[i](this);\r
+ }\r
+ if (this.updateState(f).state === /*this.BEGINNING*/ 1) {\r
+ /*再生中にbeginイベントが呼び出された場合*/\r
+ this.updateState(f);\r
+ }\r
}\r
+ state = list = void 0;\r
}\r
+ } ).mix( function() {\r
+ /*後述の$beginや$endで使うメソッド*/\r
+ this.addList = this.addBeginList;\r
} );\r
\r
/*$endFrame オブジェクト\r
event: str\r
};\r
}\r
- },\r
+ }, \r
\r
- /*parse メソッド\r
- * stringプロパティを解析して、フレーム数を算出し、結果を$frame.beginプロパティに出力\r
+ /*_parse メソッド\r
+ * 引数の文字列を解析して、フレーム数を算出し、結果を$frame.beginプロパティに出力\r
* また、イベントリスナーに登録をしておく*/\r
- parse: function() {\r
- /*初期値を設定*/\r
- this.begin = 0;\r
- this.isResolved = false;\r
- var str = this.trim(this.string),\r
- plusminus = str.search(/[\+\-]/),\r
+ _parse: function (str) {\r
+ var plusminus = str.search(/[\+\-]/),\r
event = null,\r
- ele;\r
+ ele,\r
+ /*endListのvalueプロパティには、活動継続フレーム数と開始フレーム数を足したものが入る*/\r
+ endList = this.$list.addEndList(Number.MAX_VALUE);\r
+ if (this.$end) {\r
+ /*$endオブジェクトを継承していた場合、\r
+ *活動継続フレーム数関連のリストは無効とする*/\r
+ endList = { value: 0};\r
+ }\r
if (str === "indefinite") {\r
this.begin = Number.MAX_VALUE;\r
} else if (plusminus > 0) {\r
} else if (event) {\r
ele = event.id ? this.eventTarget.ownerDocument.getElementById(event.id)\r
: this.eventTarget;\r
- /*イベントの時間差を設定しておく*/\r
- this.eventOffset = this.begin;\r
+ /*イベントの時間差を設定しておく\r
+ * eventOffsetとobjListの変数はクロージャとしてlistener関数で使われる*/\r
+ var eventOffset = this.begin,\r
+ /*objListのvalueプロパティはあとで書き換えられる(イベントの場合のみ)*/\r
+ objList = this.$list.addList(Number.MAX_VALUE),\r
+ /*イベントのリスナーとして使う*/\r
+ listener = function(evt) {\r
+ objList.value = this.begin = eventOffset + this.$frame.currentFrame;\r
+ endList.value = this.$list.begin + this.activeTime;\r
+ this.isResolved = true;\r
+ };\r
+ this.eventOffset = eventOffset;\r
if (this.repeat > 0) {\r
ele && ele.addEventListener("repeatEvent", (function(evt) {\r
if (evt.detail === this.repeat) {\r
- this.listener(evt);\r
+ listener.call(this, evt);\r
} }).bind(this), true);\r
} else if (this.accessKey) {\r
document.documentElement.addEventListener("keydown", (function(evt) {\r
if (evt.char === this.accessKey) {\r
- this.listener(evt);\r
+ listener.call(this, evt);\r
} }).bind(this), false);\r
} else {\r
var evtName = /^(?:begin|end|repeat)$/.test(event.event) ? event.event + "Event"\r
: event.event;\r
- ele && ele.addEventListener(evtName, this.listener.bind(this), false);\r
+ ele && ele.addEventListener(evtName, listener.bind(this), false);\r
}\r
} else {\r
- /*イベントの影響を防ぐため\r
- * すでに、フレームオブジェクトのタイムラインには登録済みなので、\r
- * フレームではなく、自分独自のタイムラインに登録しておけばよい*/\r
- this.$frame = this;\r
+ /*開始リストに登録しておく($endの場合は終了リストに登録)*/\r
+ this.$list.addList(this.begin);\r
+ /*活動継続時間から算出される終了フレーム数は、終了リストに入れておく*/\r
+ endList.value = this.begin + this.activeTime;\r
}\r
s = event = str = plusminus = ele = void 0;\r
+ },\r
+ \r
+ /*stringプロパティを解析して、\r
+ * 開始フレーム数の算出や、イベントリスナーの登録をするメソッド*/\r
+ parse: function() {\r
+ /*初期値を設定*/\r
+ this.begin = 0;\r
+ this.isResolved = false;\r
+ /*$listオブジェクトを更新*/\r
+ this.$list = this.$list.up();\r
+ /*beginとend属性を考慮に入れないで、活動継続時間を求める*/\r
+ var s = this.$activate.up();\r
+ this.activeTime = s.call() || Number.MAX_VALUE;\r
+ this.simpleDuration = s.simpleDur;\r
+ var str = this.trim(this.string);\r
+ if (str.indexOf(";") > -1){\r
+ /*;で区切られたリストを一つずつ解析*/\r
+ var list = str.split(";");\r
+ for (var i=0;i<list.length;++i) {\r
+ this._parse(list[i]);\r
+ }\r
+ } else {\r
+ this._parse(str);\r
+ }\r
+ s = str = void 0;\r
return this;\r
},\r
\r
\r
/*関数型の呼び出しメソッド\r
* base.jsのofメソッドを活用して活動継続時間を算出\r
+ * ただし、begin属性とend属性については、別途、$frame.$listで定める\r
* 計算方法はSMILアニメーション 3.3.4節を参照\r
* http://www.w3.org/TR/smil-animation/#ComputingActiveDur\r
*/\r
dur = this.simpleDur,\r
isIndefRepeatCount = (this.repeatCount === ind),\r
isIndefRepeatDur = (this.repeatDur === ind),\r
- isIndefEnd = (this.end === ind),\r
isDur = dur || (dur === 0),\r
- isEnd = this.end || (this.end === 0),\r
isRepeatCount = this.repeatCount || (this.repeatCount === 0),\r
isRepeatDur = this.repeatDur || (this.repeatDur === 0),\r
actList = [],\r
if (isRepeatDur && !isIndefRepeatDur) {\r
actList.push( Math.floor( this.offset(this.repeatDur) * this.fpms) );\r
}\r
- if (isEnd && !isIndefEnd) {\r
- actList.push( this.end - this.begin );\r
- }\r
if (isDur && !isRepeatCount && !isRepeatDur) {\r
/*repeatCountやrepeatDur属性が指定されていない場合*/\r
actList.push( dur );\r
}\r
\r
/*長くなるため、インライン関数を活用\r
- * indef関数は活動継続時間が不定かどうか、もし、不定なら真を返す*/\r
+ * indef関数はbeginとend属性を考慮せずに、\r
+ * 活動継続時間が不定かどうか、もし、不定なら真を返す*/\r
function indef() {\r
- if(isIndefEnd) {\r
- return true;\r
- } else if (isEnd) {\r
- return false;\r
- }\r
return !!( (!isDur && !isRepeatDur)\r
|| (isIndefRepeatCount && !isRepeatDur)\r
|| (isIndefRepeatDur && !isRepeatCount)\r
if (!this.string) {\r
return null;\r
}\r
+ /*addListメソッドには、addBeginList関数が入っているはずなので、それを変更する*/\r
+ this.$list.addList = this.$list.addEndList;\r
this.parse(this.string);\r
return this.isResolved ? this.begin\r
: "indefinite";\r
}\r
} ).mix( {\r
+ $list: $frame.$begin.$list.up(),\r
+ \r
/*イベントリスナー用の関数*/\r
listener: function(evt) {\r
if (this.begin <= 0) {\r
to: "",\r
\r
/*initメソッドで使われるアニメーション関数*/\r
- _setFrame: function (frame) {\r
- this.state = "playing";\r
+ _setFrame: function ($list) {\r
this.setAttribute(this.to);\r
},\r
\r
- /*アニメーション中かどうかの判別\r
- * 1, idling アニメがまだ始まっていない待機状態 (規定値)\r
- * 2, playing アニメーションの再生中\r
- */\r
- state: "idling",\r
- \r
/*開始を設定されたタイムライン ($beginオブジェクト)*/\r
timeline: base("$frame").$begin,\r
\r
- /*_setEndFrameメソッドで終了処理をさせたいときに、呼び出されるメソッド\r
- * 終了処理が必要なときだけ、trueを返す*/\r
- checkEnd: function (frame) {\r
- var line = this.timeline,\r
- end = line.begin + line.activeTime;\r
- if (!line.isResolved || isNaN(end)) {\r
- /*未解決など問題が発生したとき*/\r
- line = frame = void 0;\r
- return false;\r
- } else if (\r
- ( ( frame < line.begin ) || ( end <= frame )\r
- ) && (this.state === "playing") ) {\r
- /*stateプロパティを書き換えることで、一度のみ、終了処理させる*/\r
- this.state = "idling";\r
- line = frame = void 0;\r
- return true;\r
- } else {\r
- line = frame = void 0;\r
- return false;\r
- }\r
- },\r
- \r
/*アニメが終了した際の後処理*/\r
- _setEndFrame: function (frame) {\r
+ _setEndFrame: function ($list) {\r
/*removeの場合、アニメーションを凍結せずに、もとに戻す*/\r
- if (this.checkEnd(frame) && (this.fill === "remove")) {\r
+ if (this.fill === "remove") {\r
this.removeAttribute();\r
}\r
},\r
var thisele = this.element;\r
if (line && thisele) {\r
this.timeline = line;\r
- /*ラインの中に、属性処理をするためのラインを追加*/\r
- line.addLine(\r
- { setFrame: this._setFrame.bind(this),\r
- begin: 1,\r
- activeTime: 1\r
- }\r
- );\r
- base("$frame").$endFrame.addLine(\r
- { setFrame: this._setEndFrame.bind(this),\r
- begin: 1,\r
- activeTime: 1\r
- }\r
- );\r
+ /*$begin.$listのイベントに属性処理を追加*/\r
+ line.$list.addEvent("begin", this._setFrame.bind(this));\r
+ line.$list.addEvent("play", this._setFrame.bind(this));\r
+ line.$list.addEvent("end", this._setEndFrame.bind(this));\r
+ base("$frame").addLine(line.$list);\r
+ /*アニメーションが再起動する可能性もあるため、$listのstateプロパティはここで初期化*/\r
+ line.$list.state = line.$list.WAITING;\r
}\r
- /*アニメーションが再起動する可能性もあるため、stateプロパティはここで初期化*/\r
- this.state = "idling";\r
line = thisele = void 0;\r
}\r
}).up("$animateElement").mix( {\r
return "";\r
},\r
\r
- _setFrame: function(currentTime) {\r
+ _setFrame: function($list) {\r
+ var currentTime = $list.currentFrame;\r
/*durationは単純継続時間\r
*advanceは継続時間内での、進捗率\r
* 仕様を参照 http://www.w3.org/TR/smil-animation/#AnimFuncValues\r
advance = duration ? ( (currentTime - line.begin) % duration ) / duration\r
: 0;\r
this.setAttribute(this.tocall(advance));\r
- this.state = "playing";\r
line = duration = advance = void 0;\r
},\r
\r
/*_setEndFrameメソッドは、終了処理と凍結作業をするときに、falseを返す*/\r
- _setEndFrame: function(frame) {\r
+ _setEndFrame: function($list) {\r
+ var frame = $list.currentFrame;\r
/*上書きされたメソッドを呼び出してアニメーションの凍結作業をする*/\r
- if (!this.checkEnd(frame)) {\r
- return;\r
- }\r
if (this.fill === "freeze") {\r
var line = this.timeline,\r
duration = line.simpleDuration;\r
\r
_setEndFrame: function(currentFrame) {\r
var list = this.element.__transformList;\r
- if (!this.checkEnd(currentFrame) || (this.fill !== "remove") || !list) {\r
+ if ((this.fill !== "remove") || !list) {\r
return;\r
}\r
if (!this.isSum) {\r