For teaching myself javascript (and for getting/giving more insight in the field of astronomy :) ), I am setting up a page that displays relative positions of sun and moon.
Right now, the speed of the sun and moon movement is still fixed, but I would really like to make this dynamically user-definable via an input field. So, the initial speed is '30', and a user can speed this up or slow this down. Obviously, the ratio between sun and moon must stay fixed. I tried a lot of things (see some relics in the code, but I can't get it to work.
Anyone with more experience with javascript can assist me in doing this? Also, I notice CPU usage gets very high during this animation. Are there simple steps in making this script more efficient?
var dagen = 0;
function speed($this){
var speedSetting = $this.value;
//alert(speedSetting);
//return per;
}
function periode(bolletje, multiplier=30){
if (bolletje == 'zon'){var per = (multiplier*24/2);}
if (bolletje == 'maan'){var per = (multiplier*24*29.5/2);}
return per;
}
function step(delta) {
elem.style.height = 100*delta + '%'
}
function animate(opts) {
var start = new Date
var id = setInterval(function() {
var timePassed = new Date - start
var progress = timePassed / opts.duration
if (progress > 1) progress = 1
var delta = opts.delta(progress)
opts.step(delta)
if (progress == 1) {
clearInterval(id)
}
}, opts.delay || 10)
}
function terugweg(element, delta, duration) {
var to = -300;
var bolletje = element.getAttribute('id');
per = periode(bolletje);
document.getElementById(bolletje).style.background='transparent';
animate({
delay: 0,
duration: duration || per,
//1 sec by default
delta: delta,
step: function(delta) {
element.style.left = ((to*delta)+300) + "px"
}
});
if(bolletje == 'zon'){
dagen ++;
}
bolletje = element;
document.getElementById('dagen').innerHTML = dagen;
//setInterval(function (element) {
setTimeout(function (element) {
move(bolletje, function(p) {return p})
}, per);
}
function move(element, delta, duration) {
var to = 300;
var bolletje = element.getAttribute('id');
per = periode(bolletje);
document.getElementById(bolletje).style.background='yellow';
animate({
delay: 0,
duration: duration || per,
//1 sec by default
delta: delta,
step: function(delta) {
element.style.left = to*delta + "px"
}
});
bolletje = element;
//setInterval(function (element) {
setTimeout(function (element) {
terugweg(bolletje, function(p) {return p})
}, per);
}
hr{clear: both;}
form{display: block;}
form label{width: 300px; float: left; clear: both;}
form input{float: right;}
.aarde{width: 300px; height: 300px; border-radius: 150px; background: url('https://domain.com/img/aarde.png');}
#zon{width: 40px; height: 40px; background: yellow; border: 2px solid yellow; border-radius: 20px; position: relative; margin-left: -20px; top: 120px;}
#maan{width: 30px; height: 30px; background: yellow; border: 2px solid yellow; border-radius: 16px; position: relative; margin-left: -15px; top: 115px;}
<form>
<div onclick="move(this.children[0], function(p) {return p}), move(this.children[1], function(p) {return p})" class="aarde">
<div id="zon"></div>
<div id="maan"></div>
</div>
Dagen: <span id="dagen">0</span>
</form>
<form>
<label><input id="snelheid" type="range" min="10" max="300" value="30" oninput="speed(this)">Snelheid: <span id="snelheidDisplay">30</span></label>
</form>
First, change onlick to oninput in speed input tag.
<input id="snelheid" type="number" value="30" oninput="speed(this)">
And in your speed() function set multiplier = $this.value. multiplier should be global:
var multiplier = 30;
function speed($this){
console.log($this.value);
multiplier = $this.value;
//alert(speedSetting);
//return per;
}
function periode(bolletje){
if (bolletje == 'zon'){var per = (multiplier*24/2);}
if (bolletje == 'maan'){var per = (multiplier*24*29.5/2);}
return per;
}
Here is an example:
https://jsfiddle.net/do4n9L03/2/
Note: multiplier is not speed, it is delay. If you increase it it become slower
Related
I'm looking at setting a 2-3 second delay on a sprite animation. How do I pause the end of each interval and then continue from 0? I used the code below to run only an interval with no delay at the end.
var imgWidth = 48;
var numImgs = 60;
var cont = 0;
var animation = setInterval(function() {
var position = -1 * (cont * imgWidth);
$('#container').find('img').css('margin-left', position);
cont++;
if (cont == numImgs) {
cont = 0;
}
}, 18);
#container {
width: 48px;
height: 48px;
display: block;
overflow: hidden;
}
#container img {
width: 2880px;
height: 48px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="container">
<img src="https://visualcraftsman.com/doh/rdWaitAll.png" alt="My super animation" />
</div>
fiddle
One idea is to use setTimeout() recursively and change the delay depending on the animation's frame number.
// define variables
const $img = $('#container').find('img');
const imgWidth = 48;
const numImgs = 60;
var cont = 0;
var delay = 18;
function advance() {
// increment frame number, loop back to zero
cont = cont == numImgs ? 0 : cont + 1;
// calculate positioning margin
let position = -1 * (cont * imgWidth);
// calculate viewing time for this frame
let delay = cont == 0 ? 1000 : 18;
// position the image
$img.css('margin-left', position);
// schedule next frame advance
setTimeout(advance, delay);
}
// initiate the recursive loop
advance();
#container {
width: 48px;
height: 48px;
display: block;
overflow: hidden;
}
#container img {
width: 2880px;
height: 48px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="container">
<img src="https://visualcraftsman.com/doh/rdWaitAll.png" alt="My super animation" />
</div>
I'm trying to have a number "tick up" when it changes, in a similar fashion to Twitter's like/retweet/comment counters and Discord's reaction counter. I haven't found any information about this on the web (mainly because I don't know what it's formally called).
Here is my code:
The counter element itself:
<span id="counter" class="counter">0</span>
The CSS for the element:
.counter {
display: inline-block;
height: 15px;
font-size: 15px;
overflow-y: hidden;
}
The JavaScript for changing the counter:
function changeCounter(id, num) {
var _x = document.getElementById(id);
_x.innerHTML += _x.innerHTML + "<br>" + num;
_x.style.transition = "0.2s";
setTimeout(function () {
_x.style.marginTop = "-15px";
setTimeout(function () {
_x.innerHTML = num;
_x.style.transition = "0";
_x.style.marginTop = "0";
}, 200);
}, 16);
}
What happens is that it puts the new number next to the original number, goes up, and goes down with just the new number. I want it to have the new number under the old one and just snap to its original position after it stops animating.
function changeCounter(id, num) {
var _x = document.getElementById(id);
_x.innerHTML += _x.innerHTML + "<br>" + num;
_x.style.transition = "0.2s";
setTimeout(function () {
_x.style.marginTop = "-15px";
setTimeout(function () {
_x.innerHTML = num;
_x.style.transition = "0";
_x.style.marginTop = "0";
}, 200);
}, 16);
}
.counter {
display: inline-block;
height: 15px;
font-size: 15px;
overflow-y: hidden;
}
<span id="counter" class="counter">0</span>
<button onclick="changeCounter('counter', Math.floor(Math.random()*100))">Change</button>
I want to add a timer which decrease Automatically (like 10 seconds, 9 seconds... till 0 seconds) but the progress bar will increase. And I am new to javascript, and the below code also copied from another site , so please help me in adding timer inside the progress bar
Till now I did this code
I want to make like this
Demo
<div class="progress"></div>
<style>
.progress-bar {
height: 20px;
background: #1da1f2;
box-shadow: 2px 14px 15px -7px rgba(30, 166, 250, 0.36);
border-radius: 50px;
transition: all 0.5s;
}
.progress {
width: 100%;
display: flex;
flex-flow: column nowrap;
justify-content: center;
align-items: start;
background: #e6e9ff;
border-radius: 20px;
box-shadow: 0px 10px 50px #abb7e9;
}
</style>
<script>
/*
* (class)Progress<nowValue, minValue, maxValue>
*/
//helper function-> return <DOMelement>
function elt(type, prop, ...childrens) {
let elem = document.createElement(type);
if (prop) Object.assign(elem, prop);
for (let child of childrens) {
if (typeof child == "string") elem.appendChild(document.createTextNode(child));
else elem.appendChild(elem);
}
return elem;
}
//Progress class
class Progress {
constructor(now, min, max, options) {
this.dom = elt("div", {
className: "progress-bar"
});
this.min = min;
this.max = max;
this.intervalCode = 0;
this.now = now;
this.syncState();
if(options.parent){
document.querySelector(options.parent).appendChild(this.dom);
}
else document.body.appendChild(this.dom)
}
syncState() {
this.dom.style.width = this.now + "%";
}
startTo(step, time) {
if (this.intervalCode !== 0) return;
this.intervalCode = setInterval(() => {
console.log("sss")
if (this.now + step > this.max) {
this.now = this.max;
this.syncState();
clearInterval(this.interval);
this.intervalCode = 0;
return;
}
this.now += step;
this.syncState()
}, time)
}
end() {
this.now = this.max;
clearInterval(this.intervalCode);
this.intervalCode = 0;
this.syncState();
}
}
let pb = new Progress(15, 0, 100, {parent : ".progress"});
//arg1 -> step length
//arg2 -> time(ms)
pb.startTo(5, 500);
//end to progress after 5s
setTimeout( () => {
pb.end()
}, 10000)
</script>
I think the core problem is that the code you copied is overly complicated especially for beginners. What I would recommend is to start from what you know and build up.
Here is the functionality you want written using only core principles of JavaScript and CSS.
let initialTime = 10; //All time in seconds
let timeLeft = initialTime;
let interval;
let progressBarTextElement = document.getElementById('progress-bar-text');
let progressBarElement = document.getElementById('progress-bar');
function render() {
let progressPercentage = (1 - (timeLeft / initialTime) ) * 100;
progressBarElement.style.width = progressPercentage + '%';
progressBarTextElement.innerHTML = timeLeft + 's';
}
function tick() {
timeLeft = timeLeft - 1;
if(timeLeft <= 0) {
clearInterval(interval); //Stops interval
}
render(); //Updates html
}
function startProgressBar() {
interval = setInterval(tick, 1000); //Will call tick every second
render();
}
startProgressBar();
html {font-family: -apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Oxygen","Ubuntu","Cantarell","Fira Sans","Droid Sans","Helvetica Neue",sans-serif;}
.progress-bar-continer {
height: 80px;
width: 100%;
position: relative;
display: flex;
justify-content: center;
align-items: center;
background-color: #406086;
}
.progress-bar {
background-color: #1b3e80;
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 0%;
transition: width 1s; /* Makes progressBar smooth */
transition-timing-function: linear; /* Try to remove this line for more tick tock feel */
}
.progress-bar-text {
color: white;
font-size: 24px;
font-weight: 700;
position: relative;
z-index: 1;
}
<div class="progress-bar-continer">
<div class="progress-bar" id="progress-bar"></div>
<div class="progress-bar-text" id="progress-bar-text"></div>
<div>
Try to understand the code best you can and you will have no problems adding any features you want on top of it. All the fancy stuff will come later with experience.
I'm trying to learn Java Script Animations and I found really good examples on this site: http://javascript.info/tutorial/animation#maths-the-function-of-progress-delta
But the problem is, as a beginner, I don't understand how the functions and objects work with each other.
Question 01
I copied the example "Let’s create a movement animation on it’s base:" But my version does not work.
<!DOCTYPE HTML>
<html>
<head>
<style>
.example_path{
position: relative;
width: 600px;
height: 100px;
border-style: solid;
border-width: 5px;
}
.example_block{
position: absolute;
width: 100px;
height: 100px;
background-color: yellow;
}
</style>
</head>
<body>
<div onclick="move(this.children[0])" class="example_path">
<div class="example_block"></div>
</div>
<script>
function move(element, delta, duration) {
var to = 500
animate({
delay: 10,
duration: duration || 1000, // 1 sec by default
delta: delta,
step: function(delta) {
element.style.left = to*delta + "px"
}
})
}
</script>
</body>
</html>
output console: ReferenceError: animate is not defined
Does anyone know what the problem is?
Question 02
My second wish is, to integrate the easeInOut function
function makeEaseInOut(delta) {
return function(progress) {
if (progress < .5)
return delta(2*progress) / 2
else
return (2 - delta(2*(1-progress))) / 2
}
}
bounceEaseInOut = makeEaseInOut(bounce)
How can I link both code snippets? The code is also from this page: http://javascript.info/tutorial/animation#maths-the-function-of-progress-delta
Add animate and makeEaseInOut into your script tag then you can use them. You may want to include the functions in a separate JavaScript file eventually.
<script>
function animate(opts) {
var start = new Date
var id = setInterval(function() {
var timePassed = new Date - start
var progress = timePassed / opts.duration
if (progress > 1) progress = 1
var delta = opts.delta(progress)
opts.step(delta)
if (progress == 1) {
clearInterval(id)
}
}, opts.delay || 10)
}
function makeEaseInOut(delta) {
return function(progress) {
if (progress < .5)
return delta(2*progress) / 2
else
return (2 - delta(2*(1-progress))) / 2
}
}
bounceEaseInOut = makeEaseInOut(bounce)
</script>
that's what I tried.
I still have problems.
output console: delta is not a function. bounce is not a function.
I know I have to learn more about creating functions. But right now I'm not that good to solve the problem.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
.example_path{
position: relative;
width: 600px;
height: 100px;
border-style: solid;
border-width: 5px;
}
.example_block{
position: absolute;
width: 100px;
height: 100px;
background-color: yellow;
}
</style>
<script>
function move(element, delta, duration) {
var to = 500;
animate({
delay: 10,
duration: duration || 1000, // 1 sec by default
delta: delta,
step: function(delta) {
element.style.left = to*delta + "px"
}
});
}
function animate(opts) {
var start = new Date;
var id = setInterval(function() {
var timePassed = new Date - start;
var progress = timePassed / opts.duration;
if (progress > 1) progress = 1
var delta = opts.delta(progress);
opts.step(delta);
if (progress == 1) {
clearInterval(id);
}
}, opts.delay || 10);
}
function makeEaseInOut(delta) {
return function(progress) {
if (progress < .5)
return delta(2*progress)/2;
else
return (2 - delta(2*(1-progress)))/2;
};
}
varbounceEaseInOut = makeEaseInOut(bounce);
</script>
</head>
<body>
<div onclick="move(this.children[0], makeEaseInOut(bounce), 3000)" class="example_path">
<div class="example_block"></div>
</div>
</body>
</html>
I've made a very simple animation using javascript, hope it helps, try to "Run code snippet" for better understanding.
/*JavaScript*/
function myMove() {
var elem = document.getElementById("animate");
var pos = 0;
var id = setInterval(frame, 5);
function frame() {
if (pos == 350) {
clearInterval(id);
} else {
pos++;
elem.style.top = pos + 'px';
elem.style.left = pos + 'px';
}
}
}
function Back() {
var elem1 = document.getElementById("animate");
var id1 = setInterval(frame2, 5);
var pos1 = 350;
function frame2() {
if (pos1 == 0) {
clearInterval(id1);
} else {
pos1--;
elem1.style.top = pos1 + 'px';
elem1.style.left = pos1 + 'px';
}
}
}
/*CSS*/
#container {
width: 400px;
height: 400px;
position: relative;
background: yellow;
}
#animate {
width: 50px;
height: 50px;
position: absolute;
background-color: red;
}
/*HTML*/
<button onclick="myMove()">Click Me</button>
<button onclick="Back()"> roll back</button>
<div id ="container">
<div id ="animate"></div>
</div>
I have to have two images start on the left side of the screen and move to the right side and stop. Its like a race and whoever get to the right side first wins. I am generating a random number for this just, the images only move to the right once. I cant figure out why they are only moving once, and dont know how I would get them to stop on the right side.
Here's my code:
var myVar1 = setInterval(fly, 250);
function fly() {
var ranNum = Math.floor(Math.random()*2);
if(ranNum == 0){
document.getElementById("race").style.left = '25px';
} else if (ranNum == 1){
document.getElementById("race1").style.left = "25px";
}
}
body { background-image: url(images/stars.jpg); }
.img1 {
position: absolute;
bottom: 0px;
left: 0px;
}
.img2 {
position: absolute;
bottom: 200px;
left: 0px;
}
.img3 {
position: absolute;
top: 0px;
right: 0px;
}
<img src="images/tie.png" class="img1" id="race" alt="tie"></br>
<img src="images/xwing.png" class="img2" id="race1" alt="xwing">
<img src="images/death.png" class="img3" alt="dstar">
The third image(death.png aka death star), when its clicked it changes color and starts the "race" which I would use the onClick method for that, right? So once either the tie fighter or x-wing reaches the "finish line" on the right side, both images stop so we will have a winner. Also if the x-wing wins, I am going to have the dstar change to a dstar blowing up.
You need to increment the values in order to get it to move.
var leftIncrement = parseInt(document.getElementById("race").style.left) + ranNum + "px";
document.getElementById("race").style.left = leftIncrement;
At the moment you are setting it to "25px" every interval.
Try this:
var els = [document.getElementById("race"), document.getElementById("race1")],
timer = setInterval(fly, 250);
function fly() {
var el = els[Math.floor(Math.random()*2)],
newPos = (parseInt(el.style.left) || 0) + 1;
el.style.left = newPos + 'px';
if(newPos == 25) {
clearInterval(timer);
el.classList.add('winner');
}
}
var els = [document.getElementById("race"), document.getElementById("race1")],
timer = setInterval(fly, 250);
function fly() {
var el = els[Math.floor(Math.random()*2)],
newPos = (parseInt(el.style.left) || 0) + 1;
el.style.left = newPos + 'px';
if(newPos == 25) {
clearInterval(timer);
el.classList.add('winner');
}
}
body { background-image: url(images/stars.jpg); }
.img1 {
position: absolute;
bottom: 0px;
left: 0px;
}
.img2 {
position: absolute;
bottom: 200px;
left: 0px;
}
.img3 {
position: absolute;
top: 0px;
right: 0px;
}
.winner {
outline: 3px solid #0f0;
}
<img src="images/tie.png" class="img1" id="race" alt="tie"></br>
<img src="images/xwing.png" class="img2" id="race1" alt="xwing">
<img src="images/death.png" class="img3" alt="dstar">
It's because regardless of what random numbers you are generating, you are always assigned a left style value of 25px.
var left = document.getElementById("race").style.left;
left = left.replace('px','');
left += parseInt(left) + 25;
document.getElementById("race").style.left = left + "px";
Untested but should work.
This works
var racers = [ document.getElementById("race"), document.getElementById("race1") ];
var interval = setInterval( fly, 250 );
function fly() {
var racer = racers[ Math.floor(Math.random() * racers.length) ];
var newPos = parseInt( racer.style.left || 0 ) + 25;
racer.style.left = newPos + "px";
if( (newPos + racer.clientWidth) >= window.innerWidth){
clearInterval( interval );
alert( "And the winner is " + racer.id );
}
}
Here you got it in a JsFiddle http://jsfiddle.net/5poa7tnt/10/
Ask if you need help to understand
Edit : Looks similar to Oriol's post thought
Edit 2 : The race is most beautiful the a step of "1" instead of "25" and an interval of "5" or "10" instead of 250 .. I don't know what final result you want