问题
I have a json file that has a structure like this:
[{"text_content":"aaaaa","text_duration":15,"start_time":"2015-10-07 23:25:29"},
{"text_content":"aawwaaaaa","text_duration":15,"start_time":"2015-10-07 23:25:44"},
{"text_content":"bbaawwaaaaa","text_duration":15,"start_time":"2015-10-07 23:25:59"}, etc.
I have a div on my webpage user-text
and I want to fill it with strings taken from this json. But there's a condition - I want to display the first text at the time assigned to it and for the period of duration assigned to it.
So based on my example I want to display aaaaa
for 15
seconds at 2015-10-07 23:25:29
, etc.
I wrote a jquery script:
var results =[];
var cursor = 0;
function myFunction () {
$.getJSON('get_json.php', function(json) {
results = json;
cursor = 0;
// Now start printing
if(results.length == 0){
$('#user-text').hide('fast', function() {
$('#user-text').html("there is no text at the moment");
$('#user-text').show('fast');
});
}else{
printNext();
}
});
}
function printNext(){
if(cursor == results.length){
// Reset the cursor back to the beginning.
cursor = 0;
}
var textDate = results[cursor].start_time;
var textMsg = results[cursor].text_content;
var dateOfText = new Date(textDate).setMilliseconds(0);
var duration = results[cursor].text_duration;
//now we're checking every second if there is any text to display at that time
var myInterval = setInterval(check, 1000);
function check() {
var currentDate = new Date().setMilliseconds(0);
if (currentDate - dateOfText ==0) {
clearInterval(myInterval);
$('#user-text').hide('fast', function() {
$('#user-text').html(textMsg);
$('#user-text').show('fast');
});
} else{
$('#user-text').html("there is no text to display at the moment");
}
}
// Set a delay for the current item to stay
// Delay is key2 * 1000 seconds
setTimeout(function(){
printNext();
}, results[cursor].text_duration * 1000);
// Advance the cursor.
cursor++;
}
but somehow it doesn't work. What happens is that the inside loop that goes every second is disturbed by the set timeout of the whole function.
So even if we find a match here if (currentDate - dateOfText ==0) {
- it won't appear on the screen for its all duration, because it will be interupted by this:
setTimeout(function(){
printNext();
}, results[cursor].text_duration * 1000);
I don't know exactly how to proceed at this point and how to make the logic work so that each text is played for its whole duration. Could you help me with that?
回答1:
Because you have the else
condition inside check
to hide the text. I'm assuming these messages to display are going to arrive well in advance, so you're going to have three instances of check
running all at the same time by the time the messages are ready to be displayed.
Since each one of those in the else condition is going to show the "there is no text to display" message every second, even when the first check
finally decides to show the first message, the other two are going to quickly overrun it and say no messages.
You might be able to just move the setTimeout
calling printNext
to inside the check
function in the true branch of the if
.
I've built a small demo with an alternate approach: http://jsfiddle.net/nauzilus/rqctam5r/
Rather than iterating over the messages
array, it will just focus on the first one, and shift it off once done.
You can push new entries into messages
and they'll be queued to display when appropriate.
Also, this assumes messages are always going to be loaded in advance; any messages with dates in the past will be processed as though they're ready to be viewed right now.
I haven't used jQuery, just vanilla JavaScript but you should be able to adapt it easily enough. Also, I'm checking every 500ms, and displaying when message is within 1000ms of schedule just to avoid any missed messages due to browser lag or blocking. Timers and schedules aren't guaranteed to run exactly when you think they will, so if a time takes 1001ms instead of 1000ms, you're going to miss a message!
回答2:
The call to setTimeout is badly placed. Put
setTimeout(printNext, results[cursor].text_duration * 1000);
in the same {}
code block as clearInterval(myInterval);
That is, stop the interval timer, show the text, and start a timer to call back printNext
to erase the text and check for new.
Replace this block of code in printNext
:
setTimeout(function(){
printNext();
}, results[cursor].text_duration * 1000);
with just
check();
to ensure text is erased in the callback.
回答3:
An alternate approach:
//global variable used to generate the element id for each new message
var message_index = 0;
function get_schedule() {
$.getJSON('get_json.php', function(json) {
schedule_messages(json);
});
}
function schedule_messages(schedule) {
var current_time = new Date();
//iterate through each message event
for(var i = 0; i < schedule.length; i++) {
var start_time = new Date(schedule[i].start_time);
//make sure the scheduled message time has not passed
if(start_time > current_time) {
var duration = schedule[i].text_duration * 1000;
//calculate time until message placement and removal
var display_delay = start_time - current_time;
var remove_delay = start_time - current_time + duration;
//schedule placement of the message
(function(delay, message, index) {
setTimeout(function() {
display_message(message, index);
}, delay);
})(display_delay, schedule[i].text_content, message_index);
//schedule removal of the message
(function(delay, index) {
setTimeout(function() {
remove_message(index);
}, delay);
})(remove_delay, message_index);
//increment global message index to use for next message
message_index++;
}
}
}
//do your fancy jQuery effect(s) in here...
function display_message(message, index) {
$('#user-text').append(
$("<p>")
.html(message)
.attr("id", "message-" + index)
);
}
//... and here
function remove_message(index) {
$("#message-" + index).remove();
}
来源:https://stackoverflow.com/questions/33002880/i-want-to-display-elements-from-json-based-on-their-time-and-duration-and-interv