- 2008年10月15日 23:41
- しらべる
FlexやAS3などで、DisplayObjectなどのコンポーネントをマウスで移動,リサイズするときに隣のコンポーネントに吸着(スナップ)したい場合があります。
とりあえず思いついた方法をメモ。
簡単な仕様
- 各コンポーネントのエッジの間隔が一定の距離以上はなれていると吸着しない
- 各コンポーネントのエッジの間隔が一定の距離以下になったら吸着
移動またはリサイズ中のコンポーネントをsrc
吸着対象のコンポーネントをtarget
吸着する一定の距離をrange
重なっている矩形領域をintersection
とすると、y軸方向に考えると以下の4通りの場合があります
重なっていない状態で、吸着しない場合
![]()
srcとtargetがrange*2 以上はなれている
↓
srcの境界線をrangeだけ拡張した矩形がtargetと重ならない
重なっていない状態で、吸着する場合
![]()
srcとtargetがrange*2以内にある
↓
srcの境界線をrangeだけ拡張した矩形がtargetと重なる
srcの境界線をrangeだけ縮小した矩形がtargetと重ならない
重なっている状態で、吸着する場合
![]()
srcとtargetがrange*2以内にある
↓
srcの境界線をrangeだけ拡張した矩形がtargetと重なる
srcの境界線をrangeだけ縮小した矩形がtargetと重ならない
重なっている状態で、吸着しない場合
![]()
srcとtargetがrange*2以内にある
↓
srcの境界線をrangeだけ拡張した矩形がtargetと重なる
srcの境界線をrangeだけ縮小した矩形がtargetと重なる
この前提を元にすれば、わりとすっきりとした実装ができるのではないでしょうか。
実装のサンプルを全て載せると量が多いので、リサイズ時のY軸方向の処理に限定した場合の、吸着補正の矩形領域の取得メソッドをサンプルとして載せておきます。
/**
* 矩形領域のサイズを拡張/縮小するメソッド
* @param src リサイズ中のコンポーネント
* @param range 拡張/縮小する距離
*/
private function getFixedRectangle(src:DisplayObject, range:Number):Rectangle{
var pt:Point = src.parent.localToGlobal(new Point(src.x, src.y));
return new Rectangle(pt.x - range,
pt.y - range,
src.width + range * 2,
src.height + range * 2);
}
/**
* リサイズ時に吸着補正した矩形領域を返すメソッド
* @param src リサイズ中のコンポーネント
* @param target スナップ対象のコンポーネント
*/
private function getSnapedRectangle(src:DisplayObject, target:DisplayObject):Rectangle{
var srcRect:Rectangle = getFixedRectangle(src, 0);
var targetRect:Rectangle = getFixedRectangle(target, 0);
var targetShrinkRect:Rectangle = getFixedRectangle(target, -RANGE);
var targetExpandRect:Rectangle = getFixedRectangle(target, RANGE);
var intersectionRect:Rectangle = srcRect.intersection(targetRect);
var rsltRect:Rectangle = srcRect;
/**
* srcの境界線をrangeだけ拡張した矩形がtargetと重なり、
* srcの境界線をrangeだけ縮小した矩形がtargetと重ならない
*/
if(srcRect.intersects(targetExpandRect) &&
!srcRect.intersects(targetShrinkRect)){
if(intersection.height < RANGE * 2){
/**
* スナップ領域にあるのがエッジの下端だった場合
* → サイズだけ変更
*/
if(intersection.y < targetRect.y){
rsltRect.height = targetRect.y - srcRect.y;
}
/**
* スナップ領域にあるのがエッジの上端だった場合
* → サイズと位置を変更
*/
if(intersection.y == srcRect.y){
rsltRect.y = targetRect.y + targetRect.height;
rsltRect.height = (srcRect.y + srcRect.height) -
(targetRect.y + targetRect.height);
}
}
}
return rsltRect;
}
Rectangle::intersection や Rectangle::intersects を使用するところがポイントです。
思いついたまま実装したのですが、もっと簡単な方法があったりして。。。
