JavaScript pausing and resuming animation google map - javascript

I am trying to pause Google Maps markers from animating when elements pushed into an array reaches a certain number. I have tried to clearing the timer and resuming it however i am having some problems.
I have used console.log() to see where control is accessing however the only message i am getting from console.log() is '500 records saved'. The timer gets cleared however control does not access the else condition of the block to resume the timer.
I have also looked at the SO question Pause and play google map marker animation
However i am not using buttons to pause and play.
Here is the code any guidance will be greatly appreciated.
Console.log()
'500 records saved' and animation stops does not resume
var lastVertex = 1;
var stepnum=0;
var step = 90; //0.9 metres
var tick = 100; // milliseconds
var eol = [];
StartAnimation
function startAnimation(index) {
if (timerHandle[index]) clearTimeout(timerHandle[index]);
eol[index]=polyline[index].Distance();
map.setCenter(polyline[index].getPath().getAt(0));
poly2[index] = new google.maps.Polyline({path: [polyline[index].getPath().getAt(0)], strokeColor:"#FFFF00", strokeWeight:3});
timerHandle[index] = setTimeout("animate("+index+",50)",2000); // Allow time for the initial map display
}
Animate
function animate(index,d) {
if (d>eol[index]) {
marker[index].setPosition(endLocation[index].latlng);
if(marker[index].getPosition() == endLocation[index].latlng){
console.log('Completed'+' '+index);
}
return;
}
var p = polyline[index].GetPointAtDistance(d);
marker[index].setPosition(p);
updatePoly(index,d);
timerHandle[index] = setTimeout("animate("+index+","+(d+step)+")", tick);
citizens1.push({lat:marker[index].getPosition().lat(),lng:marker[index].getPosition().lng(),socialSecurityNumber:global_citizens[index].socialSecurityNumber});
if(citizens1.length = 500){
console.log('500 records saved');
window.clearTimeout( timerHandle[index]);
citizens1 = []; //clear the array
}
}

Related

How to best track how long a video was played?

When the user watches a video, I would like to make 2 AJAX calls. One when the user finished watching the video and the time played is equal or more than the duration of the video (because users can rewind as well then). timePlayed>=duration && event.type=="ended". I successfully make the call for that.
Where I struggle is that I would also like to make a call when the video is watched more than 80% and the time played of the video is more than 80% as well in order to prevent the user from just fast forwarding.
In order for that to work I have to modify my videoStartedPlaying() method and this is where I come across issues as I am trying to set an interval. Now, with setting an interval, it is like an endless loop.
var video_data = document.getElementById("video");
var timeStarted = -1;
var timePlayed = 0;
var duration = 0;
// If video metadata is loaded get duration
if(video_data.readyState > 0)
getDuration.call(video_data);
//If metadata not loaded, use event to get it
else {
video_data.addEventListener('loadedmetadata', getDuration);
}
// remember time user started the video
function videoStartedPlaying() {
timeStarted = new Date().getTime()/1000;
setInterval(function(){
playedFor = new Date().getTime()/1000 - timeStarted;
checkpoint = playedFor / duration;
percentComplete = video_data.currentTime/video_data.duration;
// here I need help of how to best accomplish this
if (percentComplete >= 0.8 && checkpoint >= 0.8) {
// AJAX call here
}
}, 2000);
}
function videoStoppedPlaying(event) {
// Start time less then zero means stop event was fired vidout start event
if(timeStarted>0) {
var playedFor = new Date().getTime()/1000 - timeStarted;
timeStarted = -1;
// add the new amount of seconds played
timePlayed+=playedFor;
}
// Count as complete only if end of video was reached
if(timePlayed>=duration && event.type=="ended") {
// AJAX call here
}
}
function getDuration() {
duration = video_data.duration;
}
video_data.addEventListener("play", videoStartedPlaying);
video_data.addEventListener("playing", videoStartedPlaying);
video_data.addEventListener("ended", videoStoppedPlaying);
video_data.addEventListener("pause", videoStoppedPlaying);
I truly would appreciate any help with this as it seems like I am at my wits end.
Thanks so much!
Edit:
Thanks to the comment I came up with this:
const video = document.getElementById("video");
const set = new Set();
const percent = .8;
let toWatch;
function mediaWatched (curr) {
alert(`${curr}% of media watched`)
}
function handleMetadata(e) {
toWatch = Math.ceil(video.duration * percent);
console.log(toWatch, video.duration);
}
function handleTimeupdate (e) {
set.add(Math.ceil(video.currentTime));
let watched = Array.from(set).pop();
if (set.has(toWatch) && watched === toWatch) {
video.removeEventListener("timeupdate", handleTimeupdate);
console.log(watched);
mediaWatched(
Math.round(watched / Math.ceil(video.duration) * 100)
);
}
}
video.addEventListener("loadedmetadata", handleMetadata);
video.addEventListener("timeupdate", handleTimeupdate);
<video width="400" height="300" controls="true" poster="" id="video">
<source type="video/mp4" src="http://www.sample-videos.com/video/mp4/720/big_buck_bunny_720p_2mb.mp4" />
</video>
Now, for example, if I fast forward to around 50% length, and let it play then, it will fire whenever 80% of the movie is reached, but it shouldn't because I fast forwarded to 50% and essentially only watched 30%.
Does that make sense? How can I achieve such behavior?
per discussion in comments here's a working sample.
It includes a couple of handlers just to make life easier in setting up the array and summing the contents so you know when you have reached the 80% mark (though you may need to change that logic if you want to force them to, say, explicit watch the first 80% not just a total of 80% throughout the video).
There are a number of console.log(...) statements in there so you can watch what it's doing in the browser console window... you'll probably want to take them out before deploying for real.
I've put the hook for where to make the ajax call in the timeupdate event, but you could always use a regular setInterval timer in the main loop as well to check for the 80% and make the call there, but this seemed cleaner
most of it should be self explanatory, but do ask in comments if there's anything that's not clear...
<video controls preload="auto" id="video" width="640" height="365" muted>
<source src="http://www.sample-videos.com/video/mp4/720/big_buck_bunny_720p_2mb.mp4" type="video/mp4">
</video>
<script>
// handler to let me resize the array once we know the length
Array.prototype.resize = function(newSize, defaultValue) {
while(newSize > this.length)
this.push(defaultValue);
this.length = newSize;
}
// function to round up a number
function roundUp(num, precision) {
return Math.ceil(num * precision) / precision
}
var vid = document.getElementById("video")
var duration = 0; // will hold length of the video in seconds
var watched = new Array(0);
var reported80percent = false;
vid.addEventListener('loadedmetadata', getDuration, false);
vid.addEventListener('timeupdate',timeupdate,false)
function timeupdate() {
currentTime = parseInt(vid.currentTime);
// set the current second to "1" to flag it as watched
watched[currentTime] = 1;
// show the array of seconds so you can track what has been watched
// you'll note that simply looping over the same few seconds never gets
// the user closer to the magic 80%...
console.log(watched);
// sum the value of the array (add up the "watched" seconds)
var sum = watched.reduce(function(acc, val) {return acc + val;}, 0);
// take your desired action on the ?80% completion
if ((sum >= (duration * .8)) && !reported80percent) {
// set reported80percent to true so that the action is triggered once and only once
// could also unregister the timeupdate event to avoid calling unneeded code at this point
// vid.removeEventListener('timeupdate',timeupdate)
reported80percent = true;
console.log("80% watched...")
// your ajax call to report progress could go here...
}
}
function getDuration() {
console.log("duration:" + vid.duration)
// get the duration in seconds, rounding up, to size the array
duration = parseInt(roundUp(vid.duration,1));
// resize the array, defaulting entries to zero
console.log("resizing arrary to " + duration + " seconds.");
watched.resize(duration,0)
}
</script>

Playing a data set using angularjs

I'm using angularjs as front-end framework. I get a data set via a rest get call.
That contains coordinates in an array. What needs to be done is that array with coordinates should be iterated and should be shown in a map.
Once the user hits play in the map those coordinates should be displayed one ofter another having at least 1 second of interval.
And when user hits play button, the button it self converts to a pause button it should do what the name intend to do. pause the process. I couldn't achive this kind of behavior using angularjs. Following is the closest that I could come.
var getReplayData = function () {
return $http.get('http://localhost:4000/replay/asiri/vin/' + from + '/' + to);
};
$scope.play = function () {
from = rangeProperties.getFrom() * 1000;
to = rangeProperties.getTo() * 1000;
getReplayData().success(function (data) {
console.log(data);
var waitingTime = 0;
var gap = 0;
for (var i = 0; i < data.length; i++) {
(function (i) {
var element = data[i];
var coordinates = new Object();
coordinates.latitude = element.LATITUDE;
coordinates.longitude = element.LONGITUDE;
setTimeout(function () {
broadcastData(coordinates);
}, waitingTime);
if (i + 1 < data.length) {
gap = data[i + 1].TIMESTAMP - element.TIMESTAMP;
console.log(gap);
} else {
gap = 0;
}
waitingTime = waitingTime + gap;
})(i);
}
});
$scope.play refers to the play action of the button. I can't figure out how to pause this process. Seems like I might have to keep the references to all timeouts and cancel them. Any idea how to implement this kinda of scenario? I don't exactly need a code segment just an idea how to approach to solve this kinda of problem would be really nice.
Seems like I might have to keep the references to all timeouts and cancel them.
That would be a good first step.
I don't exactly need a code segment just an idea how to approach to solve this kinda of problem would be really nice
I would do the following:
cache the response data as a variable, if possible.
separate the play and pause functionality, tying them into a cancelable callback that you get from using the $interval service.
during playback, treat your coordinates data as a FIFO (queue), dequeuing the item which gets passed to the $interval's promise
during pause, you simply cancel the $interval promise
playing again doesn't require any fancy work on the queue as you'll resume the same logic on the first item in the queue when you resume.

Javascript page gets laggy after a few hours when calling a function every 10 seconds

I'm refreshing data in my javascript page by calling a function every 10 seconds which requests most recent data from my server.
This is the function that is called every 10 seconds:
function loadEvents() {
//Angular $resource factory
EventsService.getEvents({}, function(response) {
var events = response.events;
var now = new Date().getTime();
$scope.currentevent = null;
for(var i = 0; i<events.length && $scope.currentEvent === null; i++){
var value = events[i];
if(new Date(value.start).getTime()<now && new Date(value.end).getTime()>now){
$scope.currentEvent = value;
}
}
//I use daypilot to show the events, the below code only updates the list of events if
//The events have changed
var currentEvents = JSON.stringify($scope.scheduler.events.list);
var newEvents = JSON.stringify(events);
if(currentEvents!=newEvents){
$scope.scheduler.events.list = events;
$scope.scheduler.update();
}
$timeout(function () {
loadEvents();
}, 10000);
});
}
For some reason, the page gets laggy after about 5 hours. This is specially noticeable on slow devices like my android tablet. This issue doesn't occur after removing the $timeout. This seems like a memory issue? I can solve the issue by just refreshing the page by calling location.reload(); every 5 hours, but this doesn't seem the best solution. I rather want to solve the issue that is causing this behavior.
I also measured how long it takes to execute the function and get the response from server, this never takes longer than 100ms. The events returned by the server is only for 1 day and is just a few kilobytes.

Multiple setInterval() issue with AJAX calls - (cooldown applied to items in inventory)

I am having trouble making the following code working properly. The correct behavior should be > on _loadInventory() apply _loadCooldowns() on all .cooldown items (a countdown). This works fine but the problems start when one of the items finishes its cooldown and refreshes the inventory (_loadInventory()). There seems to be an interval stuck that keeps reloading the _loadInventory, breaking the app. I am always clearing all the intervals and even purging the intervals array. I hope the code below will make more sense
// Loading the inventory
function _loadInventory(){
// Fetching the inventory from PHP
_ajaxFetch("php/ajax/conq-get-inventory.php", {ord : 0}).done(function(data){
// Clearing inventory
$('.inventory-field').each(function(){
$(this).html('');
})
// Adding the data to html
$.each(data.html, function(key, value){
$('.inventory-field[data-inventory-field-id="' + key + '"]').html(value);
})
// HERE i am cleaning all the old intervals
$.each(intervals, function(key, value){
clearInterval[intervals[key]];
})
// HERE i am clearing the intervals array
intervals = [];
//buffs = [];
// Teoretically now there should be no intervals, so run the function that assigns the cooldowns for each item in the inventory
_loadCooldowns();
// Other not important code
$('#pesos-wrapper').html(data.pesos);
_startTooltip()
$('#trash-wrapper').droppable({
accept: ".item",
drop: function(event, e){
_ajaxFetch("php/ajax/conq-delete-item.php", {id: e.draggable.attr('data-item-id')}, 1).done(_loadInventory);
e.draggable.remove();
}
});
})
}
Here is the code that applies the cooldown to all the items that have .cooldown class
function _loadCooldowns(){
// If there are any cooldown
if($('.cooldown').length > 0){
// Apply the cooldown for each item
$('.cooldown').each(function(){
// The cooldown element
var e = $(this);
// Just a check
if($(this).attr('data-cooldown-type') == 'item'){
// The id of the item (unique)
var id = $(this).attr('data-item-id');
// Start the interval and save it to the Intevals array
intervals[id] = setInterval(function(){ _applyCooldown(e, id) }, 1000);
}
})
}
}
Here is the actual function inside the Interval
function _applyCooldown(e, id){
// Get the current time in seconds
var time = parseInt(e.attr('data-cooldown'));
// If the time is more than 0
if(time > 0){
// Add new time
if(time > 60){
time--;
e.html(Math.round(time / 60) + 'm');
e.attr('data-cooldown', time);
}
else{
time--;
e.html(time + 's');
e.attr('data-cooldown', time);
}
}
// Otherwise
else{
// Remove this cooldown element
e.remove();
// RELOAD INVENTORY! Here when the inventory is reloaded it should clear all the current intervals and start the full procedure again
// But that does not happen, some intervals get stuck and persist
// Resulting in anonymous intervals being run in the background that keep reloading the inventory all the time as their time is not > 0 (most likely undefined)
_loadInventory();
}
}
}
Any help is appreciated as I am already pulling my hair out. Thanks!!
Fixed!
A really simple fix I think could help a lot of people.
Instead of saving the interval to an array I did the following:
window.interval_id = window.setInterval(function(){ _applyCooldown(e, id) }, 1000);
Now every time I want to reset the intervals I just do
function _resetIntervals(){
for (var i = 1; i <= window.interval_id; i++){
window.clearInterval(i);
window.i = false;
}
}
Works like a charm

Leaflet layers somehow interfering with each other

I have a javascript function that creates a leaflet layer group, adds it to a map, removes it and then repeats. Each time, it goes through a json array that has the data which is placed in the layer group. The effect is an animation of data points on the map.
There are two data sets which are formatted to be animated with this function. Let's call them Nowcast and Forecast. In my app, if I run the animations in the following sequence:
Nowcast, Forecast, Nowcast, Forecast, Nowcast
On the third Nowcast run, it looks like some of the data from the Forecast shows up along with the Nowcast data. Not all the data, and only for a few iterations, but then it just sits there and does not disappear when the animation is over. The only way to get this weird residual data is to run the Forecast animation again. (running the Nowcast again does not remove it).
Anyway, here is the function:
function animateAshData(inputData,dataLayer) {
var currentTime = inputData.ashData[t];
var currentAshData = currentTime.concentrations;
window.Android.toLogCat("t = " + currentTime.date + " " + t);
window.Android.toLogCat("Current set size= " + currentAshData.length);
if (dataLayer != undefined) { //The map has a data layer already been created from previous time step, so remove it
removeDataLayer(dataLayer);
}
var currentAshLayerGroup = new L.LayerGroup();
for (j = 0; j<currentAshData.length; j++) {
L.marker([currentAshData[j].lat, currentAshData[j].lon],{icon: ashIcon}).addTo(currentAshLayerGroup);
map.addLayer(currentAshLayerGroup);
};
t = t+1;
// Queue up the animation to the next time step
tid = setTimeout(function(){
if (t > finalt) {
//end of animation
window.Android.toLogCat("Done with animation");
removeDataLayer(currentAshLayerGroup);
window.clearTimeout(tid);
} else {
// delete currentTime.concentrations;
clearMap();
animateAshData(inputData,currentAshLayerGroup);
}
}, 100);
My first thought was that the layer groups were somehow persisting, even though I had a map.removeLayer(layer) in there (although it is mysterious why it takes 3 iterations instead of just 2 for this to show up...). So, I made a removeDataLayer() function and added a layer.clearLayers(). This did not work either. I finally added the function from here Clear all polylines from leaflet map to just try clearing everything, but this still did not work. Here is my removeDataLayer function:
function removeDataLayer(layer){
map.removeLayer(layer);
layer.clearLayers();
clearMap();
};
function clearMap(){
for(i in map._layers){
if(map._layers[i]._path != undefined)
{
try{
map.removeLayer(map._layers[i]);
}
catch(e){
console.log("problem with " + e + map._layers[i]);
}
}
}
};
Anyway, I can't imagine I am the only one with problems with leaflet layers interfering with each other and/or problems removing them completely. Thanks in advance.

Categories