1 /*SIE under the MIT Lisence
3 /* Copyright 2016 dhrname and other contributors
\r
4 * http://sie.osdn.jp/
\r
6 * Permission is hereby granted, free of charge, to any person obtaining
\r
7 * a copy of this software and associated documentation files (the
\r
8 * "Software"), to deal in the Software without restriction, including
\r
9 * without limitation the rights to use, copy, modify, merge, publish,
\r
10 * distribute, sublicense, and/or sell copies of the Software, and to
\r
11 * permit persons to whom the Software is furnished to do so, subject to
\r
12 * the following conditions:
\r
14 * The above copyright notice and this permission notice shall be
\r
15 * included in all copies or substantial portions of the Software.
\r
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
\r
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
\r
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
\r
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
\r
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
\r
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
\r
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
29 /*本体オブジェクト。base関数の裏に隠蔽されている*/
\r
32 /*_base.objはbase関数やupメソッドで呼び出されるオブジェクトの始祖となるオブジェクト*/
\r
35 * 自身をプロトタイプとして、新たにオブジェクトを生成する
\r
37 up: function(name) {
\r
38 var s = Object.create(this);
\r
50 * 別のオブジェクトと合成ができるメソッド
\r
52 mix: function(obj) {
\r
54 throw new Error("No arguments error");
\r
56 if (typeof obj !== "function") {
\r
57 var alias = _base.__ng_;
\r
58 for (var i in obj) {
\r
60 /*hasOwnPropertyメソッドを使わないのは、プロトタイプチェーンをたどるようにするため
\r
61 *なお、Object.prototypeのプロパティなどは外した方がエラーがおきにくい
\r
68 obj.call(this, this);
\r
75 * 指定した名前nameのメソッドが呼び出された場合、便乗して指定関数funcをメソッドとして実行することができる
\r
77 on: function(name, func) {
\r
79 throw new Error("No arguments error");
\r
80 } else if (/^(?:up|on|mix|of)$/.test(name)) {
\r
81 throw new Error("Invalid method name error");
\r
82 } else if (typeof func !== "function") {
\r
83 throw new Error("Not support arguments type");
\r
85 var tev = this._eventList__,
\r
87 if (!this._eventList__) {
\r
88 tev = this._eventList__ = [];
\r
89 } else if (!this.hasOwnProperty("_eventList__")) { //祖先がすでにonメソッドを呼び出していれば
\r
92 tev = this._eventList__ = s;
\r
95 if (!this[name] || !tn.isOn) { //まだ、onメソッドが呼び出されていなければ
\r
96 /*nameで指定されたメソッドの初期化*/
\r
97 if (typeof tn === "function") {
\r
98 /*nameで指定されたメソッドがすでにある場合は、配列tevの親をたどれないようにしておく*/
\r
103 tev._parent = null;
\r
105 this[name] = function() {
\r
106 var te = this._eventList__,
\r
107 _name = name, //スコープチェーンのエイリアス
\r
111 isCalled = false;//返り値の制御で使う
\r
113 while (tp = te._parent) { //親をさかのぼっていく
\r
117 while (te) { //子をたどっていく
\r
118 /*最初の返り値の結果はsとして記録して、後で返す*/
\r
119 for (var i=0, tli=te.length;i<tli;++i) {
\r
120 if(te[i].name === _name) {
\r
121 ts = te[i].func.apply(this, arguments);
\r
130 te = ts = _name = isCalled = void 0;
\r
133 this[name].isOn = true;
\r
139 tev = tn = func= void 0;
\r
143 /*__argsと__appプロパティは後のofメソッドで使う*/
\r
148 * 指定されたオブジェクトのプロパティを遅延処理で実行できるメソッド
\r
149 * 後述のcallメソッドと組み合わせて使う
\r
151 of: function(obj) {
\r
153 throw new Error("No arguments error");
\r
154 } else if(this.hasOwnProperty("__of")) {
\r
156 throw new Error("Reset error");
\r
158 /*__appと__argsプロパティに、指定されたプロパティを記録しておく*/
\r
159 var args = this.__args || [];
\r
160 for (var i in obj) {
\r
161 if(obj.hasOwnProperty(i) && (i !== "call")) {
\r
162 /*一度登録されたプロパティは二度書きしないようにする*/
\r
163 args[i] || args.push(i);
\r
164 args[i] = this[i] = obj[i];
\r
167 obj.call && (this.__app = { call: obj.call });
\r
168 this.__args = args;
\r
170 args = i = obj = void 0;
\r
175 * ofメソッドで指定されているオブジェクトのcallメソッドを実行できるメソッド
\r
176 * そのさい、オブジェクトのプロパティとメソッドは、自動で実行展開される
\r
179 if (!this.__app) { //ofメソッドが呼び出されていないか、callメソッドが一度も設定されていない場合
\r
182 var args = this.__args,
\r
183 call = this.call; //callメソッドの一時的なキャッシュ
\r
184 /*循環参照を避けるためcallメソッドの入れ替え*/
\r
185 this.call = callFunc;
\r
186 for (var i=0, ali=args.length;i<ali;++i) {
\r
187 /*callメソッドがあるオブジェクトは展開*/
\r
190 if (argi && argi.call) {
\r
191 this[ai] = argi.call(this);
\r
195 args = ai = argi = call = void 0;
\r
196 return this.__app.call.apply(this, arguments);
\r
200 /*callメソッドで使われる関数*/
\r
201 var callFunc = function() { return this };
\r
203 /*base関数でキャッシュとして使うオブジェクト*/
\r
204 var baseCache = {};
\r
206 base = function (name) {
\r
207 var __base = _base,
\r
208 _cache = baseCache; //エイリアス作成
\r
210 throw new Error("No arguments error");
\r
211 } else if (_cache[name]) {
\r
212 /*キャッシュに登録されている場合は、登録されたオブジェクトを返す*/
\r
213 return _cache[name];
\r
215 var s = Object.create(__base.obj);
\r
216 this[name] = _cache[name] = s;
\r
217 /*自身が値であるようなプロパティを設定する*/
\r
224 /*mixメソッドで使うNGハッシュを作成*/
\r
226 proto = Object.prototype;
\r
227 for (var i in proto) {
\r
229 /*上記のキャッシュについて、すべてのプロパティをnullかundefinedにしておく*/
\r
230 baseCache[i] = null;
\r
232 hash.constructor = false; //constructorはNGハッシュに追加しない
\r
233 _base.__ng_ = hash;
\r
234 hash = proto = void 0;
\r
237 * 即時関数の内部で作っていおいたオブジェクトを解放させるための関数
\r
239 base.free = function() {
\r
241 _base = baseCache = callFunc = void 0;
\r
244 /*IE8などObject.createをサポートしていないブラウザ用*/
\r
245 Object.create || (Object.create = function(obj) {
\r
246 var F = function() {};
\r
251 /*SIE under the MIT Lisence
\r
253 /* Copyright 2016 dhrname and other contributors
\r
254 * http://sie.osdn.jp/
\r
256 * Permission is hereby granted, free of charge, to any person obtaining
\r
257 * a copy of this software and associated documentation files (the
\r
258 * "Software"), to deal in the Software without restriction, including
\r
259 * without limitation the rights to use, copy, modify, merge, publish,
\r
260 * distribute, sublicense, and/or sell copies of the Software, and to
\r
261 * permit persons to whom the Software is furnished to do so, subject to
\r
262 * the following conditions:
\r
264 * The above copyright notice and this permission notice shall be
\r
265 * included in all copies or substantial portions of the Software.
\r
267 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
\r
268 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
\r
269 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
\r
270 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
\r
271 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
\r
272 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
\r
273 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
279 base("$frame").mix ( {
\r
280 /*フレームレート。1ミリ秒何フレームか。計算を省略するためミリ秒使用*/
\r
283 /*タイムラインのリスト (時間区間の設定ができる)*/
\r
286 /*開始フレーム数の候補。アニメーションの開始条件となる
\r
287 * 単位はフレーム数であって、秒数ではない
\r
288 * なお、初期値については、開始フレームが負の値を考慮しているため*/
\r
289 begin: -Number.MAX_VALUE,
\r
291 /*活動継続時間 (Active Duration)のフレーム数。アニメーションの継続条件となる
\r
292 * 単位はフレーム数であって、秒数ではない*/
\r
293 activeTime: Number.MAX_VALUE,
\r
298 /*アニメーションを開始させるメソッド*/
\r
299 startAnimation: function() {
\r
300 /*$getDocument.step関数は最後に書く*/
\r
301 base("$getDocument").step();
\r
304 /*アニメーションが停止した状態かどうか、停止しているならばtrue*/
\r
307 /*アニメーションを停止させるメソッド*/
\r
308 pauseAnimation: function() {
\r
309 this.isPaused = true;
\r
312 /*後述のinitializeメソッドで使うオブジェクトリスト*/
\r
316 initialize: function() {
\r
317 var list = this.objList;
\r
318 for (var i=0;i<list.length;++i) {
\r
319 list[i].initialize();
\r
324 * フレーム数を数値num まで進めるか、戻す*/
\r
325 setFrame: function( /*number*/ num) {
\r
326 this.currentFrame = num;
\r
327 var timelines = this.timelines;
\r
328 for (var i=0;i<timelines.length;++i) {
\r
329 if (timelines[i] === this) {
\r
330 /*自分自身が含まれていると、永久に再帰を繰り返して、「スタック領域が不足」してしまう*/
\r
333 timelines[i].setFrame(num);
\r
338 * タイムラインを追加したあと、trueを返す
\r
339 * ただし、引数objのobj.beginとobj.activeTimeが定まっていない場合はfalseを返す*/
\r
340 addLine: function( /*$frame*/ obj ) {
\r
341 if(!obj || (!obj.begin && (obj.begin !== 0))
\r
342 || (!obj.activeTime && (obj.activeTime !== 0)) ) {
\r
343 /*どちらのプロパティも未確認の場合、タイムラインは追加されない*/
\r
346 var timelines = this.timelines;
\r
347 if ( timelines.indexOf(obj) >= 0 ) {
\r
348 this.removeLine(obj);
\r
350 timelines.push(obj);
\r
356 * 指定されたタイムラインのオブジェクトを、リストから削除する*/
\r
357 removeLine: function( /*$frame*/ timeline ) {
\r
358 var list = this.timelines,
\r
359 j = list.indexOf(timeline);
\r
361 list.splice(j, 1); //Arrayのspliceを利用して、リストからtimelineを排除
\r
365 } ).mix( function($frame) {
\r
366 $frame.up("$list").mix( {
\r
367 /*終了時刻(単位フレーム数)のキャッシュとして使う*/
\r
370 /*開始時刻から終了時刻までのフレーム数
\r
371 * これがactiveTimeより短ければ、活動継続時間とみなす*/
\r
372 beginEnd: Number.MAX_VALUE,
\r
374 /*開始時刻(単位フレーム数)リスト (後述のupdateStateメソッドで使う)*/
\r
377 value: Number.MAX_VALUE
\r
380 /*終了時刻(単位フレーム数)リスト (後述のupdateStateメソッドで使う)*/
\r
383 value: Number.MAX_VALUE
\r
386 addBeginList: function (num) {
\r
387 return ( this.beginList = {
\r
389 next: this.beginList
\r
393 addEndList: function (num) {
\r
394 return ( this.endList = {
\r
400 /*引数に渡された時刻リストの中から、現在フレームf以下の、最大値を求めるメソッド
\r
401 * Number.MIN_VALUEの値を返したときはリストの中にf以下の値がないことを示す*/
\r
402 getMaxList: function (f, list) {
\r
403 var maxTime = -Number.MAX_VALUE; /*時刻の最大値*/
\r
405 var v = list.value;
\r
406 /*f以下の開始リスト値のうち、最大値をmaxTimeに代入*/
\r
408 && (maxTime <= v) ) {
\r
416 /*現在の要素の状態を数値で示す(マジックナンバーは後述の大文字プロパティを使う)*/
\r
419 /*アニメーション待機状態を示す定数*/
\r
422 /*アニメーション開始状態を示す定数*/
\r
425 /*アニメーション再生中の状態を示す定数*/
\r
428 /*アニメーション終了状態を示す定数*/
\r
431 /*終了処理をした後の待機状態を示す定数*/
\r
436 this.state = this.WAITING;
\r
441 /*引数で指定されたフレーム数に応じて、stateプロパティを更新するメソッド*/
\r
442 updateState: function( /*number*/ f) {
\r
443 if (f === void 0) {
\r
444 /*引数fが指定されないときには状態を更新しない*/
\r
447 var state = this.state,
\r
448 wait = /*this.WAITING*/ 0,
\r
449 begin = /*this.BEGINNING*/ 1,
\r
450 play = /*this.PLAYING*/ 2,
\r
451 end = /*this.ENDING*/ 3,
\r
452 post = /*this.POSTWAITING*/ 4;
\r
453 /*beginListプロパティと、endListプロパティの中で、
\r
454 * 現在フレーム数 f より大きい数値は、更新できる条件と無関係なので、除外しておく
\r
455 * だから、f以下の値の中から、最大値を探して、
\r
456 * それをbeginプロパティの値cacheBeginと比較する*/
\r
457 var startTime = this.getMaxList(f, this.beginList);
\r
458 if (startTime === -Number.MAX_VALUE) {
\r
459 (state > post) && (this.state = begin);
\r
462 var endTime = this.getMaxList(f, this.endList),
\r
463 isWait = (state === wait),
\r
464 cacheBegin = this.begin;
\r
465 if ( !startTime && isWait) {
\r
466 /*開始時刻が0ならば、アニメーションを開始
\r
467 * ただし、アニメが終了した後のPOSTWAITING状態の時には作動させない*/
\r
469 this.state = begin;
\r
470 } else if ( isWait || (state === post) ) {
\r
471 if (startTime > cacheBegin) {
\r
472 this.state = begin;
\r
473 /*beginプロパティに開始時刻をキャッシュ用に保存*/
\r
474 this.begin = startTime;
\r
476 } else if (state === begin) {
\r
477 if (endTime >= cacheBegin) {
\r
478 /*終了時刻にもう到達したときは、直接BEGINNING状態からENDING状態へ移行*/
\r
480 /*endTimeを終了時刻とみなす*/
\r
481 (endTime > 0) && (this.end = endTime);
\r
482 /*activeTimeプロパティは、begin属性とend属性が反映されていないため、
\r
483 * beginEndプロパティに別に設定しておく*/
\r
488 } else if (state === play) {
\r
489 /*activeTimeプロパティを比較して、変数endTimeを書き換える*/
\r
490 var act = cacheBegin + this.activeTime;
\r
491 endTime = (endTime > act) ? act
\r
493 if ( (f >= act) || (endTime >= cacheBegin) || (startTime > cacheBegin) ) {
\r
494 /*終了時刻に到達したか、再び開始イベントが発火されたとき*/
\r
497 /*endTimeを終了時刻とみなす*/
\r
498 this.end = endTime;
\r
499 /*activeTimeプロパティは、begin属性とend属性が反映されていないため、
\r
500 * beginEndプロパティに別に設定しておく*/
\r
501 this.beginEnd = endTime - cacheBegin;
\r
504 } else if (state === end) {
\r
505 if (startTime > cacheBegin) {
\r
506 /*再生中に開始イベントが発火されて、終了状態となったとき*/
\r
507 this.state = begin;
\r
508 this.begin = startTime;
\r
513 this.state = begin;
\r
515 cacheBegin = startTime = endTime = isWait = state = void 0;
\r
519 /*addEventメソッドで使われるイベントリスト(開始時に登録されたリスナー関数が呼び出される)*/
\r
520 _beginListenerList: [],
\r
522 /*addEventメソッドで使われるイベントリスト(終了時に登録されたリスナー関数が呼び出される)*/
\r
523 _endListenerList: [],
\r
525 /*addEventメソッドで使われるイベントリスト(再生時に登録されたリスナー関数が呼び出される)*/
\r
526 _playListenerList: [],
\r
528 /*開始と再生と終了時に発火されるイベントリスナーを登録するメソッド*/
\r
529 addEvent: function ( /*string*/ eventName, /*fnction*/ listener) {
\r
530 var evtName = "_" +eventName+ "ListenerList";
\r
531 /*プロトタイプ継承していた場合は新しく配列を作成*/
\r
532 if (!this.hasOwnProperty(evtName)) {
\r
533 this[evtName] = [];
\r
535 this[evtName].push(listener);
\r
538 /*入力されたフレーム数fの場面に切り替えるメソッド*/
\r
539 setFrame: function( /*number*/ f) {
\r
540 this.currentFrame = f;
\r
541 var state = this.updateState(f).state;
\r
542 /*アニメーション開始と再生と、終了状態のときに、beginとplayとendイベントを呼び出しておいて、
\r
543 * 次の状態(再生状態)に遷移する*/
\r
544 if (state === /*this.PLAYING*/ 2) {
\r
545 var list = this._playListenerList;
\r
546 for (var i=0;i<list.length;++i) {
\r
549 } else if (state === /*this.BEGINNING*/ 1) {
\r
550 list = this._beginListenerList;
\r
551 for (var i=0;i<list.length;++i) {
\r
554 /*開始時刻と終了時刻が一致した場合はstateはENDING状態
\r
556 state = this.updateState(f).state;
\r
558 if (state === /*this.ENDING*/ 3) {
\r
559 list = this._endListenerList;
\r
560 for (var i=0;i<list.length;++i) {
\r
563 if (this.updateState(f).state === /*this.BEGINNING*/ 1) {
\r
564 /*再生中にbeginイベントが呼び出された場合*/
\r
565 this.updateState(f);
\r
568 state = list = void 0;
\r
570 } ).mix( function() {
\r
571 /*後述の$beginや$endで使うメソッド*/
\r
572 this.addList = this.addBeginList;
\r
577 $frame.up("$begin").mix( {
\r
579 /*開始時刻やタイミングが書かれた文字列*/
\r
582 /*イベントやindefinteで未解決かどうか*/
\r
586 eventTarget: document.documentElement,
\r
588 /*現在のフレーム数を改めて初期化*/
\r
591 /*イベント同期で使う時間差のフレーム数*/
\r
594 /*repeat(1)など文字列内に書かれたリピート回数*/
\r
597 /*accessKey(a)の"a"などキーイベントの対象となる文字列*/
\r
602 trim: function(str) {
\r
603 /*strがString型以外のときは必ずエラーを出す*/
\r
604 return str.replace(/[\s\n]+/g, "");
\r
608 * 引数に渡された文字列から、ミリ秒単位に変換した時間を、解析して返す*/
\r
609 offset: function(str) {
\r
611 var plusminus = str.charAt(0),
\r
612 /*parseFloatのエイリアス*/
\r
613 _float = parseFloat,
\r
614 s = _float( str.match(/[\d.]+ms$/) || "0") + sec() + min() + h();
\r
615 if (plusminus === "-") {
\r
618 plusminus = _float = sec = min = h = void 0;
\r
621 /*00:00:0と00:0と、0sなどの文字列をミリ秒へ変換*/
\r
623 return str2num( 1000, /[\d.]+s$/, /[\d.]+$/ );
\r
626 return str2num( 60000, /[\d.]+min$/, /\d\d:[^:]+$/ );
\r
629 return str2num( 3600000, /\d+:\d\d:/, /[\d.]+h$/ );
\r
631 function str2num(s, /*RegExp*/ a, /*RegExp*/ b) {
\r
632 return s*( _float(str.match(a) || "0") || _float(str.match(b) || "0") );
\r
637 * 引数の文字列から、idとイベントに相当する文字列のプロパティを持ったオブジェクトを返す
\r
638 * idがない場合や、イベントがない場合は空文字列を該当のプロパティに入れる*/
\r
639 event: function(str) {
\r
641 if (/[\+\-]/.test(str)) {
\r
642 /*数値がある場合は切り取っておく*/
\r
643 str = str.slice(0, str.search(/[\+\-]/));
\r
645 if (str.indexOf(".") > -1) {
\r
646 /*ドットが見つかった場合、IDとイベントに分けておく*/
\r
647 var ide = str.split(".");
\r
648 /* エラーが起きて、idが空文字列ならば、evtも空文字列。逆も然り*/
\r
650 id: (ide[1] && ide[0]),
\r
651 event: (ide[0] && ide[1])
\r
662 * 引数の文字列を解析して、フレーム数を算出し、結果を$frame.beginプロパティに出力
\r
663 * また、イベントリスナーに登録をしておく*/
\r
664 _parse: function (str) {
\r
665 var plusminus = str.search(/[\+\-]/),
\r
668 /*endListのvalueプロパティには、活動継続フレーム数と開始フレーム数を足したものが入る*/
\r
669 endList = this.$list.addEndList(Number.MAX_VALUE);
\r
670 if (str === "indefinite") {
\r
671 this.begin = Number.MAX_VALUE;
\r
672 } else if (plusminus > 0) {
\r
673 /*Event-Value +/- Clock-Value の場合*/
\r
674 this.begin = this.offset( str.slice(plusminus) );
\r
675 event = this.event(str);
\r
676 } else if ( /[^\+\-\d]/.test(str.charAt(0)) ) {
\r
677 /*Event-Valuen のみの場合*/
\r
678 event = this.event(str);
\r
680 /*+/- Clock-Value のみの場合*/
\r
681 this.begin = this.offset( str );
\r
682 /*イベントもindefiniteもないので、解決済みと考える*/
\r
683 this.isResolved = true;
\r
685 /*もしもあれば、リピートの回数を求める*/
\r
686 this.repeat = /repeat\((\d+)\)/.test(str) ? +RegExp.$1 : 0;
\r
687 /*もしもあれば、押されるはずのキーを求める*/
\r
688 this.accessKey = /accessKey\(([^\)]+?)\)/.test(str) ? RegExp.$1 : "";
\r
689 this.begin = Math.floor( this.begin * this.fpms);
\r
690 if (str === "indefinite") {
\r
691 /*begin属性の値がindefiniteの場合は、何もしない。
\r
692 * 開始時刻はbeginElementメソッドに依存*/
\r
693 } else if (event) {
\r
694 ele = event.id ? this.eventTarget.ownerDocument.getElementById(event.id)
\r
695 : this.eventTarget;
\r
697 * eventOffsetとobjListの変数はクロージャとしてlistener関数で使われる*/
\r
698 var eventOffset = this.begin,
\r
699 /*objListのvalueプロパティはあとで書き換えられる(イベントの場合のみ)*/
\r
700 objList = this.$list.addList(Number.MAX_VALUE),
\r
702 listener = function(evt) {
\r
703 objList.value = this.begin = eventOffset + base("$frame").currentFrame;
\r
704 this.isResolved = true;
\r
706 this.eventOffset = eventOffset;
\r
707 if (this.repeat > 0) {
\r
708 ele && ele.addEventListener("repeatEvent", (function(evt) {
\r
709 if (evt.detail === this.repeat) {
\r
710 listener.call(this, evt);
\r
711 } }).bind(this), true);
\r
712 } else if (this.accessKey) {
\r
713 document.documentElement.addEventListener("keydown", (function(evt) {
\r
714 if (evt.char === this.accessKey) {
\r
715 listener.call(this, evt);
\r
716 } }).bind(this), false);
\r
718 var evtName = /^(?:begin|end|repeat)$/.test(event.event) ? event.event + "Event"
\r
720 ele && ele.addEventListener(evtName, listener.bind(this), false);
\r
723 /*開始リストに登録しておく($endの場合は終了リストに登録)*/
\r
724 this.$list.addList(this.begin);
\r
726 s = event = str = plusminus = ele = void 0;
\r
729 /*stringプロパティを解析して、
\r
730 * 開始フレーム数の算出や、イベントリスナーの登録をするメソッド*/
\r
731 parse: function() {
\r
734 this.isResolved = false;
\r
735 var str = this.trim(this.string);
\r
736 if (str.indexOf(";") > -1){
\r
737 /*;で区切られたリストを一つずつ解析*/
\r
738 var list = str.split(";");
\r
739 for (var i=0;i<list.length;++i) {
\r
740 this._parse(list[i]);
\r
749 /*$listと$activateオブジェクトを更新して、活動継続時間を求めるメソッド*/
\r
750 updateList: function() {
\r
751 this.$list = this.$list.up();
\r
752 /*beginとend属性を考慮に入れないで、活動継続時間を求める*/
\r
753 var s = ( this.$activate = this.$activate.up() );
\r
754 /*$endオブジェクトに付属している$listプロパティを更新したものと一致させておく*/
\r
755 s.end && (s.end.$list = this.$list);
\r
756 this.activeTime = this.$list.activeTime = s.call() || Number.MAX_VALUE;
\r
757 this.simpleDuration = s.simpleDur;
\r
762 * 活動継続時間などを計算するための計算実体
\r
763 * $begin オブジェクトからの継承*/
\r
764 } ).up("$activate").of( {
\r
766 /*単純継続時間のパースされる前の文字列*/
\r
769 /*活動をストップさせるためのオブジェクト*/
\r
770 end: $frame.$begin.up("$end"),
\r
778 /*単純継続時間 (単位はフレーム数)*/
\r
779 simpleDur: function() {
\r
780 return ( (this.dur === "indefinite") || !this.dur ) ?
\r
782 : Math.floor(this.offset(this.dur) * this.fpms) ;
\r
786 * 最小値 <= 活動継続時間 とならなければならない*/
\r
790 * 活動継続時間 <= 最大値 とならなければならない*/
\r
793 /*解決した(計算する)ときの時間*/
\r
794 resolvedTime: function() {
\r
799 * base.jsのofメソッドを活用して活動継続時間を算出
\r
800 * ただし、begin属性とend属性については、別途、$frame.$listで定める
\r
801 * 計算方法はSMILアニメーション 3.3.4節を参照
\r
802 * http://www.w3.org/TR/smil-animation/#ComputingActiveDur
\r
805 var ind = "indefinite",
\r
806 dur = this.simpleDur,
\r
807 isIndefRepeatCount = (this.repeatCount === ind),
\r
808 isIndefRepeatDur = (this.repeatDur === ind),
\r
809 isDur = dur || (dur === 0),
\r
810 isRepeatCount = this.repeatCount || (this.repeatCount === 0),
\r
811 isRepeatDur = this.repeatDur || (this.repeatDur === 0),
\r
813 min = Math.floor(this.offset(this.min) * this.fpms),
\r
814 max = (this.max === ind) ? null : Math.floor(this.offset(this.max) * this.fpms),
\r
817 /*注意点として、活動継続時間が不定かつ、min属性とmax属性が指定されたときの記述が仕様にないため、
\r
818 * W3Cのテストスイート(animate-elem-66-t.svg)に従い、max属性の値を返す*/
\r
824 if (isDur && this.repeatCount && !isIndefRepeatCount) {
\r
825 actList.push( dur * this.repeatCount );
\r
827 if (isRepeatDur && !isIndefRepeatDur) {
\r
828 actList.push( Math.floor( this.offset(this.repeatDur) * this.fpms) );
\r
830 if (isDur && !isRepeatCount && !isRepeatDur) {
\r
831 /*repeatCountやrepeatDur属性が指定されていない場合*/
\r
832 actList.push( dur );
\r
835 /*長くなるため、インライン関数を活用
\r
836 * indef関数はbeginとend属性を考慮せずに、
\r
837 * 活動継続時間が不定かどうか、もし、不定なら真を返す*/
\r
839 return !!( (!isDur && !isRepeatDur)
\r
840 || (isIndefRepeatCount && !isRepeatDur)
\r
841 || (isIndefRepeatDur && !isRepeatCount)
\r
842 || (isIndefRepeatCount && isIndefRepeatDur)
\r
846 ind = dur = isIndefRepeatCount = isIndefRepeatDurindef = isDur = isEnd = isRepeatDur = isRepeatCount = indef = void 0;
\r
848 if (actList.length === 1) {
\r
850 } else if(actList.length > 1) {
\r
851 /*属性が競合するときは、最小値をとること (SMILアニメーション 3.3.4節)*/
\r
852 s = Math.min.apply(Math, actList);
\r
856 if ( max && (min > max) ) {
\r
859 min && (min > s) && (s = min);
\r
860 max && (max < s) && (s = max);
\r
864 $frame.$begin.$end.of( {
\r
866 if (!this.string) {
\r
869 /*addListメソッドには、addBeginList関数が入っているはずなので、それを一時的に変更する*/
\r
870 this.$list.addList = this.$list.addEndList;
\r
871 this.parse(this.string);
\r
872 this.$list.addList = this.$list.addBeginList;
\r
873 return this.isResolved ? this.begin
\r
877 $list: $frame.$begin.$list.up()
\r
881 * 呈示値 (presentation value)の計算をする。値そのものを返すための計算実体*/
\r
882 base("$from").of( {
\r
886 /*呈示値の数値の部分だけを抜き出した配列を返す*/
\r
887 numList: function() {
\r
888 var s = this.string.match(/[\-\+]?[\d\.]+(?:[eE][\-\+]?[\d\.]+)?/g)
\r
891 /*mapメソッドで代用してもよい*/
\r
892 for (var i=0;i<s.length;++i) {
\r
893 s[i] = parseFloat(s[i]);
\r
899 /*呈示値の文字部分だけを抜き出した配列を返す*/
\r
900 strList: function() {
\r
901 /*replaceメソッドで1E-10などの対策*/
\r
902 return this.string.replace(/\d[eE][\-\+\d]/g, "")
\r
903 .match(/[^\d\-\+\.]+/g);
\r
906 from: base("$from").up().mix( {
\r
910 /*$toオブジェクトにこのオブジェクトを適用させる関数*/
\r
912 /*advanceメソッドで使うために、stringを保持*/
\r
913 this.numList.string = this.string;
\r
914 if (this.numList.length
\r
915 && (this.additive[0] === 0)
\r
916 && (this.accumulate[0] === 0)
\r
918 /*配列の項目がundefinedだと困るので、配列を初期化する*/
\r
921 for (var i=0;i<this.numList.length;++i) {
\r
923 additive[i] = accumulate[i] = 0;
\r
925 this.additive = additive;
\r
926 this.accumulate = accumulate;
\r
928 /*文字部分の配置パターンは4通りあるので、ここでstrListを処理
\r
931 * (3) a 0 a (ノーマルパターン)
\r
933 * これらのパターンのうち、(1)(2)(4)を(3)のパターンに統一したのが以下の処理*/
\r
934 /*文字列が1aのように、数値で始まるかどうか。始まったら真*/
\r
935 if (!this.string || !this.numList.length || !this.strList) {
\r
936 return this.numList;
\r
938 var isNormal = (this.numList.length < this.strList.length);
\r
939 if (/^[\-\+]?[\d\.]/.test(this.string) && !isNormal) {
\r
940 /*文字列が1aのように、数値で始まる場合*/
\r
941 this.strList.unshift("");
\r
943 if (/\d$/.test(this.string) && !isNormal) {
\r
944 /*文字列がa1のように、数値で終わる場合*/
\r
945 this.strList.push("");
\r
947 return this.numList;
\r
952 /*advanceメソッドの返り値で使われる小数点以下の桁数*/
\r
955 /*additve属性やaccumulate属性が設定された、累積アニメーションか、加法アニメーションで使われる*/
\r
960 * アニメーションの進行具合を示す進捗率 t (0 <= t <= 1)をもとに、現在の呈示値を算出するためのもの
\r
961 * callメソッドが前もって呼び出されていることが前提となる*/
\r
962 advance: function(t) {
\r
963 if ( (t < 0) || (1 < t)) {
\r
964 throw new Error("An Invalid Number Error");
\r
966 if (!this.string) {
\r
968 } else if (!this.from.length) {
\r
969 /*discreteのために、this.stringに数値が入っていない場合の対処*/
\r
971 return this.string.trim();
\r
973 return this.from.string.trim();
\r
976 numList = this.numList,
\r
977 strList = this.strList,
\r
978 fromNumList = this.from,
\r
980 additive = this.additive,
\r
981 accumulate = this.accumulate;
\r
982 for (var i=0,nuli=numList.length;i<nuli;++i) {
\r
983 /*原点Oを(0,0,...0)とおく
\r
984 *$fromと$toを、原点Oからの二つのベクトル (n次空間のベクトル)、ベクトルOFとベクトルOTと考える
\r
985 *$fromと$toの二つの端の点FとTを結ぶ線分を、t : 1-t で内分する点をPとおく
\r
986 * このときのベクトルOPを求めたのが以下の式*/
\r
987 str += ( t * numList[i] + (1 - t) * fromNumList[i] + additive[i] + accumulate[i]).toFixed(deg);
\r
988 strList && ( str += strList[i+1] );
\r
990 /*文字列はcallメソッドにより、a0aのパターンになっているので、aの部分を追加*/
\r
991 str = (strList ? strList[0] : "") + str;
\r
992 numList = strList = fromNumList = i = nuli = deg = additive = accumulate = void 0;
\r
997 * fromベクトルから自分自身のベクトルへの距離 (ノルム)の数値を返す。callメソッドを使うので注意すること*/
\r
998 distance: function(from) {
\r
1002 var toList = this.call(),
\r
1003 fromList = from.call ? from.call() : from,
\r
1005 if (!toList || !fromList) {
\r
1008 for (var i=0, tli=toList.length; i<tli; ++i) {
\r
1009 s += (toList[i] - fromList[i])*(toList[i] - fromList[i]);
\r
1011 return Math.sqrt(s);
\r
1014 /*setAdditive メソッド
\r
1015 * additve属性がsumのときに使われるメソッド
\r
1016 * 引数は親要素の、現在の属性値*/
\r
1017 setAdditive: function(str) {
\r
1021 var from = this.$from.up();
\r
1022 from.string = str;
\r
1023 return ( this.additive = from.call() );
\r
1026 /*setAccumulate メソッド
\r
1027 * accumulate属性がsumのときに使われるメソッド
\r
1029 setAccumulate: function(num) {
\r
1030 if (!num || isNaN(num)) {
\r
1033 return ( this.accumulate = this.numList.map( function(d) {
\r
1039 .up("$to").from = null;
\r
1041 /*計算モードを定めるための計算実体
\r
1043 base("$calcMode").mix({
\r
1044 /*計算モード (calcMode属性の値)*/
\r
1048 * たとえば、"0, 0.5, 0.7, 1"の場合、時間の区間はそれぞれ、0.5 (=0.5-0) 0.2 (=0.7-0.5) 0.3 (=1-0.7)である
\r
1049 * このうち、どれか一つが値として入力される*/
\r
1052 /*keySpline属性の値を設定*/
\r
1056 * 進捗率を時間の圧縮率( = keyTimeプロパティ)で割ることで、現在、どこまで進んでいるのかを求めることができる*/
\r
1057 _f: function (t) {
\r
1059 var tkey = this.keyTime;
\r
1060 if ( (tkey === 0) && t) {
\r
1062 } else if (!tkey || !isFinite(tkey) ) {
\r
1063 return this.string;
\r
1066 t = (t > 1) ? Math.floor(t) : t;
\r
1069 return isNaN(t) ? this.string
\r
1070 : this.to.advance(t);
\r
1073 /*discreteモードのときには、上記の_f関数の代わりに、以下の関数を用いる*/
\r
1074 funcForDiscrete: function (t) {
\r
1076 return this.string;
\r
1077 } else if (t === 1) {
\r
1078 return this.to.string;
\r
1080 return this.to.advance(0);
\r
1091 /*与えられたアニメーションの進捗率を使った時間の圧縮率を計算して呈示値を返すための関数を作る*/
\r
1092 call: function() {
\r
1093 var f = this._f.bind(this);
\r
1094 if (this.mode === "linear") {
\r
1097 } else if (this.mode === "paced") {
\r
1098 /*keyTimes属性は無視され、ベクトルの距離の割合から計算される*/
\r
1099 this.keyTime = this.to.distance(this.to.from) / this.norm;
\r
1101 } else if (this.mode === "spline") {
\r
1102 var tk = this.keySplines,
\r
1103 /*必ず関数を返すようにするため、円周率を返す関数tfを返して、nullの代わりとする*/
\r
1104 tf = function(x) {
\r
1107 tf.isNotAnimate = true;
\r
1111 for (var i=0,tki = NaN;i<tk.length;++i) {
\r
1116 if ( (tki < 0) || (1 < tki)) {
\r
1127 Ax = x4-3*(x3-x2),
\r
1130 Ay = y4-3*(y3-y2),
\r
1133 _newton = Math.qubicnewton; //高速化のためのエイリアス
\r
1134 if ( ( (x2 === 0) || (x2 === 1) )
\r
1136 && ( (x3 === 1) || (x3 === 0) )
\r
1138 /*linearモードと同じ効果 (収束ではない可能性を考慮)*/
\r
1142 var tkey = this.keyTime;
\r
1143 if (tkey || isFinite(tkey) ) {
\r
1144 /*keyTimeから時間の収縮率を3次ベジェ曲線に適用しておく*/
\r
1152 tkey = tk = x2 = y2 = x3 = y3 = x4 = y4 = void 0;
\r
1153 return function (x) {
\r
1155 *x = (x4-3*(x3-x2)-x1)*t*t*t + 3*(x3-2*x2+x1)*t*t + 3*(x2-x1)*t + x1
\r
1156 *y = (y4-3*(y3-y2)-y1)*t*t*t + 3*(y3-2*y2+y1)*t*t + 3*(y2-y1)*t + y1
\r
1158 * スプラインモードの場合、x1 = y1 = 0, x4 = y4 = 1
\r
1159 * ベジェ曲線のxの式が三次方程式であるため、その解 t から、ベジェ曲線の y を求める
\r
1160 * なお、ニュートン法の初期値はxとする
\r
1161 * なぜなら、xの式をみると、xが増加傾向となるスプラインモードでは、係数が負となる可能性が低いため*/
\r
1162 var t = _newton(Ax, Bx, Cx, -x, x);
\r
1163 return f(Ay*t*t*t + By*t*t + Cy*t);
\r
1165 } else if (this.mode === "discrete") {
\r
1167 return this.funcForDiscrete.bind(this);
\r
1170 } ).to = base("$from").$to;
\r
1173 /*ニュートン法により、三次方程式 a0x^3 + a1x^2 + a2x + a3 の解を求める
\r
1175 Math.qubicnewton = function(a0, a1, a2, a3, b) {
\r
1176 var eps = 1e-15, //収束誤差
\r
1177 fb = a0 *b*b*b + a1 *b*b + a2*b + a3; //方程式の結果
\r
1182 for (var i=0;i<100;i=(i+1)|0) {
\r
1183 /*数値nは与えられた三次方程式を微分したもの*/
\r
1184 var n = 3* a0 *b*b + 2 * a1 *b + a2;
\r
1185 if (!n || ( (fb < eps) && (fb > -eps) )) {
\r
1186 fb = eps = void 0;
\r
1191 fb = a0 *b*b*b + a1 *b*b + a2*b + a3;
\r
1194 return b; //収束しなかった結果
\r
1197 /*$attribute オブジェクト
\r
1198 * アニメーションの時間調整と、呈示値の調整を一つのオブジェクトにまとめて行うことで、
\r
1199 * アニメーションサンドイッチの実装をする
\r
1200 * $calcModeオブジェクトから継承*/
\r
1201 base("$calcMode").up("$attribute").mix( {
\r
1203 /*アニメーションの対象となる要素。たとえば、animate要素の親要素*/
\r
1206 /*$fromオブジェクトを作るためのひな形となるオブジェクト*/
\r
1207 $from: base("$from").up(),
\r
1209 /*attributeName属性の値*/
\r
1213 attrNameSpace: null,
\r
1218 /*もともと属性がターゲットの要素につけられていたかどうか*/
\r
1221 /*attributeType属性がCSSだったときや、autoで属性名がCSSプロパティ名だったときには、true*/
\r
1224 /*計算モードdicreteやsplineなど*/
\r
1227 /*指定した要素の属性値を取得するメソッド*/
\r
1228 getAttr: function(/*string*/ name, def) {
\r
1229 var nameSpace = null;
\r
1230 if (name.indexOf("xlink:") > -1) {
\r
1231 nameSpace = "http://www.w3.org/1999/xlink";
\r
1233 var s = this._ele.getAttributeNS(nameSpace, name);
\r
1234 if (this.element) {
\r
1235 var view = this.element.ownerDocument.defaultView;
\r
1236 if (s === "inherit") {
\r
1237 return view.getComputedStyle(this.element.parentNode, "").getPropertyValue(this.attrName);
\r
1238 } else if (s === "currentColor") {
\r
1239 return view.getComputedStyle(this._ele, "").getPropertyValue("color");
\r
1242 /*DOM Level2やIE11では、getAttributeNSメソッドは空文字列を返す。他のブラウザではnullを返すことが多い
\r
1244 * >the empty string if that attribute does not have a specified or default value
\r
1245 * http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-ElGetAttrNS*/
\r
1246 return (s || def);
\r
1249 _ele: document.documentElement,
\r
1251 /*指定された要素にvalues属性が付いているかどうかのチェックできるメソッド*/
\r
1252 hasAttrValues: function () {
\r
1253 var ele = this._ele;
\r
1257 return ( ele.hasAttribute("from") || ele.hasAttribute("to")
\r
1258 || ele.hasAttribute("by") || ele.hasAttribute("values") );
\r
1262 /*後述のsetAttributeメソッドで使うキャッシュ
\r
1263 * これを使うことで、二度同じ値を設定せずにすむので、高速化できる*/
\r
1266 /*アニメーションするために、対象の属性やプロパティを変化させるメソッド*/
\r
1267 setAttribute: function (value) {
\r
1268 var attrName = this.attrName;
\r
1272 if (this.__cacheAttr === value) {
\r
1273 /*同じ値であれば、再設定しない*/
\r
1276 this.__cacheAttr = value;
\r
1278 var ele = this.element;
\r
1280 /*スタイルシートのプロパティを上書きしておく*/
\r
1281 ele.style.setProperty(attrName, value, "");
\r
1283 ele.setAttributeNS(this.attrNameSpace, attrName, value);
\r
1285 value = attrName = ele = void 0;
\r
1288 /*setAttributeメソッドとは逆の効果で、無効化させるメソッド*/
\r
1289 removeAttribute: function () {
\r
1290 var attrName = this.attrName;
\r
1294 var ele = this.element;
\r
1295 if (this.isDefault) {
\r
1296 this.setAttribute(this.defaultValue);
\r
1298 /*初期段階でターゲットの要素に属性が指定されていない場合は、
\r
1299 * 現在の属性値を削除するだけでよい*/
\r
1300 ele.removeAttributeNS(this.attrNameSpace, attrName);
\r
1301 /*スタイルシートのプロパティも削除しておく。removePropertyでないことに注意*/
\r
1302 this.isCSS && ele.style.setProperty(attrName, this.defaultValue, "");
\r
1304 this.__cacheAttr = "";
\r
1305 value = attrName = ele = void 0;
\r
1308 /*アニメーションの対象となる要素を値として返すメソッド
\r
1309 * もっぱら、pushメソッドで使われる*/
\r
1310 initTargetElement: function() {
\r
1311 var ele = this._ele;
\r
1312 var s = ele.parentNode || null;
\r
1313 var id = ele.getAttribute("xlink:href");
\r
1314 /*getAttributeNSメソッドでうまくいかなかったため、NSなしで代用*/
\r
1316 return ele.ownerDocument.getElementById(id.slice(1));
\r
1318 if ( id = ele.getAttributeNS(null, "targetElement") ) {
\r
1319 return ele.ownerDocument.getElementById(id);
\r
1324 /*repeatイベントの発火時刻リスト
\r
1325 * setSmilEventメソッドを見よ*/
\r
1329 * setSmilEventメソッドを見よ*/
\r
1332 /*SMILイベント関連を発火させるためのメソッド
\r
1333 * もっぱら、$attributeオブジェクトのpush メソッドで使われる*/
\r
1334 setSmilEvent: function($list) {
\r
1335 $list.addEvent("begin", function($list) {
\r
1336 var target = this._ele,
\r
1338 /*IE11のために、MouseEventsでSMILEventsの代用をする*/
\r
1339 var evt = target.ownerDocument.createEvent("MouseEvents");
\r
1340 evt.initMouseEvent("beginEvent" ,true, true, window, detail, 0, 0, 0, 0, false, false, false, false, 0, target);
\r
1341 target.dispatchEvent(evt);
\r
1342 /*repeatイベントのために、_repeatListプロパティを初期化する*/
\r
1343 var list = this._repeatList = [],
\r
1344 active = $list.activeTime,
\r
1345 begin = $list.begin,
\r
1346 simpleDuration = this.timeline.simpleDuration;
\r
1347 if (simpleDuration && (simpleDuration !== active)
\r
1348 && (active !== Number.MAX_VALUE) ) {
\r
1349 /*活動継続時間と単純持続時間が異なるとき、repeatイベントを設定*/
\r
1350 for (var m= simpleDuration, n=1;m < active ; m+=simpleDuration) {
\r
1353 /*リピートの回数 (n >= 1)*/
\r
1361 $list.addEvent("play", function($list) {
\r
1362 var target = this._ele,
\r
1364 frame = $list.currentFrame,
\r
1365 list = this._repeatList;
\r
1366 if (!list.length) {
\r
1369 for (var i=0;i<list.length;++i) {
\r
1370 if ( (this._repaetCount >= i+1)
\r
1371 || (list[i].frame >= frame) ) {
\r
1372 this._repeatCount = detail;
\r
1375 detail = list[i].count;
\r
1376 var evt = target.ownerDocument.createEvent("MouseEvents");
\r
1377 evt.initMouseEvent("repeatEvent" ,true, true, window, detail, 0, 0, 0, 0, false, false, false, false, 0, target);
\r
1378 target.dispatchEvent(evt);
\r
1382 $list.addEvent("end", function() {
\r
1383 var target = this._ele,
\r
1385 var evt = target.ownerDocument.createEvent("MouseEvents");
\r
1386 evt.initMouseEvent("endEvent" ,true, true, window, detail, 0, 0, 0, 0, false, false, false, false, 0, target);
\r
1387 target.dispatchEvent(evt);
\r
1391 /*引数で指定した要素 ele の属性を解析して、フレームに追加する*/
\r
1392 push: function(/*Element Node*/ ele) {
\r
1393 if (!ele || !ele.hasAttribute) {
\r
1397 this.__cacheAttr = "";
\r
1400 /*getAttrメソッドとhasAttrValuesメソッドで必要*/
\r
1403 /*initTargetElementメソッドを使って、elementプロパティの初期化*/
\r
1404 this.element = this.initTargetElement();
\r
1406 if (!this.hasAttrValues()) {
\r
1407 /*from属性、to、by、values属性が指定されていない場合、アニメーションの効果が出ないように調整する
\r
1408 *SMILアニメーションの仕様を参照
\r
1410 *>if none of the from, to, by or values attributes are specified, the animation will have no effect
\r
1411 *「3.2.2. Animation function values」より引用
\r
1412 *http://www.w3.org/TR/2001/REC-smil-animation-20010904/#AnimFuncValues
\r
1418 this.attrName = this.getAttr("attributeName", "");
\r
1419 var attrName = this.attrName;
\r
1422 var attrType = this.getAttr("attributeType", "auto");
\r
1423 var computedStyle = this.element && this.element.ownerDocument.defaultView.getComputedStyle(this.element, "");
\r
1424 if ( (attrType === "CSS")
\r
1425 || ( (attrType === "auto") && this.element
\r
1426 && computedStyle.getPropertyValue(attrName)
\r
1427 && !/^(width|height|transform)$/.test(attrName)
\r
1430 this.isCSS = true;
\r
1433 /*xlink関連の属性のときは名前空間を変更しておく*/
\r
1434 if (attrName.indexOf("xlink") > -1) {
\r
1435 this.attrNameSpace = "http://www.w3.org/1999/xlink";
\r
1437 /*thiseleはターゲットとなる要素(たとえば、親要素)*/
\r
1438 var thisele = this.element;
\r
1440 /*規定値を設定しておく。属性を初期化するときに使われる*/
\r
1441 this._ele = thisele;
\r
1442 this.isDefault = thisele.hasAttributeNS(this.attrNameSpace, attrName);
\r
1443 this.defaultValue = this.getAttr(attrName,
\r
1444 computedStyle.getPropertyValue(attrName) );
\r
1445 this._ele = ele; /*_eleプロパティを元に戻しておく*/
\r
1447 /*eleの属性の値を、それぞれオブジェクトに割り当て*/
\r
1448 var $frame = base("$frame"),
\r
1449 begin = $frame.$begin,
\r
1450 frame = begin.up().mix( {
\r
1451 /*targetプロパティはbeginEventなどの発火で使う*/
\r
1453 eventTarget: (this.element || begin.eventTarget),
\r
1454 string: this.getAttr("begin", "0"),
\r
1455 $activate: begin.$activate.up().mix( {
\r
1456 dur: this.getAttr("dur", null),
\r
1457 end: begin.$end.up().mix( {
\r
1458 eventTarget: (this.element || begin.eventTarget),
\r
1459 string: this.getAttr("end", null)
\r
1461 repeatCount: this.getAttr("repeatCount", null),
\r
1462 repeatDur: this.getAttr("repeatDur", null),
\r
1463 min: this.getAttr("min", "0"),
\r
1464 max: this.getAttr("max", "indefinite")
\r
1466 } ).updateList().parse();
\r
1467 $frame.addLine(frame.$list.init());
\r
1469 /*beginElementメソッドを追加*/
\r
1470 var objList = frame.$list.addList(Number.MAX_VALUE),
\r
1471 /*endListのvalueプロパティには、活動継続フレーム数と開始フレーム数を足したものが入る*/
\r
1472 endList = frame.$list.addEndList(Number.MAX_VALUE);
\r
1473 ele.beginElement = (frame.string !== "indefinite") ? function(){}
\r
1475 objList.value = frame.begin = base("$frame").currentFrame;
\r
1476 frame.isResolved = true;
\r
1477 var evt = this.ownerDocument.createEvent("MouseEvents");
\r
1478 evt.initMouseEvent("beginEvent" ,true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, this);
\r
1479 this.dispatchEvent(evt);
\r
1481 /*endElementメソッドを追加*/
\r
1482 var endFrame = frame.$activate.end || {};
\r
1483 ele.endElement = (endFrame.string !== "indefinite") ? function(){}
\r
1485 if (frame.isResolved) {
\r
1486 endFrame.isResolved = true;
\r
1487 endList.value = base("$frame").currentFrame;
\r
1488 var evt = this.ownerDocument.createEvent("MouseEvents");
\r
1489 evt.initMouseEvent("endEvent" ,true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, this);
\r
1490 this.dispatchEvent(evt);
\r
1493 /*setFrameメソッドを使ったときの、再帰スタックの使いすぎを防ぐため*/
\r
1494 frame.timelines = [];
\r
1495 this.setSmilEvent(frame.$list);
\r
1496 begin = ele = void 0;
\r
1501 * values属性やfrom属性やto属性を処理するためのメソッド
\r
1502 * valuesは配列、それ以外の引数は文字列
\r
1503 * 返り値は、values属性は配列、それ以外の属性のときは、
\r
1504 * 自分自身となる$attributeオブジェクトのコピーを返す*/
\r
1505 setValues: function(values, from, to, by) {
\r
1506 var $from = this.$from,
\r
1507 s = [this.up().mix( {
\r
1508 to: $from.up().mix( {
\r
1513 values = values && values.split(";");
\r
1514 /*from属性はオプションなので、条件には付け加えない*/
\r
1515 if (values && values.length) {
\r
1516 /*values属性が指定された場合、他の属性は無視される
\r
1517 * W3C仕様 SMIL アニメーション 3.2.2. アニメーション関数の値*/
\r
1519 for (var i=1;i<values.length;++i) {
\r
1520 s.push( this.up().mix( {
\r
1521 to: $from.up().mix( {
\r
1525 sto = s[s.length-1].to;
\r
1526 sto.string = values[i];
\r
1527 sto.from.string = values[i-1];
\r
1531 sto.from.string = from || "0";
\r
1534 sto.from.string = from || "0";
\r
1535 var toNumList = sto.call(),
\r
1536 fromNumList = sto.from;
\r
1537 for (var i=0;i<toNumList.length;++i) {
\r
1539 toNumList[i] += fromNumList[i];
\r
1544 $from = sto = toNumList = fromNumList = void 0;
\r
1549 * 後述のsetKeyメソッドで使われる。keyTimes属性のエラーをチェックするメソッド
\r
1550 * 属性値にエラーがあれば、trueを返し、なければ、falseを返す*/
\r
1551 isKeyError: function(/*number*/ keyLength, /*number*/toLength) {
\r
1552 return !!(keyLength && (keyLength !== (toLength+1)) );
\r
1556 * 引数の要素のkeyTimes属性やkeySplines属性を処理するためのメソッド
\r
1557 * 必要な他の属性処理はsetValuesメソッドに任せている*/
\r
1558 setKey: function(ele) {
\r
1560 var to = this.setValues(this.getAttr("values", null),
\r
1561 this.getAttr("from", null),
\r
1562 this.getAttr("to", null),
\r
1563 this.getAttr("by", null) ),
\r
1564 toLength = to ? to.length : 0,
\r
1565 keyTimes = this.getAttr("keyTimes", null),
\r
1566 keySplines = this.getAttr("keySplines", null),
\r
1568 splines = keySplines && keySplines.split(";"),
\r
1569 isDiscrete = (this.mode === "discrete"),
\r
1571 if (!isDiscrete && keyTimes && to) {
\r
1572 keys = this.$from.numList.call( {
\r
1575 /*keysTime属性の最初は0でなければならない。そうでなければエラー(SVG 1.1 2ndの仕様を参照)*/
\r
1576 if (keys.length && (keys[0] !== 0)) {
\r
1579 /*toオブジェクトはtoとfromで一組となっているのでlengthが加算される*/
\r
1580 if (this.isKeyError(keys.length, toLength)) {
\r
1581 /*keyTimes属性とvalues属性のリストの個数が合致しない場合、アニメーションの効果がない
\r
1582 * 仕様を参照 SMIL Animation 3.2.3. Animation function calculation modes
\r
1583 * http://www.w3.org/TR/smil-animation/#AnimFuncCalcMode*/
\r
1585 /*ただし、animateMotion要素においては、keyPoints属性が
\r
1586 * values属性のリストよりも優先されるため、
\r
1587 * keyPoints属性があるときは、アニメーションの効果がある
\r
1589 * >Regarding determining the points which correspond to the ‘keyTimes’ attributes, the ‘keyPoints’ attribute overrides ‘path’, which overrides ‘values’
\r
1591 * http://www.w3.org/TR/SVG11/animate.html#AnimateMotionElement
\r
1595 for (var i=0;i<toLength;++i) {
\r
1596 to[i].keyTime = keys[i+1] - keys[i];
\r
1598 toiKeySplines = this.$from.numList.call( {
\r
1599 string: splines[i]
\r
1601 /*空配列を返すため、nullに変えておく*/
\r
1602 to[i].keySplines = toiKeySplines.length ? toiKeySplines : null;
\r
1605 } else if (!isDiscrete && to) {
\r
1606 var per = 1 / toLength;
\r
1607 for (var i=0;i<toLength;++i) {
\r
1608 to[i].keyTime = per;
\r
1610 toiKeySplines = this.$from.numList.call( {
\r
1611 string: splines[i]
\r
1613 to[i].keySplines = toiKeySplines.length ? toiKeySplines : null;
\r
1617 /*discreteモードの処理*/
\r
1619 keys = this.$from.numList.call( {
\r
1622 /*keysTime属性の最初は0でなければならない。そうでなければエラー(SVG 1.1 2ndの仕様を参照)*/
\r
1623 if (keys.length && (keys[0] !== 0)) {
\r
1626 if (this.isKeyError(keys.length, toLength)) {
\r
1629 for (var i=0;i<toLength;++i) {
\r
1630 to[i].keyTime = keys[i+1] - keys[i];
\r
1633 var per = 1 / (toLength+1);
\r
1634 for (var i=0;i<toLength;++i) {
\r
1635 to[i].keyTime = per;
\r
1638 /*toオブジェクトが足らないので、一つ追加しておく*/
\r
1639 to.push( to[toLength-1].up().mix( function(){
\r
1643 this.keyTime = 1-keys[keys.length-1];
\r
1645 call: function() {
\r
1646 return function (t) {
\r
1647 return isNaN(t) ? this.string
\r
1648 : this.to.advance(1);
\r
1654 if (this.mode === "paced") {
\r
1657 to.forEach( function(x) {
\r
1658 norm += x.to.distance(x.to.from);
\r
1660 to.forEach( function(x) {
\r
1664 ele = keyTimes = keys = per = splines = void 0;
\r
1667 } ).up("$setElement").mix( {
\r
1672 /*後述のinitializeメソッドで使う要素リスト
\r
1673 * getElementsByTagNameNSメソッドの返り値をArray化したことを想定*/
\r
1676 /*何番目からelementListを処理しているかの数値*/
\r
1677 numberOfElemList: 0,
\r
1681 * 初期化処理を分散させるために使う*/
\r
1682 initialize: function() {
\r
1683 var eles = this.elementList;
\r
1684 if (!eles || !eles.length) {
\r
1687 var length = this.numberOfElemList+50;
\r
1688 for (var i=length-50; i<length; ++i) {
\r
1689 if (eles.length <= i) {
\r
1690 this.elementList = null;
\r
1693 this.up().init(eles[i]);
\r
1695 this.numberOfElemList += 50;
\r
1696 eles = length = void 0;
\r
1699 /*initメソッドで使われるアニメーション関数*/
\r
1700 _setFrame: function ($list) {
\r
1701 this.setAttribute(this.to);
\r
1704 /*開始を設定されたタイムライン ($beginオブジェクト)*/
\r
1705 timeline: base("$frame").$begin,
\r
1708 _setEndFrame: function ($list) {
\r
1709 /*removeの場合、アニメーションを凍結せずに、もとに戻す*/
\r
1710 if (this.fill === "remove") {
\r
1711 this.removeAttribute();
\r
1715 /*アニメーションの呈示値を呼び出す関数*/
\r
1716 tocall: function() {},
\r
1718 init: function(ele) {
\r
1719 var line = this.push(ele);
\r
1720 if (ele && ele.getAttributeNS) {
\r
1722 this.to = this.getAttr("to", "");
\r
1723 this.fill = this.getAttr("fill", "remove");
\r
1724 if ( (this.getAttr("attributeName", "") === "font-size")
\r
1725 && /\d\s*$/.test(this.to) ) {
\r
1729 var thisele = this.element;
\r
1730 if (line && thisele) {
\r
1731 this.timeline = line;
\r
1732 /*$begin.$listのイベントに属性処理を追加*/
\r
1733 line.$list.addEvent("begin", this._setFrame.bind(this));
\r
1734 line.$list.addEvent("play", this._setFrame.bind(this));
\r
1735 line.$list.addEvent("end", this._setEndFrame.bind(this));
\r
1736 /*アニメーションが再起動する可能性もあるため、$listのstateプロパティはここで初期化*/
\r
1737 line.$list.state = line.$list.WAITING;
\r
1739 line = thisele = void 0;
\r
1741 }).up("$animateElement").mix( {
\r
1745 /*進捗率advanceから、呈示値を求める*/
\r
1746 tocall: function(advance) {
\r
1747 var tf = this.funcs;
\r
1748 if (this.mode !== "discrete") {
\r
1749 for (var i=0;i<tf.length;++i) {
\r
1751 /*keyTime(keyTimes属性で指定されたような値)で実行するかどうかを判別*/
\r
1752 if (tfi.endKeyTime >= advance) {
\r
1753 return tfi(advance - tfi.startKeyTime);
\r
1758 for (var i=0;i<tf.length;++i) {
\r
1760 if (advance >= tfi.startKeyTime) {
\r
1761 result = tfi(advance);
\r
1764 advance = tf = tfi = void 0;
\r
1767 tf = i = tfi = void 0;
\r
1771 _setFrame: function($list) {
\r
1772 var currentFrame = $list.currentFrame;
\r
1774 *advanceは継続時間内での、進捗率
\r
1775 * 仕様を参照 http://www.w3.org/TR/smil-animation/#AnimFuncValues
\r
1776 *進捗率advanceは、durationと進捗フレーム数とを割った余り(REMAINDER)で算出する
\r
1777 * 仕様を参照 SMIL Animation 3.6.2 Interval timing
\r
1778 * http://www.w3.org/TR/2001/REC-smil-animation-20010904/#IntervalTiming*/
\r
1779 var line = this.timeline,
\r
1780 duration = line.simpleDuration,
\r
1781 /*単純継続時間が不定の場合、補間はせずに初期値が採用されるため、advanceは0となる
\r
1782 * 仕様を参照 SMIL Animation 3.2.2. Animation function values のInterpolation and indefinite simple durations
\r
1783 * http://www.w3.org/TR/2001/REC-smil-animation-20010904/#AnimFuncValues*/
\r
1784 advance = duration ? ( (currentFrame - $list.begin) % duration ) / duration
\r
1786 this.setAttribute(this.tocall(advance));
\r
1787 line = duration = advance = void 0;
\r
1790 /*凍結処理をするために、進捗率を最後まで進めて調整するメソッド
\r
1792 * もっぱら、_setEndFrameメソッドで使われる*/
\r
1793 getAdvanceEnd: function($list) {
\r
1794 var line = this.timeline,
\r
1795 duration = line.simpleDuration;
\r
1797 var time = (line.activeTime > $list.beginEnd) ? $list.beginEnd
\r
1798 : line.activeTime;
\r
1799 var advance = ( time % duration ) / duration;
\r
1800 /*例外が発生するため、進捗率が1を超えないように処理*/
\r
1801 advance = (advance > 1) ? 1 : advance;
\r
1802 /*活動継続時間と単純継続時間が一致すると、余りは0となるため以下の処理*/
\r
1803 advance = advance || 1;
\r
1810 /*_setEndFrameメソッドは、終了処理と凍結作業をする*/
\r
1811 _setEndFrame: function($list) {
\r
1812 /*上書きされたメソッドを呼び出してアニメーションの凍結作業をする*/
\r
1813 if (this.fill === "freeze") {
\r
1814 this.setAttribute(this.tocall( this.getAdvanceEnd($list) ));
\r
1815 line = duration = advance = void 0;
\r
1817 this.removeAttribute();
\r
1821 /*getAttrメソッドをオーバライド*/
\r
1822 getAttr: function (name, def) {
\r
1823 var s= this.$attribute.getAttr.apply(this, arguments);
\r
1824 if ((name === "from") && !s && this.defaultValue) {
\r
1825 /*from属性がない場合、対象要素の既定値を返す*/
\r
1826 return this.defaultValue;
\r
1832 aliceblue: [240,248,255],
\r
1833 antiquewhite: [250,235,215],
\r
1834 aqua: [0,255,255],
\r
1835 aquamarine: [127,255,212],
\r
1836 azure: [240,255,255],
\r
1837 beige: [245,245,220],
\r
1838 bisque: [255,228,196],
\r
1840 blanchedalmond:[255,235,205],
\r
1842 blueviolet: [138,43,226],
\r
1843 brown: [165,42,42],
\r
1844 burlywood: [222,184,135],
\r
1845 cadetblue: [95,158,160],
\r
1846 chartreuse: [127,255,0],
\r
1847 chocolate: [210,105,30],
\r
1848 coral: [255,127,80],
\r
1849 cornflowerblue:[100,149,237],
\r
1850 cornsilk: [255,248,220],
\r
1851 crimson: [220,20,60],
\r
1852 cyan: [0,255,255],
\r
1853 darkblue: [0,0,139],
\r
1854 darkcyan: [0,139,139],
\r
1855 darkgoldenrod:[184,134,11],
\r
1856 darkgray: [169,169,169],
\r
1857 darkgreen: [0,100,0],
\r
1858 darkgrey: [169,169,169],
\r
1859 darkkhaki: [189,183,107],
\r
1860 darkmagenta: [139,0,139],
\r
1861 darkolivegreen:[85,107,47],
\r
1862 darkorange: [255,140,0],
\r
1863 darkorchid: [153,50,204],
\r
1864 darkred: [139,0,0],
\r
1865 darksalmon: [233,150,122],
\r
1866 darkseagreen: [143,188,143],
\r
1867 darkslateblue:[72,61,139],
\r
1868 darkslategray:[47,79,79],
\r
1869 darkslategrey:[47,79,79],
\r
1870 darkturquoise:[0,206,209],
\r
1871 darkviolet: [148,0,211],
\r
1872 deeppink: [255,20,147],
\r
1873 deepskyblue: [0,191,255],
\r
1874 dimgray: [105,105,105],
\r
1875 dimgrey: [105,105,105],
\r
1876 dodgerblue: [30,144,255],
\r
1877 firebrick: [178,34,34],
\r
1878 floralwhite: [255,250,240],
\r
1879 forestgreen: [34,139,34],
\r
1880 fuchsia: [255,0,255],
\r
1881 gainsboro: [220,220,220],
\r
1882 ghostwhite: [248,248,255],
\r
1883 gold: [255,215,0],
\r
1884 goldenrod: [218,165,32],
\r
1885 gray: [128,128,128],
\r
1886 grey: [128,128,128],
\r
1888 greenyellow: [173,255,47],
\r
1889 honeydew: [240,255,240],
\r
1890 hotpink: [255,105,180],
\r
1891 indianred: [205,92,92],
\r
1892 indigo: [75,0,130],
\r
1893 ivory: [255,255,240],
\r
1894 khaki: [240,230,140],
\r
1895 lavender: [230,230,250],
\r
1896 lavenderblush:[255,240,245],
\r
1897 lawngreen: [124,252,0],
\r
1898 lemonchiffon: [255,250,205],
\r
1899 lightblue: [173,216,230],
\r
1900 lightcoral: [240,128,128],
\r
1901 lightcyan: [224,255,255],
\r
1902 lightgoldenrodyellow:[250,250,210],
\r
1903 lightgray: [211,211,211],
\r
1904 lightgreen: [144,238,144],
\r
1905 lightgrey: [211,211,211],
\r
1906 lightpink: [255,182,193],
\r
1907 lightsalmon: [255,160,122],
\r
1908 lightseagree: [32,178,170],
\r
1909 lightskyblue: [135,206,250],
\r
1910 lightslategray:[119,136,153],
\r
1911 lightslategrey:[119,136,153],
\r
1912 lightsteelblue:[176,196,222],
\r
1913 lightyellow: [255,255,224],
\r
1915 limegreen: [50,205,50],
\r
1916 linen: [250,240,230],
\r
1917 magenta: [255,0,255],
\r
1918 maroon: [128,0,0],
\r
1919 mediumaquamarine:[102,205,170],
\r
1920 mediumblue: [0,0,205],
\r
1921 mediumorchid: [186,85,211],
\r
1922 mediumpurple: [147,112,219],
\r
1923 mediumseagreen:[60,179,113],
\r
1924 mediumslateblue:[123,104,238],
\r
1925 mediumspringgreen:[0,250,154],
\r
1926 mediumturquoise:[72,209,204],
\r
1927 mediumvioletred:[199,21,133],
\r
1928 midnightblue: [25,25,112],
\r
1929 mintcream: [245,255,250],
\r
1930 mistyrose: [255,228,225],
\r
1931 moccasin: [255,228,181],
\r
1932 navajowhite: [255,222,173],
\r
1934 oldlace: [253,245,230],
\r
1935 olive: [128,128,0],
\r
1936 olivedrab: [107,142,35],
\r
1937 orange: [255,165,0],
\r
1938 orangered: [255,69,0],
\r
1939 orchid: [218,112,214],
\r
1940 palegoldenrod: [238,232,170],
\r
1941 palegreen: [152,251,152],
\r
1942 paleturquoise: [175,238,238],
\r
1943 palevioletred: [219,112,147],
\r
1944 papayawhip: [255,239,213],
\r
1945 peachpuff: [255,218,185],
\r
1946 peru: [205,133,63],
\r
1947 pink: [255,192,203],
\r
1948 plum: [221,160,221],
\r
1949 powderblue: [176,224,230],
\r
1950 purple: [128,0,128],
\r
1952 rosybrown: [188,143,143],
\r
1953 royalblue: [65,105,225],
\r
1954 saddlebrown: [139,69,19],
\r
1955 salmon: [250,128,114],
\r
1956 sandybrown: [244,164,96],
\r
1957 seagreen: [46,139,87],
\r
1958 seashell: [255,245,238],
\r
1959 sienna: [160,82,45],
\r
1960 silver: [192,192,192],
\r
1961 skyblue: [135,206,235],
\r
1962 slateblue: [106,90,205],
\r
1963 slategray: [112,128,144],
\r
1964 slategrey: [112,128,144],
\r
1965 snow: [255,250,250],
\r
1966 springgreen: [0,255,127],
\r
1967 steelblue: [70,130,180],
\r
1968 tan: [210,180,140],
\r
1969 teal: [0,128,128],
\r
1970 thistle: [216,191,216],
\r
1971 tomato: [255,99,71],
\r
1972 turquoise: [64,224,208],
\r
1973 violet: [238,130,238],
\r
1974 wheat: [245,222,179],
\r
1975 white: [255,255,255],
\r
1976 whitesmoke: [245,245,245],
\r
1977 yellow: [255,255,0],
\r
1978 yellowgreen: [154,205,50]
\r
1981 setAdd: function (ele, to) {
\r
1982 /*additive属性がsum (加法アニメーション)の場合*/
\r
1983 if (ele.getAttributeNS(null, "additive") === "sum") {
\r
1984 var attrValue = ele.parentNode.getAttributeNS(null, this.attrName);
\r
1985 ele.addEventListener("beginEvent", function(evt) {
\r
1986 to.forEach( function(x) {
\r
1987 x.to.setAdditive(attrValue);
\r
1992 setAccum: function (ele, to) {
\r
1993 /*accumulate属性がsum (蓄積アニメーション)の場合*/
\r
1994 if (ele.getAttributeNS(null, "accumulate") === "sum") {
\r
1995 ele.addEventListener("repeatEvent", function(evt) {
\r
1996 to.forEach( function(x) {
\r
1998 x.to.setAccumulate(evt.detail);
\r
2004 /*displayなど補間をしなくてもよい属性への対処するためのメソッド*/
\r
2005 setString: function() {
\r
2006 if (/^(?:display|class|edgeMode|(gradient|marker|pattern|maskContent|mask|patternContent|primitive)Units|in|in2|method|mode|operator|preserveAspectRatio|result|spacing|spreadMethod|stitchTiles|target|type|xlink:href|yChannelSelector|color-interpolation|(clip|fill)-rule|cursor|filter|font-(family|stretch|style|variant)|image-rendering|marker-(end|mid|start)|mask|overflow|pointer-events|shape-rendering|stroke-(linecap|linejoin)|text-(anchor|decoration|rendering)|visibility)$/.test(this.attrName) ) {
\r
2007 this.mode = "discrete";
\r
2011 /*小数点以下の桁数を指定するため、setValuesメソッドをオーバライドする*/
\r
2013 setValues: function() {
\r
2014 var s = this.$attribute.setValues.apply(this, arguments),
\r
2015 degits = this.degits;
\r
2016 s && s.forEach(function(x) {
\r
2017 x.to.degit = degits;
\r
2023 /*#から始まる文字列#aabbcc、例えばを、rgb(.., .., ..,)形式へと変換するためのメソッド
\r
2025 toRGB: function(rgbColor) {
\r
2026 var keyword = this._keywords[rgbColor];
\r
2028 return "rgb(" + keyword.join(", ") + ")";
\r
2030 if (rgbColor && (rgbColor[0] === "#")) { //#を含む場合
\r
2032 _parseInt = parseInt;
\r
2033 if (rgbColor.length < 5) {
\r
2034 var r = rgbColor[1],
\r
2037 rgbColor = "#" + r + r + g + g + b + b;
\r
2039 rgbColor.match(/\#(\w{2})(\w{2})(\w{2})/);
\r
2040 s += _parseInt(RegExp.$1, 16)
\r
2042 + _parseInt(RegExp.$2, 16)
\r
2044 + _parseInt(RegExp.$3, 16)
\r
2046 r = g = b = void 0;
\r
2053 * onメソッドについては、base.jsを参照のこと*/
\r
2054 } ).on ("init", function(ele) {
\r
2057 /*関数toRGBはrgbColor形式への変換処理で使う*/
\r
2058 toRGB = function(x) { return x; },
\r
2059 isColor = /^(?:fill|stroke|stop-color|color)$/.test(this.attrName);
\r
2061 /*通常は、小数点以下の桁数を既定値の1桁とする
\r
2062 *RGB形式では補間に、小数を使わないため、0桁に設定
\r
2063 * (なお、この作業は、setKeyメソッドの前に済ませておく必要がある)*/
\r
2065 /*たとえば、fill属性などである場合には、rgbColor形式への変換処理をする*/
\r
2066 toRGB = this.toRGB.bind(this);
\r
2069 this.mode = ele.getAttributeNS(null, "calcMode") || "linear";
\r
2071 to = this.setKey(ele);
\r
2074 var aa = new Array(to.length);
\r
2075 for (var i=0;i<to.length;++i) {
\r
2077 x.to.string = toRGB(x.to.string);
\r
2078 x.to.from.string = toRGB(x.to.from.string);
\r
2080 /*x.keyTimeプロパティは区間を示しているため、区切り時刻に変換しておく
\r
2081 * startKeyTimeプロパティは区間のスタート時点
\r
2082 * endKeyTimeプロパティは区間のエンド地点*/
\r
2083 s.startKeyTime = keyTime;
\r
2084 keyTime = s.endKeyTime = keyTime + x.keyTime;
\r
2087 this.funcs = aa.filter( function(s) {
\r
2088 if (!this.timeline.isResolved) {
\r
2089 /*begin属性などにイベントを設定していた(未解決の)場合、後のs(0.1)がうまく作動せず、
\r
2090 * 例外を出してしまうため、ここで返しておく*/
\r
2093 /*splineモードで、かつ、アニメーションが無効な関数の場合は配列から外しておく
\r
2094 * 無効な場合に関しては、$calcModeオブジェクトのcallメソッドを参照*/
\r
2095 return (this.mode !== "spline") || !s.isNotAnimate;
\r
2097 this.setAdd(ele, to);
\r
2098 this.setAccum(ele, to);
\r
2100 keyTime = keywords = toRGB = isColor = void 0;
\r
2102 /*$animateTranformElementオブジェクト
\r
2103 * animateTransform要素に関連するオブジェクト*/
\r
2104 .up("$animateTransformElement")
\r
2106 /*__transformListで何番目のアイテムかを示すプロパティ*/
\r
2110 type: "translate",
\r
2112 /*attributeName属性の値はtransformで固定*/
\r
2113 attrName: "transform",
\r
2115 /*明確にisCSSプロパティを設定しておくことで、プロトタイプチェーンを使わせず最適化*/
\r
2118 /*additive属性の値がsumのときにtrue*/
\r
2121 /*transform属性の値に使われる数値は精密であることが求められるため、
\r
2122 *小数点以下の桁数を決めるdegitsプロパティの値も大きくしておく*/
\r
2125 /*tocallメソッド(後述の$motionElementオブジェクトも含む)で使うメソッド
\r
2126 * __transformListの値を結合して、文字列として返す*/
\r
2127 joinList: function(s) {
\r
2128 var playList = this.element.__transformList;
\r
2129 for (var i=0;i<playList.length;++i) {
\r
2130 var item = playList[i],
\r
2131 value = item.value;
\r
2134 } else if (item.isPlaying) {
\r
2135 /*他のanimateTransform要素がadditive属性の値にreplaceをすでに設定していた、
\r
2136 *かつ、その要素がアニメーション再生中の場合はsを初期化*/
\r
2143 /*$animateElementオブジェクトのtocallメソッドをオーバライド*/
\r
2144 tocall: function (advance) {
\r
2145 if (this.numberOfList < 0) {
\r
2146 throw new Error("Number of The List Error");
\r
2148 var currentItem = this.element.__transformList[this.numberOfList];
\r
2149 currentItem.value = this.type+ "(" +this.$animateElement.tocall.call(this, advance)+ ")";
\r
2150 currentItem.isPlaying = true;
\r
2151 currentItem.isSum = this.isSum;
\r
2152 return this.joinList(this.defaultValue || "");
\r
2155 /*後の_setFrameメソッドで使うダミー*/
\r
2156 __setAttribute: function(){},
\r
2158 _setFrame: function($list) {
\r
2159 var currentFrame = $list.currentFrame;
\r
2160 /*__transformListの中で、自分より後の項目に再生中のものがあれば、
\r
2161 *アニメーションを再生させないで、後に続く項目に任せる*/
\r
2162 var list = this.element.__transformList,
\r
2163 isPostActive = false,
\r
2164 length = list.length;
\r
2165 if ( (length !== 1) && (this.numberOfList < (length - 1) ) ) {
\r
2166 /*リストの項目が一つだけであったり、自分自身が最後尾であれば、アニメーションを再生する
\r
2167 * また、後に続く項目で再生中のものがあったら、今回は再生しない*/
\r
2168 for (var i=this.numberOfList+1;i<length;++i) {
\r
2169 if (list[i].isPlaying) {
\r
2170 isPostActive = true;
\r
2174 /*__setAttributeはダミーなので、アニメーションが再生されない*/
\r
2175 this.setAttribute = isPostActive ? this.__setAttribute
\r
2176 : this.$animateElement.setAttribute;
\r
2177 /*上書きされたメソッドを呼び出す*/
\r
2178 this.$animateElement._setFrame.call(this, $list);
\r
2181 _setEndFrame: function($list) {
\r
2182 var list = this.element.__transformList;
\r
2186 var item = list[this.numberOfList];
\r
2187 if (this.fill === "remove") {
\r
2190 } else if (!this.isSum) {
\r
2191 /*凍結処理をしないで、かつ、元の状態に戻して、効果が出ないようにする*/
\r
2192 item.isPlaying = false;
\r
2194 /*凍結処理をしないで、かつ、効果を出すが、変形させないようにする*/
\r
2195 item.value = "translate(0)";
\r
2199 * 自前のtocallメソッドはvalueプロパティを書きかえてしまうため、
\r
2201 item.value = this.type+ "("
\r
2202 +this.$animateElement.tocall.call( this, this.getAdvanceEnd($list) )+ ")";
\r
2204 this.setAttribute( this.joinList(this.defaultValue || "") );
\r
2208 /*setAddメソッドのオーバライド
\r
2209 * additive属性のsumに対する振る舞いが異なるため*/
\r
2210 setAdd: function() {},
\r
2212 .on("init", function (ele) {
\r
2213 if (!ele || !ele.parentNode) {
\r
2216 this.getAttr = this.$attribute.getAttr;
\r
2217 this.type = this.getAttr("type", "translate");
\r
2218 this.attrName = "transform";
\r
2219 var parent = this.element;
\r
2220 this.isDefault = parent.hasAttributeNS(null, "transform");
\r
2221 this.defaultValue = parent.getAttributeNS(null, "transform") || "";
\r
2222 this.isSum = (this.getAttr("additive", "replace") === "sum");
\r
2223 if (!parent.__transformList) {
\r
2224 parent.__transformList = [];
\r
2225 this.numberOfList = -1;
\r
2227 if (this.hasAttrValues()
\r
2228 && (this.numberOfList < 0) ) {
\r
2229 /*もし、今まで、このオブジェクトで、initメソッドを実行していなければ*/
\r
2230 this.numberOfList = parent.__transformList.length;
\r
2231 /*isPlayingプロパティはアニメーション再生終了後でもtrueとなるので注意*/
\r
2232 parent.__transformList.push( {isPlaying: false,
\r
2233 value: "translate(0)",
\r
2238 .up("$motionElement")
\r
2239 .mix( function() {
\r
2240 /*$animateTransformElementオブジェクトのでは、うまくいかないため、
\r
2241 * setRFrameとsetEndFrameメソッドを再定義*/
\r
2242 this._setFrame = this.$animateElement._setFrame;
\r
2243 this._setEndFrame = this.$animateElement._setEndFrame;
\r
2249 /*hasAttrValuesメソッドのオーバライド
\r
2250 * path属性などに対応させるため*/
\r
2251 hasAttrValues: function () {
\r
2252 if (this.$attribute.hasAttrValues.call(this)) {
\r
2255 return (this._ele.hasAttribute("keyPoints") || this._ele.hasAttribute("path")
\r
2256 || this._ele.getElementsByTagNameNS(this.path.namespaceURI, "mpath").length);
\r
2260 path: document.createElementNS("http://www.w3.org/2000/svg", "path"),
\r
2264 /*tocallメソッドのオーバライド*/
\r
2265 tocall: function(advance) {
\r
2266 /*モーションは仕様の関係上、かならず、CTMの一番初めに置かれ、前置積となる
\r
2267 * なお、__transformListプロパティはtransform属性の値であり、CTMを表現する。
\r
2268 * 必ず、CTMの一番目に設定する*/
\r
2269 return ("translate(" +this.$animateElement.tocall.call(this, advance)+ ") "
\r
2270 + this.joinList(this.defaultValue || "")).trim();
\r
2273 /*図の現在の角度rを求めて、rotate(rの文字列(最後に括弧はいらない)で返すメソッド*/
\r
2274 getRotate: function(path, advanceLength, rotate) {
\r
2275 /*パスセグメントの数値を求めてから、動いている図形の傾き角度r(ラジアンではなく度数)を算出する*/
\r
2276 var length = path.getPathSegAtLength(advanceLength),
\r
2277 seg = path.pathSegList.getItem(length),
\r
2278 command = seg.pathSegTypeAsLetter,
\r
2279 /*pi*180ではEdgeでうまく作動しない(原因は不明)*/
\r
2281 if (command === "M") {
\r
2282 /*次のセグメントがどのコマンドによるかで、計算方式が違う*/
\r
2283 var nextSeg = path.pathSegList.getItem(length+1),
\r
2284 nextCommand = nextSeg.pathSegTypeAsLetter;
\r
2285 if (nextCommand === "M") {
\r
2287 } else if (nextCommand === "L") {
\r
2288 return ") rotate(" +(Math.atan2(nextSeg.y-seg.y, nextSeg.x-seg.x)/pi*180 + rotate)+ "";
\r
2289 } else if (nextCommand === "C") {
\r
2290 return ") rotate(" +(Math.atan2(nextSeg.y1-seg.y, nextSeg.x1-seg.x)/pi*180 + rotate)+ "";
\r
2292 } else if ((command === "L") && (length-1 >= 0)) {
\r
2293 var preSeg = path.pathSegList.getItem(length-1);
\r
2294 return ") rotate(" +(Math.atan2(seg.y-preSeg.y, seg.x-preSeg.x)/pi*180 + rotate)+ "";
\r
2295 } else if (command === "C") {
\r
2296 /*3次ベジェ曲線を微分する方法はニュートン法など数値解析の必要があるので、
\r
2297 * 以下の通り、別の方法を採用する。
\r
2298 * 現在位置から一歩進んだ曲線上の点Bをとり、それを、現在の点Aと結んで線分ABとしたとき、
\r
2299 * その直線の傾きからおおよその角度を求める*/
\r
2300 var point = path.getPointAtLength(advanceLength),
\r
2304 point = path.getPointAtLength(advanceLength+1);
\r
2305 return ") rotate(" +(Math.atan2(point.y-y, point.x-x)/pi*180 + rotate)+ "";
\r
2309 /*this.$animateElement.tocallメソッドを置き換えるためのメソッド
\r
2310 * mpath要素が指定されたときか、path属性のときにのみ使われる*/
\r
2311 _tocallForPath: function(advance) {
\r
2312 if (this.isKeyPoints) {
\r
2313 /*keyPoints属性の値に従って、advance値を決定する。
\r
2314 * なお、$animateElementはinitメソッドで書き換えているので、二重に呼び出す必要がある*/
\r
2315 advance = +this.$animateElement.$animateElement.tocall.call(this, advance);
\r
2317 var path = this.path,
\r
2318 advanceLength = advance * path.getTotalLength();
\r
2319 /*全体の距離から、現在進めている距離を算出して、そこから、現在点を導き出す*/
\r
2320 var point = path.getPointAtLength(advanceLength),
\r
2321 rotate = 0; //追加すべき角度
\r
2322 if (this.rotate === "0") {
\r
2323 return point.x+ "," +point.y;
\r
2324 } else if (this.rotate === "auto") {
\r
2326 } else if (this.rotate === "auto-reverse") {
\r
2329 rotate = +this.rotate;
\r
2331 return point.x+ "," +point.y + this.getRotate(path, advanceLength, rotate);
\r
2334 /*setValuesメソッドのオーバライド*/
\r
2335 setValues: function() {
\r
2336 var keyPoints = this.getAttr("keyPoints", null),
\r
2337 /*$animateElementプロパティは下記のinitメソッドで上書きされているため、
\r
2338 * $animateElementを別方法で呼び出す必要がある*/
\r
2339 superSetValues = this.$animateElement.$animateElement.setValues;
\r
2341 return superSetValues.call(this, keyPoints, null, null, null);
\r
2343 return superSetValues.apply(this, arguments);
\r
2347 .on("init", function (ele) {
\r
2348 if (!ele || !ele.parentNode) {
\r
2351 /*type属性で変更されないように($animateTransformElementのinitメソッドを参照のこと)*/
\r
2352 this.type = "translate";
\r
2353 /*isSumプロパティは常にtrueにしておく。animateTransform要素とは挙動が違うため
\r
2354 * また、$aniamteTransformElementのtocallメソッド参照*/
\r
2355 this.isSum = true;
\r
2356 this.mode = this.getAttr("mode", "paced");
\r
2357 this.rotate = this.getAttr("rotate", "0");
\r
2358 /*isKeyPointsプロパティはkeyPoints属性が設定されていたら真*/
\r
2359 this.isKeyPoints = ele.hasAttributeNS(null, "keyPoints");
\r
2360 if (this.isKeyPoints && !ele.hasAttributeNS(null, "path")) {
\r
2361 /*keyPoints属性がある場合は、path属性に指定がなければ、
\r
2362 * values属性などの値をpath属性に書いておく*/
\r
2363 var values = this.getAttr( "values", this.getAttr("from", "")+" L "+this.getAttr("to", "") );
\r
2364 ele.setAttributeNS( null, "path", "M " +values.replace(/;/g, " L ") );
\r
2366 this.path = this.path.cloneNode(true);
\r
2367 var mpath = ele.getElementsByTagNameNS(this.path.namespaceURI, "mpath");
\r
2368 /*$animateは後で、プロパティを書き換えるために使う。tocallメソッドも参照*/
\r
2369 var $animate = this.$animateElement;
\r
2370 if (mpath.length) {
\r
2371 var p = ele.ownerDocument.getElementById(
\r
2372 mpath[0].getAttributeNS("http://www.w3.org/1999/xlink", "href").slice(1)
\r
2374 p && this.path.setAttributeNS(null, "d", p.getAttributeNS(null, "d"));
\r
2375 this.$animateElement = $animate.up().mix ( {tocall: this._tocallForPath} );
\r
2376 } else if (ele.hasAttributeNS(null, "path")) {
\r
2377 this.path.setAttributeNS(null, "d", ele.getAttributeNS(null, "path"));
\r
2378 this.$animateElement = $animate.up().mix ( {tocall: this._tocallForPath} );
\r
2382 base("$getDocument").mix ( function() {
\r
2384 function getDocument()
\r
2386 var svg = document.getElementsByTagName("object"),
\r
2387 svgns = "http://www.w3.org/2000/svg";
\r
2389 for (var i=0;i<svg.length;++i) {
\r
2390 getElement( svg[i].getSVGDocument() );
\r
2393 /*SVG文書から呼び出されたときも処理する*/
\r
2394 getElement(document);
\r
2396 var id = __step(),
\r
2397 idstop = function() {
\r
2398 /*アニメーションを中止する関数*/
\r
2399 window.cancelAnimationFrame && cancelAnimationFrame(id);
\r
2401 base("$frame").on("pauseAnimation", idstop);
\r
2402 window.addEventListener("unload", idstop);
\r
2404 /*文書からアニメーション関連要素を取り出して、オブジェクトを初期化*/
\r
2405 function getElement (svgDoc) {
\r
2406 var $set = base("$calcMode").$attribute.$setElement,
\r
2407 $animate = $set.$animateElement,
\r
2408 frame = base("$frame");
\r
2409 init($set, "set");
\r
2410 init($animate, "animate");
\r
2411 init($animate.up(), "animateColor");
\r
2412 init($animate.$animateTransformElement, "animateTransform");
\r
2413 init($animate.$animateTransformElement.$motionElement, "animateMotion");
\r
2414 /*リンクのハッシュ読み取りで、ハイパーリンクのイベント処理
\r
2415 * たとえば、a要素のxlink:href="#hoge"で、<animate id="hoge"のとき、
\r
2416 * animate要素がハイパーリンク作動と同時に動くようになる
\r
2418 * ただし、SMIL アニメーションの仕様では、
\r
2419 * animate要素の開始時刻まで、時を進める操作をするだけ*/
\r
2420 svgDoc.defaultView.addEventListener("hashchange", function() {
\r
2421 var hash = svgDoc.defaultView.location.hash.slice(1);
\r
2422 svgDoc.getElementById(hash).beginElement();
\r
2424 function init (obj, name) {
\r
2425 /*あとでframe.initializeメソッドで呼び出すために準備しておく*/
\r
2426 var elist = svgDoc.getElementsByTagNameNS(svgns, name);
\r
2427 obj.numberOfElemList = 0;
\r
2428 if (elist.length > 0) {
\r
2429 obj.elementList = elist;
\r
2430 frame.objList.push(obj);
\r
2432 elist = obj = void 0;
\r
2437 window.addEventListener && window.addEventListener("load", getDocument);
\r
2439 this.step = __step;
\r
2440 function __step() {
\r
2441 /*EdgeはhasFeatureメソッドでtrueを返す*/
\r
2442 if (!document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Animation", "1.1")
\r
2443 || (window.navigator.userAgent.toLowerCase().indexOf("edge") > 0)) {
\r
2444 if (window.requestAnimationFrame && requestAnimationFrame) {
\r
2445 /*IE11やEdgeなどSMILアニメーションに対応していないブラウザ用*/
\r
2446 /*cancelはアニメーションの中止ハンドル*/
\r
2450 (function(frame) {
\r
2451 var _cancel = cancel; /*cancelのエイリアス*/
\r
2452 var step = function () {
\r
2453 if (!this.isPaused) {
\r
2456 this.initialize();
\r
2457 this.setFrame(frame);
\r
2460 _cancel.handle = requestAnimationFrame(step);
\r
2462 }.bind(base("$frame"));
\r
2463 _cancel.handle = requestAnimationFrame(step);
\r
2467 setInterval( (function(frame) {
\r
2468 var $f = base("$frame");
\r
2469 return function () {
\r
2472 $f.setFrame(frame);
\r
2479 //#endif // _SMIL_IDL_
\r