指定した点が中心にくるようにスクロール

[flex, tips]

Flex のコンテナは、子コンポーネントのサイズが大きいときにスクロールバーを出すことができます。

 

Canvas とかだとデフォルトで、子が大きいとき自動でスクロールバーが出るようになっています。また、他のコンテナも、minWidth や minHeight プロパティを 0 に設定することで、必要なときに自動でスクロールバーが出るようにできます。

 

horizontalScrollPolicy や verticalScrollPolicy プロパティを ON にすれば、常にスクロールバーが出るようにもできますね。

さて、ここからが本題。

 

スクロールバーが出てる状態のときに、ある座標 (x, y) が表示領域の中心にくるようにするにはどうすればいいでしょうか?

 

そう、horizontalScrollPosition と verticalScrollPosition に適切な値を設定すればいいんですね。

 

しかし、この「適切な値」の算出方法がほんのちょっとだけやっかいですので解説します。

まず、今回の場合、(x, y) を表示領域の中心にしたいのですから、

 

horizontalScrollPosition = x - (表示領域の幅)/2;

verticalScrollPosition = y - (表示領域の高さ)/2;

 

で OK のハズ。

 

では、表示領域の幅と高さはどうやって取る?

 

width と height?

 

・・・ではダメで、これだと中心が微妙にズレてしまうことがあります。

まず、scaleX、scaleY が 1 以外に設定されててもいいように unscaledWidth、unscaledHeight を使います。

 

次に、unscaledWidth と unscaledHeight は、ボーダー幅を含んだ長さになっています。しかし scrollPosition に渡すべき値はボーダーを除いた賞味の表示領域の左上座標です。ですから、ボーダー幅を減算してやる必要があります。

 

ボーダー幅は viewMetrics から取れますね。

やれやれ、これで一安心・・・というわけにはいきません。

 

さらに、スクロールバーの幅を考慮してやる必要があります。

 

しかもこれがやっかいなことに、scrollPolicy が AUTO か ON かで挙動が違います。

 

scrollPolicy が AUTO のときは、スクロールバーは表示領域にかかってきますので、その幅を減算してやる必要があります。しかし ON のときはスクロールバーはボーダーにかかってくるため、減算の必要はありません。

以上をまとめると、以下のようなコードで座標 (x, y) が中心になるようにスクロールすることができます。

        // ボーダーのサイズを取得する
        var edge:EdgeMetrics = this.viewMetrics;
        
        var viewW:Number = this.unscaledWidth - edge.left - edge.right;
        var viewH:Number = this.unscaledHeight - edge.top - edge.bottom;
        
        // 必要であれば、スクロールバーのサイズを減算する
        if (this.verticalScrollPolicy != ScrollPolicy.ON &&
                this.verticalScrollBar != null)
        {
                viewW -= this.verticalScrollBar.width;
        }
        
        if (this.horizontalScrollPolicy != ScrollPolicy.ON &&
                this.horizontalScrollBar != null)
        {
                viewH -= this.horizontalScrollBar.height;
        }
        
        // 座標 (pointX, pointY) が中心くるように、表示領域の左上座標を算出する
        var left:Number = pointX - (viewW / 2.0);
        var top:Number = pointY - (viewH / 2.0);
        
        this.horizontalScrollPosition = left;
        this.verticalScrollPosition = top;

下の画像は、実際の画面で見たときに、それぞれのサイズ・座標情報がどういう関係になっているかを示したものです。(外側の灰色部分はコンテナのボーダーです。分かりやすいように borderThickness を 100 くらいにしています)

サイズ・座標情報の関係
サイズ・座標情報の関係

ちなみに、unscaledWidth と unscaledHeight は protected プロパティなので、クラス外部からは参照できません。クラス外から設定したいときは自分で計算しないとダメですね。