Ein einfacher Ansatz wäre, jeden Bereich in einen anderen, größeren Bereich einzuschließen. Vergrößern Sie ihn auf jeder Seite um den Mindestabstand, den die Maus zu den inneren Bereichen haben soll. Binden Sie eine Funktion ( evade
), die jeden Wrapper nach mouseover
auf den Verpackungen. Bei diesem Ansatz erhalten Sie einen quadratischen Rand. Wenn die grafischen Elemente in den inneren Bereichen nicht quadratisch sind, ist der Abstand zwischen der Maus und dem Rand des grafischen Elements nicht konstant, aber er ist leicht zu implementieren.
Alternativ können Sie den Stoßfänger für einen groben Näherungstest verwenden. Anstatt die Ausweichfunktion mit mouseover
binden Sie eine Funktion ( beginEvade
), die bindet evade
bei Mausbewegungen. Binden Sie außerdem eine Funktion an mouseout
der die Bindung löst evade
. Ihr evade
kann dann einen genaueren Näherungstest durchführen.
Suchen Sie zunächst eine gute Geometriebibliothek, die einen Vektortyp bereitstellt. In Ermangelung einer solchen, hier ist ein Beispiel für die Implementierung:
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());
}
};
Als Nächstes ein Beispiel für eine Zirkularausweichfunktion (die am einfachsten zu implementieren ist). Umriss:
- die Mitte des Stoßfängers zu berechnen (der Stoßfänger Ecke sowie die Außenabmessungen in zwei Hälften geteilt)
- den Maus-Offset-Vektor berechnen (aus dem Mauszeiger zum Mittelpunkt des Elements)
- Näherungstest: Wenn der Abstand >= der zulässigen Mindestdistanz ist, dann frühzeitige Rückkehr.
- Delta berechnen: Der Abstand zum Mauszeiger ist zu klein, also brauchen wir den Vektor von der Stelle, an der sich der Bumper befindet, zu der Stelle, an der er sich befinden sollte (das Delta). Verlängert man den Offset-Vektor so, dass er den minimal zulässigen Abstand hat, erhält man die Position, an der sich der Mittelpunkt des Bumpers befinden sollte, relativ zur Mausposition. Subtrahiert man den Offset-Vektor davon, erhält man das Delta von der Kante in der Nähe der Maus, das auch das Delta ist.
- neue Position berechnen:
- addiert das Delta zur aktuellen Position.
- Begrenzungsprüfung: Alle Ränder des Kreises bleiben innerhalb des Dokuments.
- den Stoßfänger verschieben
Im Code:
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);
}
Danach bleiben nur noch Funktionen zum Binden/Entbinden evade
und die Anrufe, um alles einzurichten.
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);
});
Eine Vorschau finden Sie unter jsFiddle