一种简单的方法是将每个跨度包装在另一个更大的跨度中。通过您希望鼠标能够接近内部跨度的最小距离,使其在每一侧都变大。绑定一个函数(evade
),将每个包装器移动到mouseover
包装器上。这种方法为您提供了一个方形边框,因此如果内部跨度中的图形元素不是方形的,则从鼠标到图形元素边框的距离不会是恒定的,但很容易实现。
或者,使用保险杠进行粗略的接近测试。不是将 evade 函数绑定到,而是绑定一个在 mousemove 上mouseover
绑定的函数 ( 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中预览它