碰撞检测

Avatar of Chris Coyier
Chris Coyier

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 200 美元的免费积分!

几年前发布了关于 jQuery UI 的 position 功能的文章,但我最近才想到该功能的碰撞检测部分有多么有用。 简而言之:您可以将元素放置在您想要的位置,但如果它计算出您放置的位置会超出屏幕或以其他方式隐藏,它将调整定位以修复它。

这是一个来自 Disqus 的示例,其中此用户弹出窗口的默认设置是向上打开,但如果它会被屏幕顶部隐藏,则会改为向下打开。

我不知道他们是否使用了 jQuery UI(可能没有)。

我最近在 CodePen 上做了同样的事情,以确保设置下拉区域不会在页面底部边缘下方打开,而是向上打开。

我们正在使用 jQuery UI,因为我们无论如何都在使用它来执行其他一些操作(拖动、调整大小,将来可能会重新排序)。

无论何时页面上的元素以方向性打开,并且您不能 100% 确定该方向不会被隐藏,碰撞检测都是一个好主意。

工具提示是一个经典的例子。 您可能设计了一个工具提示,使其在链接的上方和右侧打开。

$("a[data-tooltip]")
  .on("mouseenter", function() {
     var whichTip = $(this).data("tooltip");
     $(".tooltip[data-tooltip=" + whichTip + "]")
       .position({
         my: "left bottom",
         at: "left top",
         of: $(this)
       })
       .show();
  });

(附带说明,如果我们能够以某种方式使用 HTML 在其中创建工具提示而无需 JS,那不是很好吗?)

但是,如果该链接靠近页面顶部,它很容易在边缘上方打开,并且非常无用。

此外,根据其设计方式,它也可能在边缘附近存在危险。

jQuery UI 实际上默认情况下会启用碰撞检测,并且它会“翻转”,这意味着

将元素翻转到目标的另一侧,然后再次运行碰撞检测以查看它是否适合。 将使用允许更多元素可见的一侧。

如果您想要关闭它,则必须明确关闭它。 碰撞还有其他几个值,包括 fit(它会轻微调整位置,直到元素适合页面/区域),或 fitflip(它会先翻转,然后根据需要进行调整)。

使用 jQuery UI position,它将如下所示。

$("a[data-tooltip]")
  .on("mouseenter", function() {
     var whichTip = $(this).data("tooltip");
     $(".tooltip[data-tooltip=" + whichTip + "]")
       .position({
         my: "left bottom",
         at: "left top",
         of: $(this),
         collision: "flipfit"
       })
       .show();
  });

现在,如果工具提示将打开并被顶部或边缘隐藏,它将翻转或移动以适应。

它是如何工作的? 数学!

我相信您可以想象它是如何工作的。 JavaScript 知道所有元素以及视口本身的尺寸和位置。 因此,它可以进行一些检查,例如:如果一个特定尺寸的元素位于这个位置,它是否适合这个区域?

如果您不想使用 jQuery UI,您当然可以自己编写。 如果您这样做,我可能建议的一件事是:在元素翻转或调整大小后向其添加类名。 jQuery UI 目前缺少这一点,您可能需要这些类名来影响工具提示中的其他元素。 例如,确保指针箭头指向正确的位置。

简单演示

碰撞检测针对的容器不必是视口,它可以是指定的容器。 这里就是这种情况,仅用于演示。

查看 Chris Coyier 在 CodePen 上的笔 碰撞检测@chriscoyier)。

以及隐藏工具提示的 JS。

$("a[data-tooltip]")
  .on("mouseenter", function() {
     var whichTip = $(this).data("tooltip");
     $(".tooltip[data-tooltip=" + whichTip + "]")
       .position({
         my: "left bottom",
         at: "left top",
         of: $(this),
         collision: "flip",
         within: ".page-wrap"
       })
       .show();
  })
  .on("mouseleave", function() {
    var whichTip = $(this).data("tooltip");
    $(".tooltip[data-tooltip=" + whichTip + "]")
      .stop()
      .fadeOut(function() {
        $(this).removeAttr("style");
      });
  });