HTML5、CSS3 和震撼网络的书签工具

去年情人节,我们发布了一个书签工具,它凭借着 Harlem Shake 梗的流行而迅速走红。在它发布周年之际,我们想借此机会回顾一下这个书签工具的技术细节,将其作为一个应用 HTML5 的案例研究。事实上,我们使用的 HTML、JavaScript 和 CSS 在几年前的任何一个浏览器上都无法运行。接下来,我们将探讨我们如何利用最近的浏览器发展来撼动网络。

背景

去年,Harlem Shake 梗 几乎出现在了每一个屏幕上,和其他人一样,我们也开玩笑地想过要拍摄我们自己的办公室版本视频。在抛出了一些糟糕的视频想法后,Ishan 半开玩笑地建议做一个书签工具,让网页做出 Harlem Shake 舞蹈动作。Omar 和 Hari 立即抓住了他想法的独创性,并在一个小时内构建了一个原型,让整个办公室都哈哈大笑。在熬了一个经典的通宵后,我们于 2 月 14 日发布了它,宣布了“情人节快乐,互联网!Behold,Harlem Shake 书签工具”

很快,它就被 TechCrunch 和 HuffingtonPost 等新闻媒体报道,我们的访问量也随之飙升。同时,这个书签工具为“观看-然后-重混”循环提供了一个新的表达途径,而这种循环正是 Harlem Shake 这样病毒式传播的梗的生命线。开发人员现在可以不再制作人们跳舞的视频,而是可以用代码来重混这个共生梗。像 PivotDesk 这样的初创公司将其整合到他们的网站中,而 HSMaker 则利用该代码构建了一个“Harlem Shake 作为服务”的网站。最终,YouTube 甚至在其网站上构建了自己的版本作为彩蛋。

那么,它是如何工作的呢?

一旦你点击 Harlem Shake 书签,一段 JS 代码就会在网页上执行,就像你在地址栏中输入javascript:alert(“Hi MozHacks!”);一样。这段 JavaScript 将播放 Harlem Shake 音频,"摇动" DOM 节点(根据附加到音频的时间事件),并在之后移除所有 DOM 更改。

我们是如何将音频附加到页面上并获得合适的摇动时间的呢?

HTML5 强大的音频支持使得这个实现相当容易。只需要插入一个<audio>标签,并将 src 指向 Harlem_Shake.ogg 文件即可。一旦插入到 DOM 中,文件将开始下载,并且在缓冲足够的文件后开始播放。

HTML5 定时音频事件允许我们准确地知道播放何时开始、更新和结束。我们在音频节点上附加了一个监听器,一旦音频到达某个时间,它就会执行一些 JS 代码。第一个节点在歌曲超过 0.5 秒后开始摇动。然后,在 15.5 秒时,我们闪烁屏幕并开始摇动所有节点。在 28.5 秒时,我们放慢动画速度,并且一旦音频结束,我们就停止所有动画并清理 DOM。

audioTag.addEventListener("timeupdate", function() {
  var time = audioTag.currentTime,
      nodes = allShakeableNodes,
      len = nodes.length, i;

  // song started, start shaking first item
  if(time >= 0.5 && !harlem) {
    harlem = true;
    shakeFirst(firstNode);
  }

  // everyone else joins the party
  if(time >= 15.5 && !shake) {
    shake = true;
    stopShakeAll();
    flashScreen();
    for (i = 0; i < len; i++) {
      shakeOther(nodes[i]);
    }
  }

  // slow motion at the end
  if(audioTag.currentTime >= 28.4 && !slowmo) {
    slowmo = true;
    shakeSlowAll();
  }
}, true);

audioTag.addEventListener("ended", function() {
  stopShakeAll();
  removeAddedFiles();
}, true);

我们是如何选择要摇动的页面部分的呢?

我们编写了一些辅助函数来计算给定节点的渲染大小,确定节点是否在页面上可见,以及其大小是否在某个(相当随意)范围内。

var MIN_HEIGHT = 30; // pixels
var MIN_WIDTH = 30;
var MAX_HEIGHT = 350;
var MAX_WIDTH = 350;

function size(node) {
  return {
    height: node.offsetHeight,
    width: node.offsetWidth
  };
}
function withinBounds(node) {
  var nodeFrame = size(node);
  return (nodeFrame.height > MIN_HEIGHT &&
          nodeFrame.height < MAX_HEIGHT &&
          nodeFrame.width > MIN_WIDTH &&
          nodeFrame.width < MAX_WIDTH);
}
// only calculate the viewport height and scroll position once
var viewport = viewPortHeight();
var scrollPosition = scrollY();
function isVisible(node) {
  var y = posY(node);
  return (y >= scrollPosition && y <= (viewport + scrollPosition));
}

我们收到了很多关于书签工具如何在启动摇动时异常准确地定位页面上的 logo 和重要部分的问题。事实证明,这只是使用了非常简单的启发式方法带来的运气。所有节点都被收集(通过 document.getElementsByTagName(“*”)),我们对其循环了两次。

  1. 在第一次迭代中,一旦我们找到一个在范围内且在页面上可见的节点,我们就停止。然后,我们开始播放音频,只让这个节点摇动。由于元素按照其在 DOM 中出现的顺序(大约是页面上的顺序)进行搜索,因此 logo 被选中具有惊人的一致性。
  2. 插入音频后,我们大约有 15 秒的时间来循环遍历所有节点,以识别所有可摇动的节点。这些节点被存储在一个数组中,以便在时间到达时,我们可以对其进行摇动。
// get first shakeable node
var allNodes = document.getElementsByTagName("*"), len = allNodes.length, i, thisNode;
var firstNode = null;
for (i = 0; i < len; i++) {
  thisNode = allNodes[i];
  if (withinBounds(thisNode)) {
    if(isVisible(thisNode)) {
      firstNode = thisNode;
      break;
    }
  }
}

if (thisNode === null) {
  console.warn("Could not find a node of the right size. Please try a different page.");
  return;
}

addCSS();

playSong();

var allShakeableNodes = [];

// get all shakeable nodes
for (i = 0; i < len; i++) {
  thisNode = allNodes[i];
  if (withinBounds(thisNode)) {
    allShakeableNodes.push(thisNode);
  }
}

我们是如何使摇动动画不显得乏味的呢?

我们使用了并调整了Animate.css 的库来加快过程,它轻量且易于使用,效果很好。

首先,所有选定的节点都会获得一个基本类“harlem_shake_me”,该类定义了动画的持续时间和应用样式的方式。

.mw-harlem_shake_me {
  -webkit-animation-duration: .4s;
     -moz-animation-duration: .4s;
       -o-animation-duration: .4s;
          animation-duration: .4s;
  -webkit-animation-fill-mode: both;
     -moz-animation-fill-mode: both;
       -o-animation-fill-mode: both;
          animation-fill-mode: both;
}

第二组定义动画行为的类是随机选择的,并分配给不同的节点。

@-webkit-keyframes swing {
  20%, 40%, 60%, 80%, 100% { -webkit-transform-origin: top center; }
  20% { -webkit-transform: rotate(15deg); }
  40% { -webkit-transform: rotate(-10deg); }
  60% { -webkit-transform: rotate(5deg); }
  80% { -webkit-transform: rotate(-5deg); }
  100% { -webkit-transform: rotate(0deg); }
}

@-moz-keyframes swing {
  20% { -moz-transform: rotate(15deg); }
  40% { -moz-transform: rotate(-10deg); }
  60% { -moz-transform: rotate(5deg); }
  80% { -moz-transform: rotate(-5deg); }
  100% { -moz-transform: rotate(0deg); }
}

@-o-keyframes swing {
  20% { -o-transform: rotate(15deg); }
  40% { -o-transform: rotate(-10deg); }
  60% { -o-transform: rotate(5deg); }
  80% { -o-transform: rotate(-5deg); }
  100% { -o-transform: rotate(0deg); }
}

@keyframes swing {
  20% { transform: rotate(15deg); }
  40% { transform: rotate(-10deg); }
  60% { transform: rotate(5deg); }
  80% { transform: rotate(-5deg); }
  100% { transform: rotate(0deg); }
}

.swing, .im_drunk {
  -webkit-transform-origin: top center;
  -moz-transform-origin: top center;
  -o-transform-origin: top center;
  transform-origin: top center;
  -webkit-animation-name: swing;
  -moz-animation-name: swing;
  -o-animation-name: swing;
  animation-name: swing;
}

像拍立得照片一样摇动它

最初只是一个玩笑,却最终变成了一个小型现象。世界已经从 Harlem Shake 梗中走了出来,但这个书签工具仍然激励着开发人员用 HTML5 创造性地进行开发

如果你想查看完整的源代码或提出建议,请随时为Github 代码库做出贡献!

关于 Hari Ananth

Hari 是 Moovweb 的软件工程师,负责 Moovweb SDK 和移动应用程序。Hari 拥有加州大学伯克利分校的电子工程和计算机科学学位,并且非常高。

Hari Ananth 的更多文章…

关于 Omar Jalalzada

Omar Jalalzada 是一位总部位于旧金山的数字产品设计师。Omar 热衷于人类行为,并与品牌合作,提升其产品的人性化属性,并使其更容易被消费者所接受。

Omar Jalalzada 的更多文章…

关于 Ishan Anand

Ishan Anand 是 Moovweb 的新产品总监。从 iPhone 发布的那天起,他就一直在推出移动产品,他的作品曾在 TechCrunch、ReadWriteWeb 和 LifeHacker 上被报道。Ishan 拥有麻省理工学院的电气工程和数学双学位。

Ishan Anand 的更多文章…

关于 Robert Nyman [荣誉编辑]

Mozilla Hacks 的技术布道师和编辑。关于 HTML5、JavaScript 和开放网络发表演讲和博客文章。Robert 是 HTML5 和开放网络的坚定支持者,自 1999 年以来一直在从事 Web 前端开发工作——在瑞典和纽约市。他还在 http://robertnyman.com 上定期发布博客文章,并且热爱旅行和结识新朋友。

Robert Nyman [荣誉编辑] 的更多文章…


11 条评论

  1. Caspy7

    我显然错过了这个书签工具的第一次发布,所以我去尝试了一下,它连续两次导致 Aurora 崩溃(无法修复地冻结)——在音乐的第二段开始时。
    我在 Chrome 中尝试了一下,第一个对象晃动了,但随后标签/页面变得无响应,尽管动画还在继续。
    :-/

    2014 年 2 月 15 日 15:58

    1. Erwan d’Orgeville

      完全相同的问题。

      2014 年 2 月 16 日 14:19

    2. voracity

      导致我的主 Firefox 崩溃,然后导致我的 Nightly 崩溃。为什么在发布演示之前没有注意到这一点?

      2014 年 2 月 17 日 21:32

      1. Robert Nyman [编辑]

        我在主 Firefox 和 Google Chrome 中试过,它一直运行良好。当然,您的体验可能会有所不同,因此如果问题仍然存在,我建议您提交问题

        2014 年 2 月 18 日 02:36

  2. Joshua Smith

    我也崩溃了 :-( 我在 Intel 计算机上使用 OS X 10.9 Mavericks 测试了 FF、Chrome、Safari 和 Opera。

    2014 年 2 月 18 日 13:02

  3. Rodolphe

    这里 Chrome 和 Firefox 都冻结了(Ubuntu 12)

    2014 年 2 月 20 日 03:57

  4. Grant Kemp

    我不在乎别人怎么说。那个 Harlem Shake 书签工具——给我带来了无数的乐趣——尤其是在我警告那些不懂技术的朋友注意四处传播的 Harlem Shake 病毒时。

    经典且令人赞叹的技术。

    感谢分享它是如何实现的。接下来是 Rick Roll 版本……。

    2014 年 2 月 20 日 14:49

  5. Ishan Anand

    大家好,我是这篇文章的作者之一。如果您遇到任何问题,我感到抱歉!它在我们这里确实运行良好。正如 Robert 所提到的,请在 github 代码库中记录任何问题https://github.com/moovweb/harlem_shaker/issues,我们会查看。

    2014 年 2 月 22 日 10:34

  6. Ray

    导致我的 IE、Chrome、Firefox 和 Opera 崩溃。这实际上非常令人印象深刻。

    2014 年 3 月 3 日 17:12

  7. Alex Jones

    太棒了,我在 Chromebook 上使用它,它让 Chrome 如虎添翼。
    20% { transform: rotate(5deg); }
    40% { transform: rotate(-3deg); }
    60% { transform: rotate(2deg); }
    80% { transform: rotate(-2deg); }
    100% { transform: rotate(4deg); }。以创建轻柔的摇动效果。
    也尝试在这里实现一些应用程序
    http://www.tutorialspark.com/css3Reference/CSS3_animation_Property.php

    2014 年 3 月 4 日 13:27

  8. Arun EB

    很棒,但在百分比间隔内的关键帧动画中,如果我使用 background-image 属性,它在 Firefox 中将无法工作。

    例如:
    @-moz-keyframes rotate-banner{
    0% {
    background:url(../images/banner/HTML5_Wallpaper_1680x1050_03.jpg) 0 0;

    }
    20% {
    background:url(../images/banner/css3_03.jpg) 0 0;

    }
    40% {
    background:url(../images/banner/on-page-seo-banner_03.jpg) 0 0;

    }
    60% {
    background:url(../images/banner/responsive-banner_03.jpg) 0 0;

    }
    80% {
    background:url(../images/banner/banner-bg_03.jpg) 0 0;

    }
    100% {
    background:url(../images/banner/banner_color_03.jpg) 0 0;

    }

    }

    如果任何其他解决方案可以解决 Firefox 中的此问题,请发表您的评论,以便我们更好地使用 Firefox,使其与完全更新的 CSS3 兼容。

    2014 年 3 月 12 日 08:25

本文的评论已关闭。