jquery 当鼠标靠近时排斥物体

kse8i1jr  于 2023-10-17  发布在  jQuery
关注(0)|答案(3)|浏览(104)

我在一个名为“. background”的父div中随机放置了一堆span元素。这些都是用JavaScript生成的。就像这样:

<span class="circle" style="width: 54px; height: 54px; background: #5061cf; top: 206px; left: 306px"></span>

我想让它们在鼠标靠近时移开(或排斥),但我不知道如何做到这一点!如何在jQuery中实现这一点?
我想你必须搜索附近的跨度,然后改变它们的位置,如果它们在鼠标周围的一定半径内,但我真的不知道从哪里开始。感谢您的帮助!

noj0wjuj

noj0wjuj1#

一个简单的方法是将每个跨度包裹在另一个更大的跨度中。使其在每一侧都按您希望鼠标能够接近内部跨度的最小距离增大。绑定一个函数(evade),将每个 Package 器移动到 Package 器上的mouseover。这种方法为您提供了一个方形边框,因此如果内部跨度中的图形元素不是方形的,则从鼠标到图形元素边框的距离不会是恒定的,但很容易实现。
或者,使用缓冲器进行粗略的接近测试。与其将evoke函数绑定到mouseover,不如在mousemove上绑定一个函数(beginEvade)来绑定evade。另外,将一个函数绑定到mouseout,取消绑定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());
    }
};

接下来是一个示例循环规避函数(这是最容易实现的)。大纲:
1.计算保险杠的中心(保险杠的corner加上outer dimensions除以一半)
1.计算鼠标偏移矢量(从mouse cursor到元素中心)
1.接近测试:如果距离>=最小允许距离,则提前返回。
1.计算delta:到鼠标光标的距离太小,所以我们需要从缓冲器所在位置到它应该所在位置的向量(delta)。拉长偏移向量,使其成为缓冲器中心相对于鼠标位置的最小允许距离。减去偏移向量,得到从邻近边缘到鼠标的增量,这也恰好是增量。
1.计算新位置:
1.将delta值添加到当前位置。
1.边界检查:将圆的所有边界保留在文档内。
1.移动保险杠
在代码中:

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中预览此内容

cnjp1d6j

cnjp1d6j2#

你可以用jQuery选择所有具有类circle的对象,把它放在一个变量中,然后通过循环遍历它们来检查mousemove(也可以用jQuery完成),如果一个对象在鼠标的某个半径内。

1tu0hz3e

1tu0hz3e3#

这个问题和答案是非常有用的,作为一个开始的地方,我写了一个脚本,以排斥一个图像与光标。我已经迭代了前面的答案,以支持矩形元素,而不仅仅是方形元素。我在下面包含的示例代码也使用了Web API而不是jQuery库,因此代码将在浏览器中运行而没有任何额外的依赖关系,并且在移动排斥图像时还考虑了窗口滚动。
一个JSFiddle示例是here,下面包含了相同的代码。

// Get the element that will evade the mouse
const bumper = document.getElementById("bumper")

// Pass the element to the parent function `evadeTheMouse`
evadeTheMouse(bumper)

function evadeTheMouse(element) {
  if (!element) {
    return
  }

  element.addEventListener("mousemove", (e) => {
    // Get the location of the window, element, and mouse
    const {
      scrollX,
      scrollY
    } = window
    const {
      clientX: mouseX,
      clientY: mouseY
    } = e
    const {
      top,
      left,
      right,
      bottom,
      height,
      width
    } = element.getBoundingClientRect()

    // Exit early if the mouse isn't overlapping
    const mouseIsIntersecting = mouseX >= left && mouseX <= right && mouseY >= top && mouseY <= bottom
    if (!mouseIsIntersecting) {
      return
    }

    // Create vectors for each coordinate to more easily do math operations
    const windowScrollV = new Vector(scrollX || 0, scrollY || 0)
    const mouseV = new Vector(mouseX, mouseY)
    const centerV = new Vector(left + width / 2, top + height / 2)
    // Calculate where the line from the center to mouse will extend
    // into the edge of the image
    const targetV = getIntersectingPoint({
      top,
      left,
      right,
      bottom,
      height,
      width
    }, centerV, mouseV)
    // Calculate how far the mouse is from the edge of the image
    // (where it should be)
    const deltaV = mouseV.subtract(targetV)
    // Move the corner of the image the same amount, plus add
    // the scroll location to the calculated coordinates
    const newTopLeftV = new Vector(left, top).add(deltaV).add(windowScrollV)

    if (newTopLeftV.x < 0) {
      newTopLeftV.x = 0;
    }
    if (newTopLeftV.y < 0) {
      newTopLeftV.y = 0;
    }

    // Set the new element's position
    element.style.setProperty("position", "absolute")
    element.style.setProperty("top", `${newTopLeftV.y}px`)
    element.style.setProperty("left", `${newTopLeftV.x}px`)
  })
}

/**
 * Find the point where the vector from the center of the HMTL element
 * to the mouse will intersect with the edge of the HTML element
 * @param {HTMLElement} element The HTML element
 * @param {Vector} center A vector representing the element's center coordinates
 * @param {Vector} mouse A vector representing the mouse coordinates
 * @param {boolean} [debug=false] Option to draw shapes to help with debugging
 * @returns {Vector} The vector of the intersection
 */
function getIntersectingPoint(element, center, mouse) {
  const edges = findNearestEdges(element, center, mouse)

  // Calculate the intersecting point for the two closest edges.
  // One edge will have an intersecting point on the edge of the element.
  for (let i = 0; i < edges.length; i++) {
    const edge = edges[i]
    const slope = getSlope(center, mouse)
    const {
      solveForX,
      solveForY
    } = getEquations(slope, mouse)
    if (edge.x === undefined) {
      const {
        y
      } = edge
      const x = solveForX(y)
      if (x <= element.right && x >= element.left) {
        return new Vector(x, y)
      }
    } else if (edge.y === undefined) {
      const {
        x
      } = edge
      const y = solveForY(x)
      if (y <= element.bottom && y >= element.top) {
        return new Vector(x, y)
      }
    } else {
      throw new Error("Unable to find intersecting point")
    }
  }
}

/**
 * @typedef {object} Edge
 * @property {number|undefined} x
 * @property {number|undefined} y
 */

/**
 * Find the two edges of an HTML element that are closest
 * to the mouse hovering over it
 * @param {HTMLElement} element The HTML element
 * @param {Vector} center A vector representing the element's center coordinates
 * @param {Vector} mouse A vector representing the mouse coordinates
 * @returns {Edge[]} An array of the two closest edges with either an `x` or `y` value
 */
function findNearestEdges(element, center, mouse) {
  const edges = []
  if (mouse.x <= center.x) {
    // Mouse is on the left
    edges.push({
      x: element.left,
      y: undefined
    })
  } else {
    // Mouse is on the right
    edges.push({
      x: element.right,
      y: undefined
    })
  }

  if (mouse.y <= center.y) {
    // Mouse is on the top
    edges.push({
      x: undefined,
      y: element.top
    })
  } else {
    // Mouse is on the bottom
    edges.push({
      x: undefined,
      y: element.bottom
    })
  }

  return edges
}

/**
 * Calculate the slope of the line between two vectors
 * @param {Vector} point1
 * @param {Vector} point2
 * @returns {number}
 */
function getSlope(point1, point2) {
  return (point2.y - point1.y) / (point2.x - point1.x)
}

/**
 * Returns two functions that calculate coordinates
 * on the same line, given either an `x` coordinate
 * or a `y` coordinate
 * @param {number} slope The slope of a line
 * @param {Vector} point A known point on the line
 */

function getEquations(slope, point) {
  /**
   * @param {number} x The `x` coordinate on the line
   * @returns {number} The `y` coordinate on the line
   */
  function solveForY(x) {
    return slope * x - slope * point.x + point.y
  }

  /**
   * @param {number} y The `y` coordinate on the line
   * @returns {number} The `x` coordinate on the line
   */
  function solveForX(y) {
    return (y - point.y + slope * point.x) / slope
  }

  return {
    solveForX,
    solveForY
  }
}

/**
 * A vector with (`x`, `y`) coordinates and methods for
 * basic vector math operations
 */
class Vector {
  /**
   * Create a vector
   * @param {number} x The x coordinate
   * @param {number} y The y coordinate
   */
  constructor(x, y) {
    this.x = x
    this.y = y
  }

  /**
   * @param {Vector} v2
   */
  add(v2) {
    return new Vector(this.x + v2.x, this.y + v2.y)
  }

  /**
   * @param {Vector} v2
   */
  subtract(v2) {
    return new Vector(this.x - v2.x, this.y - v2.y)
  }

  /**
   * @param {number} factor A number to multiply the vector by
   */
  multiply(factor) {
    return new Vector(this.x * factor, this.y * factor)
  }

  /**
   * @param {number} factor A number to divide the vector by
   */
  divide(factor) {
    return new Vector(this.x / factor, this.y / factor)
  }

  length() {
    return Math.sqrt(this.x * this.x + this.y * this.y)
  }

  normalize() {
    return this.divide(this.length())
  }
}
#circle {
  display: block;
  width: 35px;
  height: 75px;
  background-color: #5061cf;
}

#bumper {
  position: absolute;
  top: 75px;
  left: 200px;
  padding: 32px;
  overflow: auto;
  outline: 1px solid red;
}
<span id="bumper">
  <span id="circle" />
</span>

相关问题