27

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

現在、サーバーからの時刻に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秒程度以内になりますが、もっとうまくやる必要があります。スライドショーが自動進行している場合、違いは非常に顕著です。

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

これが私が何とかまとめたものです

何か案は?

4

5 に答える 5

25

リクエストを送信してからレスポンスを返すまでの経過時間を測定します。次に、その値を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);
}

興味深いことに、John Resigは、Javascriptのタイミングの精度に関する優れた記事を掲載しています。
この場合、時間のずれが1秒以内であるだけなので、問題は発生しません。15ミリ秒の差はあまり効果がないはずです。

于 2012-05-14T15:17:08.890 に答える
21

別のアプローチはどうですか:誰が時間を気にしますか?(システムクロックをJavaScriptと確実に同期させることはできません。)

代わりに、socket.ioを備えたノードサーバーを使用して、クライアントがスライドショーを進めるときに同期します。クライアントがいつ進むかを決定する代わりに、サーバーはクライアントに指示します。

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

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);
}

views / 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、魔法を見てください。

于 2012-05-19T04:16:57.790 に答える
11

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

コードはServerDateと呼ばれ、無料でダウンロードできます。これがREADMEの一部です。私の例では108ミリ秒の精度を達成したことに注意してください。

関数またはそのインスタンスの1つをServerDate使用するのと同じように使用できます。例:Date

> 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
于 2012-08-14T15:57:29.073 に答える
0

サーバーと実際に同期することはできません。サーバーリクエストに必要な時間を測定することは(MikeWyattが提案したように)、レイテンシーの良い指標ではありません。

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

複数のデバイス間でアプリを同期するには、サーバーはどのアクションをいつ実行するかをデバイスに送信する必要があります。「いつ」は「私の応答を受け取ったらすぐに」ではなく、正確なタイムスタンプである必要があります。デバイスのシステムクロックが正確で同期されている限り(通常は同期されています)、アプリはメソッドを同期して実行します。これは、いつ何が起こるか(または少なくとも:そのとき何が起こったはずか)を認識しており、何を補間できるかを知っているためです。 「今」起こる)。

于 2012-05-14T23:14:49.710 に答える
0

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

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

サーバーでは、スライドを変更するときまで、すべての回答を保留する必要があります。(クライアントでそれぞれ同じ時間、より進んで後で遅延する可能性がありますが、それはおそらく必要ではありません)。何が利用できるかわからないため、ここではそのサンプルコードを示すことはできません。

つまり、サーバーが指揮者を演じ、すべてのクライアントが指揮者を聴いているオーケストラを効果的に作成していることになります。

次に、タイミングは、(ほぼ)同時に要求に応答するサーバーの機能とネットワーク遅延によって決定されます。
通常、クライアントはネットワークの同じ部分にある必要があるため、遅延は非常に似ている可能性があります。絶対値はここでは問題にならず、変動のみが問題になります。

また、追加のトリックが役立つ場合もあります。ハードな交換でスライドを変更するのではなく、ブレンドします。これにより、変更がぼやけて、常に発生するわずかなタイミングの違いを目で捉えることができなくなります。

(サーバーにコンダクターを再生させることができない場合は、MikeWyattによるソリューションを使用する必要があります。ネットワークの設定に応じて、おそらくいくつかのリクエストと結果の平均化が必要です。LANでは、1つのリクエストで十分な場合があります。フルインターネット上では、平均化を少し超えても問題はありません...)

于 2012-05-19T12:20:38.770 に答える