script.aculo.usを読み解く

第6回 effects.js(後編)基礎エフェクトの組み合わせからなる15種類の複合エフェクト

この記事を読むのに必要な時間:およそ 23.5 分

Effect.Shake

0655:Effect.Shake = function(element) {
0656:  element = $(element);
0657:  var options = Object.extend({
0658:    distance: 20,
0659:    duration: 0.5
0660:  }, arguments[1] || {});
0661:  var distance = parseFloat(options.distance);
0662:  var split = parseFloat(options.duration) / 10.0;
0663:  var oldStyle = {
0664:    top: element.getStyle('top'),
0665:    left: element.getStyle('left') };
0666:    return new Effect.Move(element,
0667:      { x:  distance, y: 0, duration: split, afterFinishInternal: function(effect) {
0668:    new Effect.Move(effect.element,
0669:      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
0670:    new Effect.Move(effect.element,
0671:      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
0672:    new Effect.Move(effect.element,
0673:      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
0674:    new Effect.Move(effect.element,
0675:      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
0676:    new Effect.Move(effect.element,
0677:      { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
0678:        effect.element.undoPositioned().setStyle(oldStyle);
0679:  }}) }}) }}) }}) }}) }});
0680:};
0681:

655~681行目のEffect.Shakeは,ブルブルと左右に震えるエフェクトです。左に動かすEffect.Moveと右に動かすそれを,交互に数珠つなぎにして作られています。

658行目で,振動の幅をデフォルトでは20pxにします。

659行目で,持続時間はデフォルトでは0.5秒です。

661行目で,options.distanceの値を浮動小数に変換しておきます。後でEffect.Moveにx軸移動量として渡すからです。

662行目で,持続時間を,移動の回数で割ります。

663~665行目で,要素の位置を取得します。エフェクト後に元に戻すためです。

666~679行目で,Effect.Moveを,afterFinishInternalというフックで数珠つなぎにしています。

右に20px動かします。左に40px動かします。右に40px動かします。左に40px動かします。右に40px動かします。左に20px動かします。これで原点に戻ってきました。

678行目で,最後のフックで,undoPositionedでpositionを元に戻し,要素の位置をエフェクト前の値に戻します。

ちなみに,以下のように,Effect.Moveでオプションでtransitonにsin関数を使う工夫をしても同様のものが実装できます。

Effect.YetanotherShake = function (element) {
  element = $(element);
  var options = Object.extend({distance:20, duration:0.5}, arguments[1] || {});
  var distance = parseFloat(options.distance) * 2;
  var oldStyle = {top:element.getStyle("top"), left:element.getStyle("left")};
  options = Object.extend({x:distance, y:0, transition:function(pos){
    return (Math.sin(6*pos*Math.PI)/2)
  }, afterFinish: function(effect) {
    effect.element.undoPositioned().setStyle(oldStyle)
  }},options);
  return new Effect.Move(element, options);
}

Effect.SlideDown

0682:Effect.SlideDown = function(element) {
0683:  element = $(element).cleanWhitespace();
0684:  // SlideDown need to have the content of the element wrapped in a container element with fixed height!
0685:  var oldInnerBottom = element.down().getStyle('bottom');
0686:  var elementDimensions = element.getDimensions();
0687:  return new Effect.Scale(element, 100, Object.extend({ 
0688:    scaleContent: false, 
0689:    scaleX: false, 
0690:    scaleFrom: window.opera ? 0 : 1,
0691:    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
0692:    restoreAfterFinish: true,
0693:    afterSetup: function(effect) {
0694:      effect.element.makePositioned();
0695:      effect.element.down().makePositioned();
0696:      if (window.opera) effect.element.setStyle({top: ''});
0697:      effect.element.makeClipping().setStyle({height: '0px'}).show(); 
0698:    },
0699:    afterUpdateInternal: function(effect) {
0700:      effect.element.down().setStyle({bottom:
0701:        (effect.dims[0] - effect.element.clientHeight) + 'px' }); 
0702:    },
0703:    afterFinishInternal: function(effect) {
0704:      effect.element.undoClipping().undoPositioned();
0705:      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
0706:    }, arguments[1] || { })
0707:  );
0708:};
0709:

682~709行目のEffect.SlideDownは,お店のシャッターを降ろすようなエフェクトで,Effect.BlindDownに似ていますが,要素の内容が吐き出されるように流れてくるのが違います。BlindDownと同じくEffect.Scaleを使っていても,そのぶん処理が少し複雑です。

683行目で,要素の内容からcleanWhitespaceで空白を取り除きます。文字列を流すときに邪魔になるからです。

685行目で,要素のdownメソッドで子要素を取得し、そのCSSのbottomプロパティの値を保存しておきます。エフェクト後に元に戻すためです。

686行目で,要素のサイズを取得します。

687行目で,Effect.Scaleを,倍率100%(等倍)でかけます。オプションは以下です。

688行目で,要素の内容のフォントサイズを変えません。

689行目で,横幅を変えません。

690行目で,大きさの初期値は1%です。0にしないのは,IEで起こるチラツキを防ぐためです。

691行目で,高さ,幅については先ほどのgetDimensionsで取得したものを基準にします。

692行目で,終了後に大きさを元に戻します。

693行目で,初期化後のフックで,次の処理をします。

694行目で,makePositionedで,要素のその子要素のpositionを'relative'にして,移動に備えます。

696行目で,positionをstaticからrelativeに変えたときのoperaの仕様に対応しているようです。この仕様については「prototype.jsを読み解く」第6回のmakePositionedの項に詳しく書かれています。

697行目で,makeClippingでoverflowを'hidden'にしてクリップされるようにし,heightを0pxにしてから,showで表示します。

699行目で,更新後のフックで,子要素のbottomプロパティの値を要素の高さと同期させて,要素の内容が吐き出されるように流れてくるように見せます。

703行目で,終了後のフックで,要素をundoClippingしてoverflowを元に戻し,undoPositionedでpositionを元に戻します。

705行目で,子要素もundoPositionedして,さらに,bottomプロパティの値を元に戻します。

Effect.SlideUp

0710:Effect.SlideUp = function(element) {
0711:  element = $(element).cleanWhitespace();
0712:  var oldInnerBottom = element.down().getStyle('bottom');
0713:  var elementDimensions = element.getDimensions();
0714:  return new Effect.Scale(element, window.opera ? 0 : 1,
0715:   Object.extend({ scaleContent: false, 
0716:    scaleX: false, 
0717:    scaleMode: 'box',
0718:    scaleFrom: 100,
0719:    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
0720:    restoreAfterFinish: true,
0721:    afterSetup: function(effect) {
0722:      effect.element.makePositioned();
0723:      effect.element.down().makePositioned();
0724:      if (window.opera) effect.element.setStyle({top: ''});
0725:      effect.element.makeClipping().show();
0726:    },  
0727:    afterUpdateInternal: function(effect) {
0728:      effect.element.down().setStyle({bottom:
0729:        (effect.dims[0] - effect.element.clientHeight) + 'px' });
0730:    },
0731:    afterFinishInternal: function(effect) {
0732:      effect.element.hide().undoClipping().undoPositioned();
0733:      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});
0734:    }
0735:   }, arguments[1] || { })
0736:  );
0737:};
0738:

710~738行目のEffect.SlideUpは,お店のシャッターを上げるようなエフェクトで,Effect.BlindUpに似ていますが,要素の内容が吸い込まれるように流れていくのが違います。BlindUpと同じくEffect.Scaleを使っていても,そのぶん処理が少し複雑です。コードはEffect.SlideDownとほとんど同じですので,解説は省略します。

717行目と719行目でscaleModeが2回登場するのはバグでしょう。

著者プロフィール

源馬照明(げんまてるあき)

名古屋大学大学院多元数理科学研究科1年。学部生のときにSchemeの素晴らしさを知ったのをきっかけに,関数型言語の世界へ。JavaScriptに,ブラウザからすぐに試せる関数型言語としての魅力と将来性を感じている。

ブログ:Gemmaの日記

コメント

コメントの記入