我一直很喜欢印第安纳琼斯电影中的旅行/飞行场景,从 YouTube 上的复制尝试数量来看,我并不孤单。由于我没有使用任何视频编辑软件,我认为使用 Web 技术和 Google 地图可以实现相同的效果,并且果然可以。
您可以下载动画演示并尝试本地运行 - 您只需要一个支持 HTML5 视频的浏览器。我知道 - 音乐与电影中的音乐不太一样,但至少这首音乐没有侵犯版权,它来自我的内心(在 Mozilla 办公室的会议室里待了 5 分钟)。
那么这是如何完成的,需要解决什么问题呢?以下是方法和问题。
步骤 1:查找电影并将其转换为正确的格式
这很简单。 Archive.org 拥有许多可供您使用的很棒的公共领域电影,它们已经是 HTML5 视频元素所需的格式。在这种情况下,我拍摄了查尔斯·林德伯格起飞的简短电影,他于 1927 年从纽约起飞前往巴黎进行破纪录的飞行。
步骤 2:显示视频
使用视频非常简单
MP4 格式将被基于 Webkit 的浏览器使用,而 Ogg 版本将被 Firefox 和其他浏览器使用。由于我们要控制视频,因此我们在 video
元素上省略了 controls
属性 - 而是使用 JavaScript 创建一个按钮来播放视频。
window.addEventListener('load',
function() {
var stage = document.getElementById('stage');
var v = document.getElementsByTagName('video')[0];
but = document.createElement('button');
but.innerHTML = 'Click to see Lindbergh's flight';
stage.appendChild(but);
but.addEventListener('click',function(e) {
v.play();
e.preventDefault();
},false);
},
false);
由于视频是标记,我们可以对其进行任何处理 - 开放技术的强大功能。例如,正如我们在这里所做的那样,我们可以在 CSS 中设置其不透明度,并将其放在地图的顶部。
步骤 3:创建地图路径动画
说到这里,让我们开始进行移动路径。Google Earth 有一个 API 可以做到这一点,但它需要一个特殊的插件。Google 地图允许您在地图上绘制路径(实际上是 SVG,另一种开放标准)。将其放入递归函数中,您会获得所需的效果
本质上,我所做的是获取起点和终点的纬度和经度,并根据动画的持续时间计算出这两点之间的尽可能多的点。我将这些点存储在一个名为 pos
的数组中,然后从起点绘制一条到当前点的路径,并在每次迭代时将地图中心移动到该点。
spirit.draw = function(){
var path = new google.maps.Polyline({
path: [startpos,pos[now]],
strokeColor: "#c00",
strokeOpacity: .7,
strokeWeight: 10
});
path.setMap(map);
map.panTo(pos[now])
now = now + 1;
if(now < animationend-1){
setTimeout(spirit.draw,200);
}
}
查看地图示例的高度注释的源代码以获取详细信息。现在,我们可以使用这种动画并播放视频,但问题是它们可能会不同步。当电影停顿(就像在这种酒店无线连接上经常发生的那样)时,我们不希望动画继续移动,对吧?
步骤 4:同步视频和地图移动
我们必须将自己限制在一个真实的来源,而不是拥有两个计时信息来源。这是当前正在播放的电影的时间戳。
顺便说一句 - 您可能已经注意到我将地图代码包装在一个 tilesloaded
事件处理程序中。这是保持事物同步的另一种保障措施。我发现,在连接速度慢的情况下,瓦片加载可能会极大地延迟整个界面(由于所有子域查找),因此我让整个界面依赖于地图的加载,并且只有在瓦片加载完成后才继续。由于 tilesloaded
事件也会在地图平移时触发,因此我们需要使用一个布尔值来阻止它多次启动效果。
google.maps.event.addListener(map,'tilesloaded',function(){
if(played === false){
// [...other code...]
played = true;
}
});
您可以使用 video.currentTime
读取视频的当前时间戳,并且当电影播放时,它会不断触发一个名为 timeupdate
的事件。由于该事件触发了很多次,因此我们需要对其进行节流。这里的诀窍是只取完整的秒数,并在达到新秒数时增加计数器。您可以在视频同步演示中看到时间戳和秒间隔触发。
var now = 0;
v.addEventListener('timeupdate',function(o){
log.innerHTML = v.currentTime; /* logging the real timestamp */
var full = parseInt(v.currentTime);
if(full >= now) {
seqlog.innerHTML = now; /* logging the seconds firing */
now = now + 1;
}
},false);
这样,电影可以在中间滞后,而序列仍然保持同步。查看Github 上此演示的源代码。
将它们全部组合在一起
就这样 - 我所要做的就是在某个时间戳设置电影的不透明度,在另一个时间戳开始声音,并在另一个时间戳显示和隐藏版权声明。由于我们依赖于时间戳来实现其他效果,因此我们需要一个布尔开关来避免重复触发。
v.addEventListener('timeupdate',function(o){
full = parseInt(v.currentTime);
if(full === now-1){
mapelm.style.opacity = .8;
v.style.opacity = .4;
}
if(full === animationstart+1 && audioplay === false){
a.play();
audioplay = true;
}
if(full === animationstart+2 && hidden === true){
drmbedamned.style.display = 'block';
hidden = false;
}
if(full === animationstart+3 && hidden === false){
drmbedamned.style.display = 'none';
hidden = true;
}
if(full >= now) {
path = new google.maps.Polyline({
path: [startpos,pos[full]],
strokeColor: "#c00",
strokeOpacity: .7,
strokeWeight: 10
});
path.setMap(map);
map.panTo(pos[full])
now = now + 1;
}
},false);
我们还需要订阅的另一个事件是电影结束,这样我们就可以停止音乐并开始滚动字幕。
v.addEventListener('ended',function(o){
a.pause();
spirit.credslist.parentNode.style.display = 'block';
spirit.creds();
},false)
由于主题对于整个动画来说太短,因此我们需要循环播放它。这可以通过测试 ended
事件并将时间回滚到 0 来完成。
a.addEventListener('ended', function(o) {
a.currentTime = 0;
},false);
摘要
就是这样 - 使用开放服务和开放技术创建印第安纳琼斯风格的地图。对于受版权保护的音频(使用免费的Audacity 声音编辑器录制、编辑和转换)的解决方法,以及使用Google 的 Web 字体作为图形。
现在您可以使用它并对其进行更改以获得更令人惊叹的效果。
- 用 Openstreetmap 替换 Google 地图,以避免超出限制。
- 在从纽约到巴黎的路径上添加轻微的曲线,使其更准确(但同样,时间也不准确 - 查尔斯花费的时间更长)。
- 使用静态地图并使用 Canvas 绘制路径,以加快和平滑动画。
为什么不尝试一下 - 它是免费的,并且玩起来很有趣。
关于 Chris Heilmann
HTML5 和开放 Web 的布道者。让我们解决这个问题!
9 条评论