I'm creating a rewards site where people can watch videos to earn points. The problem is that people will skip to the end of the video to earn their points. Therefore, I need to track when the video is done by a timer.
I have the following code:
var video_percent_count = 0;
function video_percent() {
var prize_video = document.getElementById("prize_video");
total_duration = Math.floor(prize_video.duration) + 1;
video_percent_count++;
percent = =total_duration / video_percent_count;
alert(percent);
}
To summarize, the code is adding to a variable every second, this is the timer. The function then grabs the total duration, then divides it by the timer for a percentage.
The function is not outputting a proper percentage, why is this code incorrect?
You've got a syntax error:
percent = =total_duration / video_percent_count;
Should be:
percent = total_duration / video_percent_count;
Notice the second = is removed.
In the future, you can use the web console to find simple syntax errors such as this one.
Related
I have an ASCII art "pathfinding visualizer" which I am modeling off of a popular one seen here. The ASCII art displays a n by m size board with n*m number of nodes on it.
My current goal is to slowly change the appearance of the text on the user-facing board, character by character, until the "animation" is finished. I intend to animate both the "scanning" of the nodes by the pathfinding algorithm and the shortest path from the start node to the end node. The animation, which is just changing text in a series of divs, should take a few seconds. I also plan to add a CSS animation with color or something.
Basically the user ends up seeing something like this, where * is the start node, x is the end node, and + indicates the path:
....
..*.
..+.
.++.
.x..
.... (. represents an empty space)
After doing some research on both setTimeout, promises and other options, I can tell you:
JavaScript really isn't designed to allow someone to delay code execution in the browser.
I've found lots of ways to freeze the browser. I also tried to set a series of promises set to resolve after setTimeout(resolve, milliseconds) occurs, where the milliseconds steadily increases (see below code). My expectation was that numOfAnimationsForPath number of promises would be set and trigger a change in the appearance of the board when each one resolved (forming the appearance of a path). But, they all seem to resolve instantly (?) as I see the path show as soon as I click the "animate" button
const shortestPathAndScanningOrder = dijkstras(grid);
promisesRendering(1000, shortestPathAndScanningOrder[0].path, shortestPathAndScanningOrder[1])
function promisesRendering(animationDelay, algoPath, scanTargets) {
const numOfAnimationsForScanning = scanTargets.length;
const numOfAnimationsForPath = algoPath.length;
for (let i = 1; i < numOfAnimationsForPath - 1; i++) {
const xCoordinate = algoPath[i][0];
const yCoordinate = algoPath[i][1];
renderAfterDelay(animationDelay * i).then(renderNode(xCoordinate, yCoordinate, "path"))
}
}
function renderAfterDelay(milliseconds) {
return new Promise(resolve => setTimeout(resolve, milliseconds))
}
function renderNode(x, y, type) {
if (type === "scan") {
const targetDiv = getLocationByCoordinates(x, y);
targetDiv.innerHTML = VISITED_NODE;
} else if (type === "path") {
const targetDiv = getLocationByCoordinates(x, y);
targetDiv.innerHTML = SHORTEST_PATH_NODE;
} else {
throw "passed incorrect parameter to 'type' argument"
}
}
In my other attempt, I tried to generate pathLength number of setTimeouts as in:
const shortestPathAndScanningOrder = dijkstras(grid);
renderByTimer(10000, shortestPathAndScanningOrder[0].path, shortestPathAndScanningOrder[1])
function renderByTimer(animationDelay, algoPath, scanTargets) {
const numOfAnimations = algoPath.length;
for (let i = 1; i < numOfAnimations - 1; i++) {
const xCoordinate = algoPath[i][0];
const yCoordinate = algoPath[i][1];
setTimeout(i * animationDelay, updateCoordinatesWithTrailMarker(xCoordinate, yCoordinate))
}
}
...but this also resulted in the path being "animated" instantly instead of over a few seconds as I want it to be.
I believe what I want is possible because the Pathfinding Visualizer linked at the start of the post animates its board slowly, but I cannot figure out how to do it with text.
So basically:
If anyone knows how I can convince my browser to send an increasing delay value a series of function executions, I'm all ears...
And if you think it can't be done, I'd like to know that too in the comments, just so I know I have to choose an alternative to changing the text slowly.
edit: a friend tells me setTimeout should be able to do it... I'll update this w/ a solution if I figure it out
Edit2: Here is the modified version of #torbinsky's code that ended up doing the job for me...
function renderByTimer(algoPath, scanTargets) {
const numOfAnimations = algoPath.length - 1; // - 1 because we don't wanna animate the TARGET_NODE at the end
let frameNum = 1;
// Renders the current frame and schedules the next frame
// This repeats until we have exhausted all frames
function renderIn() {
if (frameNum >= numOfAnimations) {
// end recursion
console.log("Done!")
return
}
// Immediately render the current frame
const xCoordinate = algoPath[frameNum][0];
const yCoordinate = algoPath[frameNum][1];
frameNum = frameNum + 1;
updateCoordinatesWithTrailMarker(xCoordinate, yCoordinate);
// Schedule the next frame for rendering
setTimeout(function () {
renderIn(1000)
}, 1000);
}
// Render first frame
renderIn()
}
Thanks #torbinsky!
This should absolutely be doable using setTimeout. Probably the issue is that you are immediately registering 10,000 timeouts. The longer your path, the worse this approach becomes.
So instead of scheduling all updates right away, you should use a recursive algorithm where each "frame" schedules the timeout for the next frame. Something like this:
const shortestPathAndScanningOrder = dijkstras(grid);
renderByTimer(10000, shortestPathAndScanningOrder[0].path, shortestPathAndScanningOrder[1])
function renderByTimer(animationDelay, algoPath, scanTargets) {
const numOfAnimations = algoPath.length;
// Renders the current frame and schedules the next frame
// This repeats until we have exhausted all frames
function renderIn(msToNextFrame, frameNum){
if(frameNum >= numOfAnimations){
// end recursion
return
}
// Immediately render the current frame
const xCoordinate = algoPath[frameNum][0];
const yCoordinate = algoPath[frameNum][1];
updateCoordinatesWithTrailMarker(xCoordinate, yCoordinate);
// Schedule the next frame for rendering
setTimeout(msToNextFrame, function(){
renderIn(msToNextFrame, frameNum + 1)
});
}
// Render first frame
renderIn(1000, 1)
}
Note: I wrote this code in the StackOverflow code snipppet. So I was not able to test it as I did not have the rest of your code to fully run this. Treat it more like pseudo-code even though it probably works ;)
In any case, the approach I've used is to only have 1 timeout scheduled at any given time. This way you don't overload the browser with 1000's of timeouts scheduled at the same time. This approach will support very long paths!
This is a general animation technique and not particularly unique to ASCII art except that old-school ASCII art is rendered one (slow) character at a time instead of one fast pixel frame at a time. (I'm old enough to remember watching ASCII "movies" stream across hard-wired Gandalf modems at 9600bps to a z19 terminal from the local mainframe...everything old is new again! :) ).
Anyhow, queueing up a bunch of setTimeouts is not really the best plan IMO. What you should be doing, instead, is queueing up the next event with either window.requestAnimationFrame or setTimeout. I recommend rAF because it doesn't trigger when the browser tab is not showing.
Next, once you're in the event, you look at the clock delta (use a snapshot of performance.now()) to figure what should have been drawn between "now" and the last time your function ran. Then update the display, and trigger the next event.
This will yield a smooth animation that will play nicely with your system resources.
I’ve run into a frustrating brick wall when using:
projectItem.setInPoint(seconds)
projectItem.setOutPoint(seconds)
…about 50% of the time the I/O points (in the source window) are set with a 1 frame error (sometimes 2 frames out). I feel like I’ve tried everything to discover what the pattern is, but it seems entirely random.
I thought it might be something to do with drop frame, variable frame rates, the clip being different from the sequence, or other oddities, but the error occurs at simple constant frame rates like 25 fps. There just seems to be no rhyme or reason to the errors (although the same error occurs consistently on certain frames).
There’s even a bigger problem with subclips, as the scripting environment thinks that all subclips start at frame 0.
I’ve tried everything, including working in ticks, seconds or frames, and converting between them. Nothing made a difference.
What I’m trying to accomplish is to set in/out on a set of clips, run a script to make smaller cuts from those source clips, and then restore the clips to the original I/O points. Got most of this working except I can’t restore all the clips to the original I/O points with this bug.
Below is a test script I wrote. It gets the current I/O positions, stores them, and then sets them back to the same clip. Half the time the values are not the same! Argh! This makes it impossible set the clips I/O accurately.
function framesToSeconds (frames, fps)
{
return frames / fps;
}
function secondsToFrames (sec, fps)
{
return sec * fps;
}
/*---------------------------------------------------*/
var projItems = app.project.rootItem.children;
var clip = projItems[2];
var fps = clip.getFootageInterpretation().frameRate;
var setIn = clip.getInPoint().seconds;
var setOut = clip.getOutPoint().seconds;
var inFrame = secondsToFrames (setIn, fps);
var outFrame = secondsToFrames (setOut, fps);
var secIn = framesToSeconds (inFrame, fps);
var secOut = framesToSeconds (outFrame, fps);
clip.setInPoint( secIn );
clip.setOutPoint( secOut );
var setIn = clip.getInPoint().seconds;
var setOut = clip.getOutPoint().seconds;
I’ve done some more testing. Although I don’t quite understand the source of the error, I believe I have figured out a fix for it.
I tested 2 slugs, each 10 seconds long with different frame rates, and ran them through a loop and set the IO points of each frame. I checked each frame to see which frames came back wrong. What I found was:
test_25fps_1280x720.mov : error on frames 211,209,207,205,203,201
test_29fps_1024x576.mov : error on frames 251,244,242,122,121,61
These errors weren’t random. Whenever I tried to set an in or out point on these frames it would ALWAYS round down 1 frame (I was wrong before about 50% of the frames being off, it’s actually less than 3%).
My best guess is that there’s some precision error because there’s a calculation going somewhere involving large floating point numbers. I can’t confirm it, nor do I really understand how to fix that. But I did figure out I could test the in and out points after setting them, and if it didn’t match expectations I could reset the point by adding the duration of half a frame (in seconds). A full frame would just repeat the error, but half a frame would get Premiere to round up to the correct frame.
This is the main part of my code:
/*---------------------------------------------------*/
function fixAnyFrameErrors (clip, inFrame, outFrame, fps, halfFrame)
/*---------------------------------------------------*/
{
var inSecSet = clip.getInPoint().seconds;
var inFrameSet = secondsToFrames (inSecSet, fps);
var outSecSet = clip.getOutPoint().seconds;
var outFrameSet = secondsToFrames (outSecSet, fps);
if ( parseFloat(inFrame) != parseFloat(inFrameSet) ) {
clip.setInPoint( secIn + halfFrame );
}
if ( parseFloat(outFrame) != parseFloat(outFrameSet) ) {
clip.setOutPoint( secOut + halfFrame );
}
}
/*---------------------------------------------------*/
var tps = 254016000000; // 2.54016e11 ticks per second (Premiere Pro constant)
var projItems = app.project.rootItem.children;
var clip = projItems[2];
clip.addMetadata();
var fps = clip.getFootageInterpretation().frameRate;
var tpf = clip.videoInPoint.frame_rate;
var frameDuration = tpf / tps; // in seconds
var halfFrame = (frameDuration * 0.5);
var inFrame = 201;
var outFrame = 211;
var secIn = framesToSeconds (inFrame, fps);
var secOut = framesToSeconds (outFrame, fps);
clip.setInPoint ( secIn );
clip.setOutPoint( secOut );
fixAnyFrameErrors (clip, inFrame, outFrame, fps, halfFrame);
My observations are:
(We encounter the problem when setting markers.)
Our calculation is correct, but Premiere sets marker seconds e.g. to 8.35999999999606 instead of 8.36 (at 25fps), the marker is shown at 8secs 9frames, but the displayed value is 8secs 8frames.
From Adobe Forums:
https://community.adobe.com/t5/premiere-pro/clip-marker-different-start-end-time-in-seconds/m-p/9309128?page=1
they suggest using an epsilon to check if a value is very close to the calculated value.
In the example, the difference is 0,00000000000394 - which is very low and should be corrected to 0.
I do not want to know what happens if we used framerates with dropframes (23.976 for example)...
EDIT: you would have to correct the time inside the existing marker, in my case, so this does not work. i will try to add half a frame...
I was wondering if it were possible to implement some kind of crude JavaScript anti-flood protection.
My code receives events from a server through AJAX, but sometimes these events can be quite frequent (they're not governed by me).
I have attempted to come up with a method of combating this, and I've written a small script: http://jsfiddle.net/Ry5k9/
var puts = {};
function receiverFunction(id, text) {
if ( !puts[id] ) {
puts = {};
puts[id] = {};
}
puts[id].start = puts[id].start || new Date();
var count = puts[id].count = puts[id].count + 1 || 0;
var time = (new Date() - puts[id].start) * 0.001;
$("text").set("text", (count / time.toFixed()).toString() + " lines/second");
doSomethingWithTextIfNotSpam(text);
}
};
which I think could prove effective against these kinds of attacks, but I'm wondering if it can be improved or perhaps rewritten?
So far, I think everything more than 3 or 2.5 lines per second seems like spam, but as time progresses forward (because start mark was set... well... at the start), an offender could simply idle for a while and then commence the flood, effectively never passing 1 line per minute.
Also, I would like to add that I use Mootools and Lo-Dash libraries (maybe they provide some interesting methods), but it would be preferable if this can be done using native JS.
Any insight is greatly appreciated!
If you are concerned about the frequency a particular javascript function fires, you could debounce the function.
In your example, I guess it would be something like:
onSuccess: function(){ _.debounce(someOtherFunction, timeOut)};
where timeout is the maximum frequency you want someOtherFunction to be called.
I know you asked about native JavaScript, but maybe take a look at RxJS.
RxJS or Reactive Extensions for JavaScript is a library for
transforming, composing, and querying streams of data. We mean all
kinds of data too, from simple arrays of values, to series of events
(unfortunate or otherwise), to complex flows of data.
There is an example on that page which uses the throttle method to "Ignores values from an observable sequence which are followed by another value before dueTime" (see source).
keyup = Rx.Observable.fromEvent(input, 'keyup').select(function(ev) {
return ev.target.value;
}).where(function(text) {
return text.length > 2;
}).throttle(500)
.distinctUntilChanged()
There might be a similar way to get your 2.5-3 per second and ignore the rest of the events until the next second.
I've spent many days pondering on effective measures to forbid message-flooding, until I came across the solution implemented somewhere else.
First, we need three things, penalty and score variables, and a point in time where last action occured:
var score = 0;
var penalty = 200; // Penalty can be fine-tuned.
var lastact = new Date();
Next, we decrease score by the distance between the previous message and current in time.
/* The smaller the distance, more time has to pass in order
* to negate the score penalty cause{d,s}.
*/
score -= (new Date() - lastact) * 0.05;
// Score shouldn't be less than zero.
score = (score < 0) ? 0 : score;
Then we add the message penalty and check if it crosses the threshold:
if ( (score += penalty) > 1000 ) {
// Do things.
}
Shouldn't forget to update last action afterwards:
lastact = new Date();
First of all I want to mention two things,
One: My code isn't perfect (esspechially the eval parts) - but I wanted to try something for my self, and see if I could duplicate the jQuery Animation function, so please forgive my "bad" practices, and please don't suggest that I'll use jQuery, I wanted to experiment.
Two: This code isn't done yet, and I just wanted to figure out what makes it work badly.
So the animation runs for about 12 seconds while the duration parameter I entered was 15 seconds, What am I doing wrong?
function animate(elem, attr, duration){
if(attr.constructor === Object){//check for object literal
var i = 0;
var cssProp = [];
var cssValue = [];
for(key in attr) {
cssProp[i] = key;
cssValue[i] = attr[key];
}
var fps = (1000 / 60);
var t = setInterval(function(){
for(var j=0;j<cssProp.length;j++){
if(document.getElementById(elem).style[cssProp[j]].length == 0){
//asign basic value in css if the object dosn't have one.
document.getElementById(elem).style[cssProp[j]]= 0;
}
var c = document.getElementById(elem).style[cssProp[j]];
//console.log(str +" | "+c+"|"+cssValue[j]);
if(c > cssValue[j]){
document.getElementById(elem).style[cssProp[j]] -= 1/((duration/fps)*(c-cssValue[j]));
}else if(c < cssValue[j]){
document.getElementById(elem).style[cssProp[j]] += 1/((duration/fps)*(c-cssValue[j]));
}else if(c == cssValue[j]){
window.clearInterval(t);
}
}
},fps);
}
}
animate('hello',{opacity:0},15000);
html:
<p id="hello" style="opacity:1;">Hello World</p>
Note: I guess there is a problem with the
(duration/fps)*(c-cssValue[j])
Part or/and the interval of the setInterval (fps variable).
Thanks in advance.
I'm not gonna try and refactor that and figure it out, cause it's pretty wonky. That said... a few things.
Don't rely on the value you are animating to let you know animation progress
In general your approach is unsound. You are better off keeping track of progress yourself. Also, as a result of your approach your math seems like it's trying too hard, and should be much simpler.
Think of it like this: your animation is complete when the time has elapsed, not when the animated value seems to indicate that it's at the final position.
Don't increment, set
Floating point math is inexact, and repeated addition cumulation like this is going accumulate floating point errors as well. And it's far more readable to make some variables to keep track of progress for you, which you can use in calculations.
animatedValue += changeOnThisFrame // BAD!
animatedValue = valueOnThisFrame // GOOD!
Don't do the positive/negative conditional dance
It turns out that 10 + 10 and 10 - (-10) is really the same thing. Which means you can always add the values, but the rate of change can be negative or positive, and the value will animate in the appropriate direction.
timeouts and intervals aren't exact
Turns out setTimeout(fn, 50) actually means to schedule the fn to be call at least 50ms later. The next JS run loop to execute after those 50ms will run the function, so you can't rely on it to be perfectly accurate.
That said it's usually within a few milliseconds. But 60fps is about 16ms for frame, and that timer may actually fire in a variable amount of time from 16-22ms. So when you do calculations based on frame rate, it's not matching the actual time elapsed closely at all.
Refactor complex math
Deconstructing this line here is gonna be hard.
document.getElementById(elem).style[cssProp[j]] -= 1/((duration/fps)*(c-cssValue[j]));
Why for more complex break it up so you can easily understand what's going on here. refactoring this line alone, I might do this:
var style = document.getElementById(elem).style;
var changeThisFrame = duration/fps;
var someOddCalculatedValue = c-cssValue[j];
style[cssProp[j]] -= 1 / (changeThisFrame * someOddCalculatedValue);
Doing this makes it clearer what each expression in your math means and what it's for. And because you didn't do it here, I had a very hard time wondering why c-cssValue[j] was in there and what it represents.
Simple Example
This is less capable than what you have, but it shows the approach you should be taking. It uses the animation start time to create the perfect value, depending on how complete the animation should be, where it started, and where it's going. It doesn't use the current animated value to determine anything, and is guaranteed to run the full length of the animation.
var anim = function(elem, duration) {
// save when we started for calculating progress
var startedAt = Date.now();
// set animation bounds
var startValue = 10;
var endValue = 200;
// figure out how much change we have over the whole animation
var delta = endValue - startValue;
// Animation function, to run at 60 fps.
var t = setInterval(function(){
// How far are we into the animation, on a scale of 0 to 1.
var progress = (Date.now() - startedAt) / duration;
// If we passed 1, the animation is over so clean up.
if (progress > 1) {
alert('DONE! Elapsed: ' + (Date.now() - startedAt) + 'ms');
clearInterval(t);
}
// Set the real value.
elem.style.top = startValue + (progress * delta) + "px";
}, 1000 / 60);
};
anim(document.getElementById('foo'), 5000);
JSFiddle: http://jsfiddle.net/DSRst/
You cannot use setInterval for accurate total timing. Because JS is single threaded and multiple things compete for cycles on the one thread, there is no guarantee that the next interval call will be exactly on time or that N intervals will consume the exact duration of time.
Instead, pretty much all animation routines get the current time and use the system clock to measure time for the total duration. The general algorithm is to get the start time, calculate a desired finish time (starttime + duration). Then, as you've done, calculate the expected step value and number of iterations. Then, upon each step, you recalculate the remaining time left and the remaining step value. In this way, you ensure that the animation always finishes exactly (or nearly exactly) on time and that you always get exactly to the final position. If the animation gets behind the ideal trajectory, then it will self correct and move slightly more for the remaining steps. If it gets ahead for any reason (rounding errors, etc...), it will dial back the step size and likewise arrive at the final position on time.
You may also need to know that browsers don't always support very small timing amounts. Each browser has some sort of minimum time that they will allow for a timer operation. Here's an article on minimum timer levels.
Here's an article on tweening (the process of continually recalculating the step to fit the duration exactly).
I'd also suggest that you look at the code for doing animation in some libraries (jQuery, YUI or any other one you find) as they can all show you how this is done in a very general purpose way, including tweening, easing functions, etc...
Let me explain what I'm trying to do.
I want to make a simple box which counts down numbers at intervals I specify.
For example, I'd like to set it to start at 150, and then I want to set it to drop by 15 every 30 seconds.
Is this possible with AJAX/Javascript? If so, could someone point me in the right direction?
Would really appreciate any help on this script, been Googling for hours now! :(
Cheers
Kieran
Have a look at the setTimeout or setInterval methods, they allow you to execute a function after a specified number of milliseconds (1000ms = 1second). Use that, to call a function that keeps dropping the number and writes it to a HTML element to the user can see it.
this isn't tested, but i hope it shows you the way to go.
var start = 150;
var drop = 15;
var interval = 30;
function countdown(){
document.getElementById('mybox').innerHTML = start;
start-=drop;
window.setTimeout("countdown",interval*1000);
}
countdown();
You may use jQuery to do that, see http://keith-wood.name/countdown.html -> tab Callbacks
Keep in mind that 30 seconds in my browser are not necessarily equal to 30 seconds in your browser. It depends on the workload of the browser.
The time difference is minor for a short time but can increase over a long time. The times will drift apart. If the times must not be equal (or nearly equal) between two visitors than such simple solution should be fine.
We had once a problem to introduce a live clock / countdown in one of our projects. We build a script with javascript, ajax and PHP for clock synchronisation (server time was timeserver).
You should use setInterval / clearInterval which is made for this kind of tasks:
function cooldown(element, start, stop, step, delay) {
var current = start;
element.innerHTML = current;
var timer = setInterval(function () {
current -= step;
if(current < stop) current=stop;
element.innerHTML = current;
if(current == stop) clearInterval(timer);
}, delay*1000);
}
Demonstrated here : http://jsfiddle.net/PCMHn/