Chen Yangjian's Blog

Carpe diem - Seize the day

Coda Popup Bubbles

| Comments

via

=============以下正文翻译的分割线===============

Coda 是个 Mac 上的 Web 开发套装 —— 在我认识的设计师与开发者们里头很流行。Panic (Coda 的开发团队)也以他们敏锐的设计闻名。并且,Jorge Mesa 提出了鼠标悬停在下载图片上的时候像吞云吐雾般弹出一个提示框的需求

简单说该效果只是多个特效的简单封装。不过有几个些微处需要注意。

Coda Popup Bubble

如何解决问题

为了得到该特效,我们需要做以下事情:

  1. 有考虑 JavaScript 被关闭的情况的 HTML 代码。不过分的说,用 CSS 把弹出框隐藏掉就是了。
  2. 隐藏的弹出框使用层叠样式表单保证出现的时候是已经设计好的。
  3. 用 jQuery 实现鼠标移至与鼠标移出时的弹出特效。

最值得注意的地方就是:当你的鼠标移到弹出框上的时候,该事件会触发原图片的鼠标移出事件。我会详述如何保证这个问题不会影响到弹出框的实现。

我弄了个示范视频来演示怎么搞出这个效果。下面是我是怎么以及用什么做的。

演示视频Flash 版本

QuikeTime 版本的大约 23 兆,Flash 的是流媒体。

demo / 演示

HTML 代码

为了保证重用性,我把“触发者”与“弹出框”放在了一个 div 里头。触发者即用户鼠标移至时显示弹出框的目标元素。

demo.html
1
2
3
4
5
6
<div class="bubbleInfo">
  <img class="trigger" src="http://mysite.com/path/to/image.png" />
  <div class="popup">
    <!-- your information content -->
  </div>
</div>

CSS

实际需要的 CSS 其实并不多。当然,你的 HTML 代码如何对此肯定会有影响。演示视频用的是 Coda 网站上的版本,所以需要设定的 CSS 还是相当可观的。

我推荐的标配如下:

demo.css
1
2
3
4
5
6
7
.bubbleInfo {
    position: relative;
}
.popup {
    position: absolute;
    display: none; /* keeps the popup hidden if no JS available */
}

这样,弹出框就能以绝对定位放置在触发者旁了。

jQuery

为了实现特效,我们需要为弹出框实现如下动画效果。

Mouse Over

  1. mouseover(鼠标移至)事件触发时:重置弹出框的位置(因为弹出框弹出的时候网上偏移了,所以这是必须的)
  2. 弹出框的不透明度从 0 到 1,并将它的 CSS 属性 top 减 10px(实现往上移动的效果)
  3. 如果 mouseover 时间再度触发,而弹出框正在动画中 —— 无视
  4. 如果 mouseover 时间再度触发,而弹出框已经可见 —— 无视

Mouse Out

  1. 设置个倒计时来触发弹出框的隐藏功能(这能保证用户不慎移出的时候弹出框不会立即消失)
  2. 如果用以隐藏的倒计时已经设定,则重置(如此方可保证隐藏功能只会被触发一次)
  3. 时间到时,将弹出框的不透明度从 1 降到 0 并将它的 CSS top 属性继续减 10 (往上飘走……)
  4. 标记弹出框已经隐藏

The ‘Trick’

一开始有个恼人的地方我不知道怎么解决。每次我将鼠标从触发者移到弹出框上的时候,触发者都会触发一个 mouseout(鼠标移出)事件 —— 于是弹出框隐藏了。这个臭虫真要命。

我这已经有了解决办法。不过或许有别的,而且据我所知,Coda 网站的开发者们并不是这么按我这说的来的。

当前文所述的 mouseover 触发的时候,你需要重置 mouseout倒数计时。问题解决。

Complete Source Code

以下是完整代码,包括注释。

demo.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
$(function () {
  $('.bubbleInfo').each(function () {
    // options
    var distance = 10;
    var time = 250;
    var hideDelay = 500;

    var hideDelayTimer = null;

    // tracker
    var beingShown = false;
    var shown = false;

    var trigger = $('.trigger', this);
    var popup = $('.popup', this).css('opacity', 0);

    // set the mouseover and mouseout on both element
    $([trigger.get(0), popup.get(0)]).mouseover(function () {
      // stops the hide event if we move from the trigger to the popup element
      if (hideDelayTimer) clearTimeout(hideDelayTimer);

      // don't trigger the animation again if we're being shown, or already visible
      if (beingShown || shown) {
        return;
      } else {
        beingShown = true;

        // reset position of popup box
        popup.css({
          top: -100,
          left: -33,
          display: 'block' // brings the popup back in to view
        })

        // (we're using chaining on the popup) now animate it's opacity and position
        .animate({
          top: '-=' + distance + 'px',
          opacity: 1
        }, time, 'swing', function() {
          // once the animation is complete, set the tracker variables
          beingShown = false;
          shown = true;
        });
      }
    }).mouseout(function () {
      // reset the timer if we get fired again - avoids double animations
      if (hideDelayTimer) clearTimeout(hideDelayTimer);

      // store the timer so that it can be cleared in the mouseover if required
      hideDelayTimer = setTimeout(function () {
        hideDelayTimer = null;
        popup.animate({
          top: '-=' + distance + 'px',
          opacity: 0
        }, time, 'swing', function () {
          // once the animate is complete, set the tracker variables
          shown = false;
          // hide the popup entirely after the effect (opacity alone doesn't do the job)
          popup.css('display', 'none');
        });
      }, hideDelay);
    });
  });
});

Taking it Further

This effect could be perfected by changing the initial reset (popup.css()) code to read from the trigger element and approximate it’s position. In my example, I’ve hardcoded it because I only have one on the page - but you may want to use this effect several times across your page.

Comments