2012-05-14 32 views
22

Tôi đang sử dụng thư viện reveal.js tuyệt vời để tạo trình chiếu HTML. Vấn đề duy nhất của tôi là tôi cần nó để đồng bộ hóa trên nhiều thiết bị.Đồng bộ thời gian JS giữa nhiều thiết bị

Hiện tại tôi đang thực hiện yêu cầu AJAX đến thời gian từ máy chủ và giữ đồng hồ bên trong cho trang.

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 
} 

Trong khi điều này giúp tôi trong vòng 1 giây hoặc lâu hơn, tôi cần làm tốt hơn. Sự khác biệt thực sự đáng chú ý khi trình chiếu tự động tiến lên.

Mã sẽ chạy trên cùng một nền tảng, vì vậy khả năng tương thích của trình duyệt chéo không có gì phải lo lắng.

Here's what I've managed to put together

Bất kỳ ý tưởng nào?

Trả lời

18

Cách tiếp cận khác nhau: ai quan tâm đến thời gian? (Bạn sẽ không đồng bộ một cách đáng tin cậy đồng hồ hệ thống với JavaScript.)

Thay vào đó, hãy sử dụng máy chủ Node với socket.io để đồng bộ hóa khi khách hàng của bạn trước trình chiếu. Thay vì các khách hàng quyết định khi nào để tiến lên, máy chủ sẽ nói với họ.

Cách tiếp cận này đi kèm với phần thưởng tăng thêm khi có thể tự chỉnh sửa bằng trình chiếu trong khi trình chiếu đang chạy. Trong ví dụ sau, tôi đã thêm một nút Tiếp theo khiến tất cả khách hàng được kết nối phải chuyển sang trang trình bày tiếp theo ngay lập tức.

ứng dụng.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

Tập tin này được xử lý như một mẫu 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> 

Sao chép hai tập tin này vào một thư mục, sau đó chạy

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

và điều hướng đến http://localhost:70 trong các cửa sổ khác nhau, sau đó xem sự kỳ diệu.

+0

Yêu câu trả lời này! Bỏ qua câu hỏi của tôi để đạt được mục tiêu tương tự. Tôi sẽ thực hiện cách tiếp cận này và xem những gì tôi có thể thoát ra khỏi nó! –

+0

Và kudo cho liên kết demo! –

+0

Tôi gặp phải một số lỗi khi chạy phiên bản Express mới nhất. Chỉ khi tôi ghim nó vào phiên bản 2.5.10 và cài đặt lại thì nó có hoạt động không. Ngoài ra, tôi đã phải chạy 'sudo node app.js' – skube

21

Đo thời gian trôi qua giữa việc gửi yêu cầu và nhận lại phản hồi. Sau đó, chia giá trị đó cho 2. Điều đó mang đến cho bạn giá trị thô về độ trễ một chiều. Nếu bạn thêm giá trị đó vào giá trị thời gian từ máy chủ, bạn sẽ gần với thời gian máy chủ thực.

Something như thế này nên làm việc:

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

Thú vị dành: John Resig có good article về tính chính xác của thời gian Javascript.
Nó không nên gây ra một vấn đề trong trường hợp này, vì bạn chỉ quan tâm đến thời gian của bạn bị tắt bởi ~ 1 giây. Một sự khác biệt 15 ms không nên có nhiều hiệu ứng.

+0

Câu trả lời thú vị! Khá chắc chắn đây là giải pháp tốt nhất hiện có, nhưng tôi sẽ thêm tiền thưởng để xem tôi có thể thu hút một số giải pháp khác không. –

+4

Đây là cách tôi sẽ làm điều đó. Sự khác biệt duy nhất là tôi sẽ thực hiện một cuộc gọi đồng bộ vài lần (tức là 10 lần) và sau đó sử dụng giờ hệ thống từ cuộc gọi có độ trễ thấp nhất. Thật vậy, độ trễ càng cao, bạn càng có nhiều tác động từ thực tế là sự phân phối giữa thời gian từ máy khách đến máy chủ và thời gian từ máy chủ đến máy khách không chính xác là 1: 1. –

+0

Có tùy chọn nào tốt hơn là chia thời gian phản hồi cho 2 không? Trong một số thử nghiệm của tôi, máy chủ chặn một lúc nhưng sau đó trả về nhanh chóng. Vì vậy, có lẽ thời gian chuyến đi là 200ms, nhưng thời gian quay trở lại chỉ là 20ms. Điều đó có ý nghĩa? – jocull

0

Bạn thực sự không thể đồng bộ hóa với máy chủ. Đo thời gian yêu cầu máy chủ của bạn (như MikeWyatt đề xuất) không phải là một chỉ báo tốt về độ trễ.

Chỉ máy chủ của bạn biết khi nào bạn trả lời yêu cầu. Do đó, nó sẽ gửi thông tin đó trở lại với câu trả lời. Với Date.now() - new Date(timestringOfServerResponse) bạn có thể đo độ trễ chính xác. Tuy nhiên, tôi không chắc chắn lý do tại sao bạn sẽ cần giá trị đó.

Để đồng bộ hóa ứng dụng giữa các thiết bị đa nhiệm, máy chủ sẽ gửi cho họ hành động nào cần thực hiện khi nào. "Khi" không nên "ngay sau khi bạn nhận được phản hồi của tôi", nhưng một dấu thời gian chính xác. Theo như đồng hồ hệ thống của thiết bị của bạn chính xác và đồng bộ hóa (thường là), ứng dụng sẽ chạy các phương thức của nó một cách đồng bộ, bởi vì nó biết điều gì sẽ xảy ra khi (hoặc ít nhất: điều gì sẽ xảy ra sau đó, và nó có thể nội suy những gì xảy ra "bây giờ").

+0

Đó chính là cách hoạt động của nó. Trang chờ cho đến thời điểm chính xác trước khi thực hiện một hành động để đảm bảo nó hoạt động trên các thiết bị. Vấn đề đi kèm với thực tế là tôi có hai iPad cạnh nhau được thiết lập để đồng bộ thời gian với các máy chủ thời gian, nhưng cách nhau 1 giây. –

+0

Vì vậy, bạn muốn triển khai máy chủ thời gian trong javascript, để đồng bộ hóa "thời gian hệ thống tùy chỉnh"? – Bergi

+2

@Bergi, bạn giả định sai rằng đồng hồ của khách hàng khớp với đồng hồ trên máy chủ. Chúng được đảm bảo thực tế là khác nhau, có thể bằng nhiều giây hoặc vài phút. Điều chỉnh độ trễ không hoàn hảo, nhưng nó sẽ giúp bạn khá gần. Trong trường hợp xấu nhất, bạn sẽ được tắt bởi thời gian chuyến đi khứ hồi đầy đủ, nếu một trong những chuyến đi được ngay lập tức. – MikeWyatt

0

Tôi đang sử dụng rộng rãi mẫu COMET tại đây cho ứng dụng web thời gian thực của tôi.

Để sử dụng điều đó trong trường hợp của bạn, bạn cần khách hàng mở yêu cầu AJAX tới máy chủ và chờ câu trả lời. Ngay sau khi khách hàng phải thay đổi trang trình bày.

Trên máy chủ, bạn phải giữ lại tất cả các câu trả lời cho đến lúc cần thay đổi trang trình bày. (Bạn có thể nâng cao hơn và trì hoãn sau đó trên máy khách trong cùng một thời điểm, nhưng điều đó rất có thể là không cần thiết). Tôi không thể hiển thị mã mẫu cho bạn ở đây vì tôi không biết mã nào có sẵn cho bạn.

Vì vậy, bạn đang tạo một dàn nhạc hiệu quả nơi máy chủ phát dây dẫn và tất cả khách hàng đều lắng nghe anh ấy.

Thời gian sau đó được xác định bởi khả năng của máy chủ để trả lời các yêu cầu tại (gần) cùng với thời gian chờ của mạng.
Thông thường, khách hàng phải ở trong cùng một phần của mạng để độ trễ có thể rất giống nhau - và giá trị tuyệt đối không bị tổn thương ở đây, chỉ có biến thể.

Và cũng có thể có thêm mẹo giúp: không thay đổi trang trình bày bằng cách thay thế cứng, trộn chúng. Điều này sẽ làm mờ sự thay đổi để mắt không thể nắm bắt được những khác biệt nhỏ về thời gian mà bạn sẽ luôn có.

(Nếu bạn không thể có dây dẫn chơi máy chủ, bạn có thể phải sử dụng giải pháp của MikeWyatt - có thể với một vài yêu cầu và tính trung bình kết quả, tùy thuộc vào thiết lập mạng. là đủ, đi qua internet đầy đủ một chút trên trung bình sẽ không làm tổn thương ...)

10

Tôi rất vui vì bạn đã tìm thấy câu trả lời thỏa đáng cho câu hỏi của bạn. Tôi đã có một nhu cầu tương tự để đồng bộ hóa trình duyệt với đồng hồ của máy chủ và được xác định để đạt được nó với độ chính xác cao hơn 1 giây như bạn. Tôi đã viết mã để làm điều này và đang đăng câu trả lời này ở đây trong trường hợp bất cứ ai khác cũng cần giải pháp.

Mã được gọi là ServerDate và có sẵn miễn phí để tải xuống. Đây là một phần của README. Lưu ý rằng tôi đã đạt được độ chính xác 108 ms trong ví dụ của mình:

Bạn có thể sử dụng ServerDate vì bạn sẽ sử dụng hàm Date hoặc một trong các trường hợp của nó, ví dụ::

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

> ServerDate.now() 
1344900478753 

> ServerDate.getMilliseconds() 
22 

Ngoài ra còn có một phương pháp mới để có được độ chính xác của ước lượng đồng hồ máy chủ của ServerDate (trong mili giây):

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

Bạn có thể thấy sự khác biệt giữa đồng hồ của máy chủ và các trình duyệt đồng hồ, tính bằng mili giây:

> ServerDate - new Date() 
39 
Các vấn đề liên quan