2012-05-14 15 views
22

素晴らしいreveal.jsライブラリを使用してHTMLスライドショーを作成しています。私の唯一の問題は、複数のデバイス間で同期する必要があることです。複数のデバイス間でJS時間を同期

現在のところ私はサーバーから時刻にAJAXリクエストを行い、ページの内部時計を保持しています。

function syncTime() { 
    // Set up our time object, synced by the HTTP DATE header 
    // Fetch the page over JS to get just the headers 
    console.log("syncing time") 
    var r = new XMLHttpRequest(); 
    r.open('HEAD', document.location, false); 
    r.send(null); 
    var timestring = r.getResponseHeader("DATE"); 

    systemtime = new Date(timestring); // Set the time to the date sent from the server 
} 

これは1秒程度の精度で私を得ていますが、私はより良くする必要があります。違いは、スライドショーが自動的に進んでいるときにはっきりと分かります。

コードはすべて同じプラットフォーム上で実行されるため、ブラウザ間の互換性は心配しません。

Here's what I've managed to put together

任意のアイデア?

答えて

18

どのように異なるアプローチ:時間を気にするのでしょうか?

代わりにsocket.ioNodeサーバーを使用して、クライアントがスライドショーを進めるときに同期させます。クライアントがいつ進めるかを決めるのではなく、サーバーはそれらを指示します。

このアプローチには、スライドショーを実行中に手作業で作成するという追加の特典が付いています。次の例では、次のボタンを追加しました。これにより、接続されているすべてのクライアントがすぐに次のスライドに進むことができます。

app。JS

var express = require('express') 
    , app = express.createServer() 
    , io = require('socket.io').listen(app) 
    , doT = require('dot') 
    , slide = 0 
    , slides = [ 
     'http://placekitten.com/700/400?image=13', 
     'http://placekitten.com/700/400?image=14', 
     'http://placekitten.com/700/400?image=15', 
     'http://placekitten.com/700/400?image=16', 
     'http://placekitten.com/700/400?image=1', 
     'http://placekitten.com/700/400?image=2', 
     'http://placekitten.com/700/400?image=3', 
     'http://placekitten.com/700/400?image=4', 
     'http://placekitten.com/700/400?image=5', 
     'http://placekitten.com/700/400?image=6', 
     'http://placekitten.com/700/400?image=7', 
     'http://placekitten.com/700/400?image=8', 
     'http://placekitten.com/700/400?image=9', 
     'http://placekitten.com/700/400?image=10', 
     'http://placekitten.com/700/400?image=11', 
     'http://placekitten.com/700/400?image=12', 
    ]; 

app.listen(70); // listen on port 70 

app.register('.html', doT); // use doT to render templates 
app.set('view options', {layout:false}); // keep it simple 
doT.templateSettings.strip=false; // don't strip line endings from template file 

app.get('/', function(req, res) { 
    res.render('index.html', { slide: slide, slides: slides }); 
}); 

app.post('/next', function(req, res) { 
    next(); 
    res.send(204); // No Content 
}); 

setInterval(next, 4000); // advance slides every 4 seconds 

function next() { 
    if (++slide >= slides.length) slide = 0; 
    io.sockets.emit('slide', slide); 
} 

ビュー/ index.htmlには

このファイルはdoTテンプレートとして処理されます。

<!DOCTYPE html> 
<html> 
<head> 
<title>Synchronized Slideshow</title> 
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script> 
<script src="/socket.io/socket.io.js"></script> 
<script> 
var curslide = {{=it.slide}}; // the slide the server is currently on. 

$(function() { 
    $('#slide' + curslide).css('left',0); 

    $('#next').click(function() { 
     $.post('/next'); 
    }); 
}); 

var socket = io.connect('http://localhost:70'); 
socket.on('slide', function(slide) { 
    $('#slide' + curslide).animate({left:-700}, 400); 
    $('#slide' + slide).css('left',700).show().animate({left:0}, 400); 
    curslide = slide; 
}); 
</script> 
<style> 
#slideshow, .slide { width:700px; height:400px; overflow:hidden; position:relative; } 
.slide { position:absolute; top:0px; left:700px; } 
</style> 
</head> 
<body> 
    <div id="slideshow"> 
     {{~it.slides :url:i}} 
      <div id="slide{{=i}}" class="slide"><img src="{{=url}}"></div> 
     {{~}} 
    </div> 
    <button id="next">Next &gt;</button> 
</body> 
</html> 

コピーしたフォルダにこれらの2つのファイルは、その後、その後、魔法を参照してください、

$ npm install express socket.io dot 
$ node app.js 

を実行し、いくつかの異なるウィンドウでhttp://localhost:70に移動します。

+0

この回答を愛してください!私の質問を無視して、同じ目的に到達する。私はこのアプローチを取って、私がそれから得ることができるものを見るつもりです! –

+0

デモリンクのための名誉! –

+0

最新版のExpressでこれを実行すると、いくつかのエラーが発生しました。バージョン2.5.10に固定して再インストールしたときにのみ動作します。また、 'sudo node app.js'を実行しなければならなかった – skube

21

要求を送信してから応答を返すまでの経過時間を測定します。その値を2で割ります。これは、一方的な待ち時間の大まかな価値をもたらします。これをサーバーからの時間値に追加すると、真のサーバー時間に近くなります。このような

何か作業をする必要があります:

function syncTime() { 
    // Set up our time object, synced by the HTTP DATE header 
    // Fetch the page over JS to get just the headers 
    console.log("syncing time") 
    var r = new XMLHttpRequest(); 
    var start = (new Date).getTime(); 

    r.open('HEAD', document.location, false); 
    r.onreadystatechange = function() 
    { 
     if (r.readyState != 4) 
     { 
      return; 
     } 
     var latency = (new Date).getTime() - start; 
     var timestring = r.getResponseHeader("DATE"); 

     // Set the time to the **slightly old** date sent from the 
     // server, then adjust it to a good estimate of what the 
     // server time is **right now**. 
     systemtime = new Date(timestring); 
     systemtime.setMilliseconds(systemtime.getMilliseconds() + (latency/2)) 
    }; 
    r.send(null); 
} 

興味深い脇:ジョンResig氏はJavascriptタイミングの精度にgood articleを持っています。
このような場合には問題が発生するはずはありません。時間が約1秒遅れることだけが心配であるからです。 15ミリ秒の違いはあまり効果がありません。

+0

驚くべき答え!これは最高の解決策だと確信していますが、私は他のソリューションを引き付けることができるかどうかを知ろうとしています。 –

+4

これは私がそれをやる方法です。唯一の違いは、同期呼び出しを数回(つまり10回)実行してから、最小の待ち時間で呼び出しからシステム時間を使用することです。実際、クライアントからサーバーへの時間とサーバーからクライアントへの時間との間の分布が厳密に1:1ではないことから、レイテンシが高いほど影響が大きくなります。 –

+0

応答時間を2で割るよりも良い選択肢はありますか?いくつかの私のテストでは、サーバはしばらくブロックしていましたが、すぐに復帰しました。だから、旅行時間は200msだったかもしれませんが、返される時間はわずか20msです。それは理にかなっていますか? – jocull

0

実際にサーバーと同期することはできません。あなたのサーバ要求が(MikeWyattが示唆したように)必要とする時間を測定することは、待ち時間の良い指標ではありません。

リクエストに応答するのは自分のサーバーだけです。したがって、その情報を返信する必要があります。 Date.now() - new Date(timestringOfServerResponse)を使用すると、レイテンシを正確に測定できます。しかし、私はあなたがなぜその価値を必要とするのか分かりません。

複数のデバイス間でアプリを同期させるには、サーバーはいつ実行するかを送信する必要があります。 「いつ」は「あなたの返事を受け取ったらすぐに」ではなく、正確なタイムスタンプです。あなたのデバイスのシステムクロックが正確で同期されている限り(通常はそうです)、アプリは同期の方法を同期して実行します。なぜなら、いつ何が起こるかを知っているからです(少なくとも何が起こったのだろうか? "今"起こる)。

+0

これはまさにこれがどのように機能しているかです。ページは、デバイス間で同期されるようにアクションを実行する前に、正しい時刻まで待機します。問題は、時間サーバーと時間を同期させるように設定された2つのiPadsが隣にあり、1秒離れているという事実にあります。 –

+0

あなたは "カスタムシステム時間"を同期するために、JavaScriptのタイムサーバーを実装したいですか? – Bergi

+2

@Bergi、あなたは間違ってクライアントの時計がサーバーの時計と一致すると仮定しています。おそらく数秒または数分で異なることが実際に保証されています。待ち時間の調整は完璧ではありませんが、かなり近づくでしょう。最悪の場合、旅行のうちの1つが瞬間的であった場合、あなたは完全なラウンドトリップ時間だけ休みます。 – MikeWyatt

0

ここで私のリアルタイムWebアプリケーションにはCOMETパターンを広く使用しています。

これを使用するには、クライアントがサーバーへのAJAX要求を開き、応答を待つ必要があります。それが来るとすぐに、クライアントはスライドを変更する必要があります。

サーバー上では、スライドを変更するまですべての回答を保持する必要があります。 (あなたはもっと先進的で、後でクライアントで同じ時間に遅れるかもしれませんが、それは必要ではないでしょう)。私はあなたに何が入手可能かわからないので、ここではサンプルコードをここに表示することはできません。

したがって、サーバーが指揮者を演奏し、すべてのクライアントが彼を聞いているオーケストラを効果的に作成しています。

タイミングは、サーバーが(ほぼ)同じ時刻にネットワーク遅延を加えた要求に応答する能力によって決定されます。
通常、クライアントはネットワークの同じ部分に存在する必要があります。したがって、待ち時間は非常に似ています。絶対値はここでは犠牲にならず、ばらつきのみです。

また、追加のトリックが役立つかもしれません。スライドを硬質の交換用に交換したり、混ぜたりしないでください。これは、あなたがいつも持っている小さなタイミングの違いを目で捕らえることができないように、変更をぼかすでしょう。

(サーバーを演奏させることができない場合は、MikeWyattのソリューションを使用する必要があります。ネットワークの設定によっては、おそらくいくつかの要求と平均化が必要です。十分に、完全なインターネット上で少し平均化することは傷つけません...)

10

私はあなたの質問に満足のいく答えを見つけられてうれしいです。私はブラウザとサーバーの時計を同期させる同様の必要性があり、あなたのように1秒以上の精度で達成することを決めました。私はこれを行うためのコードを書いており、誰かが解決策を必要とする場合に備えて、この回答を投稿しています。

コードはServerDateと呼ばれ、無料でダウンロードできます。 READMEの一部です。

を使用すると、例えば、Date機能またはその インスタンスのいずれかを使用するのと同じようにはServerDateを使用することができます。私は私の例では108ミリ秒の精度を達成していることに注意してください:

> ServerDate() 
"Mon Aug 13 2012 20:26:34 GMT-0300 (ART)" 

> ServerDate.now() 
1344900478753 

> ServerDate.getMilliseconds() 
22 

(ミリ秒単位) サーバのクロックのServerDateの推定の精度を取得するための新しい方法もあります:

> ServerDate.toLocaleString() + " ± " + ServerDate.getPrecision() + " ms" 
"Tue Aug 14 01:01:49 2012 ± 108 ms" 

あなたは、サーバーの時計とブラウザ間の違いを見ることができますクロック(ミリ秒):

> ServerDate - new Date() 
39 
関連する問題