簡単なアプローチは、各スパンを別のより大きなスパンでラップすることです。マウスが内側のスパンに近づくことができるようにする最小距離だけ、両側を大きくします。evade
各ラッパーをラッパーに移動する関数 ( ) をバインドしますmouseover
。このアプローチでは正方形の境界線が得られるため、内部スパンのグラフィック要素が正方形でない場合、マウスからグラフィック要素の境界線までの距離は一定ではありませんが、実装は簡単です。
または、大まかな近接テストにバンパーを使用します。回避関数を にバインドする代わりに、mousemovemouseover
にバインドする関数 ( beginEvade
) をバインドします。evade
また、mouseout
unbindsに関数をバインドしますevade
。evade
その後、より正確な近接テストを実行できます。
まず、ベクトル タイプを提供する優れたジオメトリ ライブラリを見つけます。実装がない場合の実装例を次に示します。
Math.Vector = function (x,y) {
this.x = x;
this.y = y;
}
Math.Vector.prototype = {
clone: function () {
return new Math.Vector(this.x, this.y);
},
negate: function () {
this.x = -this.x;
this.y = -this.y;
return this;
},
neg: function () {
return this.clone().negate();
},
addeq: function (v) {
this.x += v.x;
this.y += v.y;
return this;
},
subeq: function (v) {
return this.addeq(v.neg());
},
add: function (v) {
return this.clone().addeq(v);
},
sub: function (v) {
return this.clone().subeq(v);
},
multeq: function (c) {
this.x *= c;
this.y *= c;
return this;
},
diveq: function (c) {
this.x /= c;
this.y /= c;
return this;
},
mult: function (c) {
return this.clone().multeq(c);
},
div: function (c) {
return this.clone().diveq(c);
},
dot: function (v) {
return this.x * v.x + this.y * v.y;
},
length: function () {
return Math.sqrt(this.dot(this));
},
normal: function () {
return this.clone().diveq(this.length());
}
};
次に、サンプルの循環回避関数 (実装が最も簡単です)。概要:
- バンパーの中心を計算する (バンパーの角と外側の寸法を半分に割った値)
- マウス オフセット ベクトルを計算します (マウス カーソルから要素の中心まで)
- 近接テスト: 距離が最小許容距離以上の場合、早期に戻ります。
- デルタの計算: マウス カーソルまでの距離が小さすぎるため、バンパーがある場所からあるべき場所 (デルタ) までのベクトルが必要です。オフセット ベクトルを長くして、最小許容距離になるようにすると、マウスの位置に対してバンパーの中心がどこにあるべきかがわかります。そこからオフセット ベクトルを引くと、近接エッジからマウスまでのデルタが得られます。これもたまたまデルタです。
- 新しい位置を計算します:
- デルタを現在の位置に追加します。
- 境界チェック: ドキュメント内の円のすべての境界を保持します。
- バンパーをずらす
コード内:
function evade(evt) {
var $this = $(this),
corner = $this.offset(),
center = {x: corner.left + $this.outerWidth() / 2, y: corner.top + $this.outerHeight() / 2},
dist = new Math.Vector(center.x - evt.pageX, center.y - evt.pageY),
closest = $this.outerWidth() / 2;
// proximity test
if (dist.length() >= closest) {
return;
}
// calculate new position
var delta = dist.normal().multeq(closest).sub(dist),
newCorner = {left: corner.left + delta.x, top: corner.top + delta.y};
// bounds check
var padding = parseInt($this.css('padding-left'));
if (newCorner.left < -padding) {
newCorner.left = -padding;
} else if (newCorner.left + $this.outerWidth() - padding > $(document).width()) {
newCorner.left = $(document).width() - $this.outerWidth() + padding;
}
if (newCorner.top < -padding) {
newCorner.top = -padding;
} else if (newCorner.top + $this.outerHeight() - padding > $(document).height()) {
newCorner.top = $(document).height() - $this.outerHeight() + padding;
}
// move bumper
$this.offset(newCorner);
}
その後、バインド/アンバインドする関数と、evade
すべてをセットアップするための呼び出しだけが残ります。
function beginEvade() {
$(this).bind('mousemove', evade);
}
function endEvade() {
$(this).unbind('mousemove', evade);
}
$(function () {
// you can also wrap the elements when creating them.
$('.circle').wrap('<span class="bumper" />')
$('.bumper').bind('mouseover', beginEvade);
$('.bumper').bind('mouseout', endEvade);
});
これはjsFiddleでプレビューできます