My project currently features quizzes, which have a countdown timer using Javascript. This time then resets to 7 seconds after it has counted down fully or a the user selects a multiple-choice answer.
That function is working, but it just shows how much time is left, in text. I want to make a "bar" (think: column) that counts down (so, if a quiz gives you 7 seconds, the bar reduces 1/7th each time). jQuery and Javascript seem to be the easiest way to do this, so I added:
var height = 50;
var timePassed = 0;
var totalSeconds = 7;
var reduceHeight = function(elem, totalSeconds){
timePassed++;
height = 100 - (100/totalSeconds * timePassed);
elem.css({height: height + "%"});
if(height > 0){
window.setTimeout(function(){
reduceHeight(elem, totalSeconds)
}, 1000)
}
}
$(document).ready(function(){
var inner = $('#inner');
reduceHeight(inner, 20);
});
The bar now appears and does count down, but it is not completing reduceHeight at the correct rate.
Since I am defining the totalSeconds variable, it seems like it should since height is being set at 100/totalSeconds * timePassed. To reiterate, as the timer will run for 7 seconds, I would want it to become 1/7th smaller in each passing second.
Any idea where I went wrong?
I think your height calculation logic is incorrect
it should be
height = 100 - ( ( timePassed/totalSeconds ) * 100 );
since for example totalSeconds is 20 and timePassed is 10
then you height should be 50,
height = 100 - ( ( 10/20 ) * 100 ) = 50;
Which means that you need to
get the ratio of timePassed with totalSeconds
and then get the percentage out of that ratio
so that you can reduce that percentage value from 100
Related
I am developing an XP/Leveling system for a website. Think of it like Call of Duty levels.
For example
Level 1 = 0 - 100XP
Level 2 = 100 - 200XP
Level 3 = 200 - 300XP
Now obviously for level 1, since it's 100 level max, I can do
const currentXp = 50;
const nextLevel = 100;
function calculateProgress(currentXp ,nextLevel) {
return (currentXp / nextLevel) * 100;
}
// returns 50% (which is correct), this means the progress bar would be halfway full.
However, when you get past 100, this equation does not work anymore. For example,
const currentXp = 150
const nextLevel = 200
function calculateProgress(currentXp, nextLevel) {
return (currentXp / nextLevel) * 100;
}
// returns 75%, and the progress bar is 75% full, when it should be 50% full, because 150 is halfway in between 100 and 200 i.e 50% progress in between the levels.
This is where thinking of call of duty comes into play. I am trying to calculate the progress between 2 levels. Not simply the percentage a particular number is of another number.
I have spent numerous hours trying to come up with a solution and have utterly failed.
Any insight/advice/help would be so very much appreciated. Thank you so much for your time.
you should use:
((currentXp - previousLevel) / (nextLevel - previousLevel)) * 100
example:
((150 - 100) / (200 - 100)) * 100 = 50
I am currently solving the following JS challenge.
We want to display the progress that a TV show or ad has made.
We have already prepared a pretty cool progressbar to display the
progress. But the length of the progressbar and the duration of the
shows vary. Implement the getProgress(width:int, duration:int,
current:int) function that returns the position of the progressbar in
pixel, px.
As an example, the function call getProgress(500, 90, 16) should
return 89. Always round to the nearest whole number.
The problem here is, that I don't get the expected result 89 when I do the math. Why they expect 89 is a riddle to me...Below my code.
export function getProgress(width, duration, current) {
const currentMin = duration / 100 * current
const pixPerMin = width / duration
const currentPosition = currentMin * pixPerMin
return Math.ceil(currentPosition)
}
With my function I get 80 which should be correct - or at least I think it's correct. I am probably overlooking something or making a obvious mistake but can't see it.
The total duration is 90, and the current position is 16, so the process is around 18% complete:
console.log(16 / 90);
For a bar that's 500 pixels wide, 17.7778% of that is 88.88889 pixels:
const ratioDone = 16 / 90; // 0 to 1
console.log(500 * ratioDone);
That's where 89 comes from.
So you need:
function getProgress(width, duration, current) {
const ratioDone = current / duration;
return Math.round(ratioDone * width);
}
console.log(getProgress(500, 90, 16));
I have a task to make an SVG rotate in IE9.
I found the FakeSmile library which makes it rotate, but after the whole DOM is ready, which is not the behavior I want. I made an attempt to do it manually with JavaScript and ended with this code:
//init an array with values from 0 to 360 for degrees
var degrees = [];
for(var i = 0; i <= 360; i++) {
degress.push(i);
}
// function to rotate it, after it's fetched from the DOM
var rotate = function() {
var deg = degrees.shift();
element.style.msTransform = "rotate(" + deg + "deg)";
degrees.push(deg);
}
setInterval(rotate, 7);
Though it is working, I am worried if any performance hit will occur. Also if there is a better solution. Any suggestions are welcomed.
A creator function and organized objects would be a good start. Remember you shouldn't pollute the global namespace if you can avoid it.
Also de-bounce request and animation. A request every 7 millisecond is two request per frame on a 60fps screen (the most common) and there is no need to calculate and throw away frames the user never sees.
In my example i use requestAnimationFrame because that will synchronize with the screens refreshrate. On every request i check if the handle is already drawing a frame and if it isn't i schedule a frame drawing.
Notice that you can still set JavaScript variables every 7 millisecond. It's just the DOM that's slows.
EDIT 1 - No requestAnimationFrame in IE9
My mistake about requestAnimationFrame, but de-bounce is still a good idea. With de-bounce, several factors can request a change and it will still only render when relevant.
I have replaced requestAnimationFrame with setTimeout(.... 1000/60) for close to 60 fps animation.
function createRotator(element) {
var rotator;
rotator = {
degrees: 0,
element: element,
eventHandle: false,
rotate: function rotate() {
rotator.degrees = (rotator.degrees + 1) % 360;
if (rotator.eventHandle === false)
rotator.eventHandle = setTimeout(function() {
rotator.element.style.transform = "rotate(" + rotator.degrees + "deg)";
rotator.element.style.msTransform = "rotate(" + rotator.degrees + "deg)";
rotator.eventHandle = false;
}, 1000 / 60);
}
};
return rotator;
}
//TEST
var nodes = 0;
var handle;
handle = setInterval(function() {
nodes++;
if (nodes > 10) {
clearInterval(handle);
}
var testNode = document.body.appendChild(document.createElement("p"));
testNode.innerHTML = "Hello dear World!";
testNode.style.width = "115px";
testNode.style.cssFloat = "left";
testNode.style.marginTop = "100px";
var rotator = createRotator(testNode);
setInterval(rotator.rotate, 3);
}, 1000 / 4);
Yeah, with IE9, you're out of luck on CSS animations. My only suggestion would be a memory optimization
//init a variable to store the current angle
let angle = 0;
// function to rotate it
function rotate() {
angle = (++angle)%360;
element.style.msTransform = "rotate(" + angle+ "deg)";
}
setInterval(rotate, 7);
This design change also lets you change the speed of the rotation on the fly without changing the interval length. All you would change is ++angle to angle + w where w is the angular velocity.
What is also unfortunate is that you can't use requestAnimationFrame instead of an interval. Oh well. It's not the end of the world.
EDIT:
It was bugging me that the function was relying so heavily on global variables. So, here is a slightly "better", though heavier, way of doing it.
/** Takes in an element, an angular velocity, and an interval, and makes the element spin in IE9
PARAMS:
element : Element - The element we are spinning
da : Number - The angular velocity in degrees per interval
interval : Number - The number of milliseconds per interval
RETURNS:
Number - The ID of the interval that is created
**/
function makeRotate(element, da, interval){
// Variable to store angle
let a = 0;
// If da isn't provided, make it 1
da = da || 1;
// If interval isn't provided, make it 7
interval = interval || 7;
// Get the ID and make the interval
let id = window.setInterval(() => {
// Increment the angle by the angular velocity, but wrap around 360
a = (a + da)%360;
// Apply the transform to the element
element.style.msTransform = "rotate(" + a + "deg)";
}, interval);
// Return the ID of the interval
return id;
}
const intervalId = makeRotate(element, 1, 7);
Also, I made sure to return the interval id because it is always handy to be able to cancel those suckers! window.clearInterval(intervalId);
I have this progress bar in JS and I'm filling up progress by increasing its width using this code:
function move(ValueSC) {
var elem = document.getElementById("scaleOrig");
var width = 0;
var internalVlue = ValueSC;
var id = setInterval(frame(internalVlue), 50);
function frame(internalVlue) {
if (width >= internalVlue) {
clearInterval(id);
} else {
width += internalVlue;
elem.style.width = width + '%';
}
}
}
This works perfectly but the problem is that I will get a value (result) from my calculator which is something between numbers 15 to 40, so with this stated 15 will be 0% and 40 will 100%. I want the width of progress bar to go up to 100 percent if the result (from my calculator) is 40 and if it is 15 the value of progress bar should be 0%. Basically I need the logic to convert all the 25 numbers from 15 to 40 to have a percentage value between 0 to 100%.
As a broader answer, the formula you're looking for is ((value - min) / (max - min)) * 100. max and min are the bounds of your value and value is the current progress. This will return the percentage of the progress so far for any given value between two other values.
var percent = ((value - 15) / (40 - 15)) * 100
40 will give you 100 and 15 will give you 0
Before you call your method do this:
ValueSC= ((Max-Min)/100)*(Current-Min))
Where:
Current: Your value
Min = Minimum Value
Max = Max Value
First you have to divide up 100 to smaller steps. There can be 25 (40-15) different values for your number, so the length of a step should be 100 / 25, and your starting number is 15. Something like this should work:
function getPercentage(number) {
return (number - 15) * (100 / 25);
}
I'm trying to make a smooth javascript animation that makes a div slide from position left 900 to 550 on the screen..
With two variables; place and speed, I use place to locate where the div is on the screen, and speed to decide the speed.
To make this nice and flowy I've tried to make the speed go slower and slower, so the slide starts fast and goes slower and slower.
I don't know if I'm trying to do the right thing here, but basically, I want the speed to start at 100 %, lets say 50 px, and go a percentages slower for each time.
The speeds percentages should be equal to the number of my place.
So that place starts at 900, and ends at 550.
The speed should start at 50 and go a percentages slower for each time and end at 0 as the place ends at 550...
How do I set this up???
I've tried this:
function doit(place, speed, proc) {
var denne = document.getElementById("screen1");
if (place > 550) {
var speedproc = 100 - (place / 950 * 100); // (the reason that I'm using 950 here is because it should have a percentage to start from that isn't 0)
var newspeed = speed - (speed / 100 * proc);
speed = newspeed;
proc += speedproc;
place -= speed;
denne.style.marginLeft = place + "px";
setTimeout("doit(" + place + ", " + speed + ", " + proc + ")", 20);
}
}
And the body:
<body onload='doit(900, 50, 0);'>
But it acts out crazy... What am I doing wrong?
All in all I would do something like this to create the slow down effect:
newspeed = speed*proc; //Now, proc is a number between 0 and 1.
//the closer proc is to 1, the smaller the decrease will be.
// when proc is 1 - there will be no decrease in speed
For example:
<body onload='doit(900, 50, 0.9);'>