去年情人节,我们发布了一个书签工具,它凭借着 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(“*”)),我们对其循环了两次。
- 在第一次迭代中,一旦我们找到一个在范围内且在页面上可见的节点,我们就停止。然后,我们开始播放音频,只让这个节点摇动。由于元素按照其在 DOM 中出现的顺序(大约是页面上的顺序)进行搜索,因此 logo 被选中具有惊人的一致性。
- 插入音频后,我们大约有 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 创造性地进行开发。
@moovweb 今年将你的 Harlem Shake 按钮作为我们贺卡的起点。写了一篇文章https://#/IkQ3Tp9Zkg
— Jon Bishop (@JonDBishop) 2013 年 12 月 19 日
如果你想查看完整的源代码或提出建议,请随时为Github 代码库做出贡献!
关于 Hari Ananth
Hari 是 Moovweb 的软件工程师,负责 Moovweb SDK 和移动应用程序。Hari 拥有加州大学伯克利分校的电子工程和计算机科学学位,并且非常高。
关于 Omar Jalalzada
Omar Jalalzada 是一位总部位于旧金山的数字产品设计师。Omar 热衷于人类行为,并与品牌合作,提升其产品的人性化属性,并使其更容易被消费者所接受。
关于 Ishan Anand
Ishan Anand 是 Moovweb 的新产品总监。从 iPhone 发布的那天起,他就一直在推出移动产品,他的作品曾在 TechCrunch、ReadWriteWeb 和 LifeHacker 上被报道。Ishan 拥有麻省理工学院的电气工程和数学双学位。
关于 Robert Nyman [荣誉编辑]
Mozilla Hacks 的技术布道师和编辑。关于 HTML5、JavaScript 和开放网络发表演讲和博客文章。Robert 是 HTML5 和开放网络的坚定支持者,自 1999 年以来一直在从事 Web 前端开发工作——在瑞典和纽约市。他还在 http://robertnyman.com 上定期发布博客文章,并且热爱旅行和结识新朋友。
11 条评论