I am rotating an element when I click on a button using setInterval along with clearInterval to stop rotation when a certain value is reached by clearing the interval time t.It works well but when I click on the same button over and over again before the current animation completes , the animation no longer stop rotating.clearInterval does not work in this case.
The code below illustrates it :
var t; // interval
var x = 0 ; // counter
var cal ; // element to be rotate
function prepareRotatesX(){
x=x+5;
cal =document.getElementById("myCalendar");
cal.style.transform="rotateX("+x+"deg)";
if(x>360){ clearInterval(t); x=0;}
}
function rotate(){
t = setInterval(function(){
prepareRotatesX();
}, 10);
}
#myCalendar{
position: absolute;
top:45px;
left:20px;
border: 2px solid blue;
width: 210px;
height:170px;
background-color:blue;
}
<div id="myCalendar">
<!---HERE COME CALENDAR -->
</div>
<!-- make rotation-->
<button id='bnext' onclick='rotate();'>Rotation</button>
So how to stop rotation in this case ?
The option you are probably after is if it is still running than do not run it again. So see if t is defined, if it is exit. And when the loop is done, than you can reset the interval.
var t; // interval
var x = 0; // counter
var cal; // element to be rotate
function prepareRotatesX() {
x = x + 5;
cal = document.getElementById("myCalendar");
cal.style.transform = "rotateX(" + x + "deg)";
if (x > 360) {
clearInterval(t);
x = 0;
t = null
}
}
function rotate() {
if (t) return; // if t is defined, than it is running so exit
t = setInterval(function() {
prepareRotatesX();
}, 10);
}
#myCalendar {
position: absolute;
top: 45px;
left: 20px;
border: 2px solid blue;
width: 210px;
height: 170px;
background-color: blue;
}
<div id="myCalendar">
<!---HERE COME CALENDAR -->
</div>
<!-- make rotation-->
<button id='bnext' onclick='rotate();'>Rotation</button>
other option is to just clear the interval and restart it
function rotate() {
if (t) clearInterval(t)
t = setInterval(function() {
prepareRotatesX();
}, 10);
}
Variable t declared globally is creating problem, if you move t to local scope in your code. It should fix your problem (Assuming that previous clicks rotation should continue)
In case if you want to abort the previous click, you can follow solution given by #epascarello
var x = 0 ; // counter
var cal ; // element to be rotate
function prepareRotatesX(t){
x=x+5;
cal =document.getElementById("myCalendar");
cal.style.transform="rotateX("+x+"deg)";
if(x>360){ clearInterval(t); x=0;}
}
function rotate(){
var t; // interval
t = setInterval(function(){
prepareRotatesX(t);
}, 10);
}
#myCalendar{
position: absolute;
top:45px;
left:20px;
border: 2px solid blue;
width: 210px;
height:170px;
background-color:blue;
}
<div id="myCalendar">
<!---HERE COME CALENDAR -->
</div>
<!-- make rotation-->
<button id='bnext' onclick='rotate();'>Rotation</button>
In your rotate method, which is called on click you need to make a change to see if the interval is defined and clear it if it is. See below code update.
var t; // interval
var x = 0 ; // counter
var cal ; // element to be rotate
function prepareRotatesX(){
x=x+5;
cal =document.getElementById("myCalendar");
cal.style.transform="rotateX("+x+"deg)";
if(x>360){ clearInterval(t); x=0;}
}
function rotate(){
if(t !== undefined) {
clearInterval(t);
}
t = setInterval(function(){
prepareRotatesX();
}, 10);
}
#myCalendar{
position: absolute;
top:45px;
left:20px;
border: 2px solid blue;
width: 210px;
height:170px;
background-color:blue;
}
<div id="myCalendar">
<!---HERE COME CALENDAR -->
</div>
<!-- make rotation-->
<button id='bnext' onclick='rotate();'>Rotation</button>
Related
I have a variable count that triggers a function positiveBar if the value of count is > 0. If the value of count is < 0, it triggers a function negativeBar.
positiveBar changes a div's position using
progressBar.style.left = '50%';
negativeBar changes that same div's position using
progressBar.style.right = '50%';
This gives me the result I want; however, if at any point count becomes greater than 0, the positioning on negativeBar stops working, and it uses the positioning of the positiveBar function instead.
Video to explain:
var count = 0;
// Show count on the page
document.getElementById("countDisplay").innerHTML = count;
// Update count
function updateDisplay() {
countDisplay.innerHTML = count;
};
// Change negative count to an absolute
function absCount() {
return Math.abs(count);
};
function positiveBar() {
progressBar.style.backgroundColor = "#77eb90";
progressBar.style.width = (count * 10) + 'px';
progressBar.style.left = '50%';
};
function negativeBar() {
progressBar.style.backgroundColor = "#ef5c3f";
progressBar.style.width = (absCount() * 10) + 'px';
progressBar.style.right = '50%';
};
// Count up and down when + and - buttons are clicked and edit bar
add1.addEventListener("click", () => {
count++;
updateDisplay();
if (count > 0) {
positiveBar();
} else {
negativeBar();
}
});
subtract1.addEventListener("click", () => {
count--;
updateDisplay();
if (count > 0) {
positiveBar();
} else {
negativeBar();
}
});
.progressBar__Container {
height: 10px;
margin: 20px auto;
border: 1px solid black;
position: relative;
}
#progressBar {
height: 10px;
width: 0;
position: absolute;
}
<div id="countDisplay"></div>
<button id="add1">+</button>
<button id="subtract1">-</button>
<div class="progressBar__Container">
<div id="progressBar"> </div>
</div>
I tried reordering statements. I also tried creating a condition for if count = 0, but that didn't change the result. I'm very confused because it initially works how I intend, but if count becomes greater than 0 at any point, progressBar.style.right = '50%'; stops being applied.
You aren't clearing any previously set left or right styles when you switch from negative to positive and vice versa.
I would use CSS classes to control the position and colour as it's easier to toggle them based on the state of count.
let count = 0;
const countDisplay = document.getElementById("countDisplay");
const progressBar = document.getElementById("progressBar");
// Update count
function updateDisplay() {
countDisplay.textContent = count;
progressBar.style.width = `${absCount() * 10}px`;
progressBar.classList.toggle("positive", count > 0);
progressBar.classList.toggle("negative", count < 0);
};
// Change negative count to an absolute
function absCount() {
return Math.abs(count);
};
// Count up and down when + and - buttons are clicked and edit bar
add1.addEventListener("click", () => {
count++;
updateDisplay();
});
subtract1.addEventListener("click", () => {
count--;
updateDisplay();
});
.progressBar__Container {
height: 10px;
margin: 20px auto;
border: 1px solid black;
position: relative;
}
#progressBar {
height: 10px;
width: 0;
position: absolute;
}
#progressBar.positive {
background-color: #77eb90;
left: 50%;
}
#progressBar.negative {
background-color: #ef5c3f;
right: 50%;
}
<div id="countDisplay">0</div>
<button id="add1">+</button>
<button id="subtract1">-</button>
<div class="progressBar__Container">
<div id="progressBar"> </div>
</div>
See MDN:
When both left and right are defined, if not prevented from doing so by other properties, the element will stretch to satisfy both. If the element cannot stretch to satisfy both — for example, if a width is declared — the position of the element is over-constrained. When this is the case, the left value has precedence when the container is left-to-right; the right value has precedence when the container is right-to-left.
Because you are setting style.left when you then come to set style.right the above applies - i.e. the style.right setting will get overridden.
I'm trying to visualize a countdown via a div's width. This could be used for something like a banner system showing when the next banner will slide in, or for a notification system showing how long the notification will be visible.
So in my example below, I have the .outer div emptied after 5 seconds, but the .timer div's width is not reaching width: 0%; at the same time as the setTimeout() kicks in.
The variable len would represent how long the banner or notification would be shown for.
The calculation in the variable offset is what is throwing me off (I think), I cannot seem to get the calculation correct. I would like it to be dynamic, meaning, no matter what len is and what the width of the outer/parent div is, it will always take len time to reach width: 0%;.
I hope my explanation makes sense. Any help would be greatly appreciated.
const len = 5000;
let outer = document.querySelector('.outer');
let timer = document.querySelector('.timer');
let timerWidth = timer.offsetWidth;
let offset = len / timerWidth;
let init = 100;
let interval = setInterval(() => {
init = init - offset;
timer.style.width = init + '%';
}, 1000);
setTimeout(() => {
outer.innerHTML = '';
clearInterval(interval);
}, len);
* {
box-sizing:border-box;
}
body {
padding: 100px 10px 10px;
}
.outer {
width: 100%;
border: 1px solid slategray;
padding: 10px;
}
.timer {
width: 100%;
height: 10px;
background: red;
transition: all 1000ms linear;
}
<div class="outer">
<div class="timer"></div>
<p>Some Message Here!</p>
</div>
Two problems with the code:
interval doesn't start as soon as the page is loaded so the CSS is late in transition.
offset was wrong indeed.
Here's how I fixed it:
let toElapse = 3000; // modify as you like
let tick = 1000; //if you modify this don't forget to replicate
//in CSS transition prop
let countDownEl = document.querySelector('#countdown');
let outer = document.querySelector('.outer');
let timer = document.querySelector('.timer');
let init = 100;
// we calculate the offset based on the tick and time to elapse
let offset = init / (toElapse/tick);
countDownEl.innerHTML = init;
setTimeout(()=>{
// we need to start the first CSS transition ASAP so it is not late.
init = init - offset;
timer.style.width = init.toFixed(2) + '%';
},0)
let interval = setInterval(() => {
// the same interval.
countDownEl.innerHTML = init;
init = init - offset;
timer.style.width = init.toFixed(2) + '%';
}, tick);
setTimeout(() => {
// the same clearance timeout
outer.innerHTML = '';
clearInterval(interval);
}, toElapse);
* {
box-sizing:border-box;
}
body {
padding: 100px 10px 10px;
}
.outer {
width: 100%;
border: 1px solid slategray;
padding: 10px;
}
.timer {
width: 100%;
height: 10px;
background: red;
transition: width 1s linear;
}
<div class="outer">
<div class="timer"></div><span id="countdown"></span>
<p>Some Message Here!</p>
</div>
If you use percentage in width you don't need to know the width of your box.
you just need to substract and add on offset to timeout.
const len = 5000;
let outer = document.querySelector('.outer');
let timer = document.querySelector('.timer');
let timerWidth = timer.offsetWidth;
let offset = 100 * 1000 / 5000;
let interval = setInterval(() => {
init = init - 20;
timer.style.width = init + '%';
}, 1000);
setTimeout(() => {
outer.innerHTML = '';
clearInterval(interval);
}, len + 1000);
I'm working in this long press time counter
PROBLEM
It works well, but now instead of displaying time differences (end - start), I want it to display count numbers like working with count++ (1,2,3,4....)
I tried using delta = end.getSeconds() - start.getSeconds() ; or delta = end.getMilliseconds() - start.getMilliseconds();but sometimes it shows negative numbers https://jsfiddle.net/7h65ufLq/18/
(function(window, document, undefined){
'use strict';
var start;
var end;
var delta;
var button = document.getElementById('myCanvas');
function getDate(){
start = new Date();
}
function retrieveDate() {
end = new Date();
delta = end - start;
document.getElementById("demo").innerHTML = delta;
}
button.addEventListener("mousedown", getDate);
button.addEventListener("mouseup",retrieveDate );
button.addEventListener("touchstart", getDate);
button.addEventListener("touchend",retrieveDate );
document.addEventListener('contextmenu', event => event.preventDefault())
})(window, document)
#myCanvas{
height:100px;
width:100px;
border:1px solid red;
-webkit-user-select: auto;
-webkit-touch-callout: inherit;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
<canvas id="myCanvas">click</canvas>
<span id="demo">0</span>
Rather than display a delta time, you could just start an interval that increases the count at specific times. Then on mouse up, clear the interval.
For example:
(function(window, document, undefined) {
'use strict';
var button = document.getElementById('myCanvas');
var interval;
var count;
function startCount() {
count = 0
document.getElementById("demo").innerHTML = count
interval = setInterval(function() {
count++;
document.getElementById("demo").innerHTML = count
}, 1000) // increase count every second
}
function endCount() {
clearInterval(interval)
}
button.addEventListener("mousedown", startCount);
button.addEventListener("mouseup", endCount);
button.addEventListener("touchstart", startCount);
button.addEventListener("touchend", endCount);
document.addEventListener('contextmenu', event => event.preventDefault())
})(window, document)
#myCanvas {
height: 100px;
width: 100px;
border: 1px solid red;
-webkit-user-select: auto;
-webkit-touch-callout: inherit;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
<canvas id="myCanvas">click</canvas>
<span id="demo">0</span>
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
So, I'm trying to make an animation in JavaScript (I want a navigation bar to pull down when I click it). The problem is that every time I click this navigation bar, it only moves down one pixel. How do I make it to where I can make the "Move" function repeat over and over so that it realizes the navigation bar is below "0", and move it up? Here's the code I have atm:
var i = -43 //original position of div
function Move(x)
{
if (i < 0)
{
i++;
x.style.top = i + "px";
}
}
function setPosition(x)
{
setInterval(Move(x), 500);
}
P.S. I have the "div onclick" equal to "setInterval(this)"
I would use a setTimeout() (so you don't have to worry about canceling the setInterval()), and note the use of an anonymous function (function(){}) for the first argument of the setTimeout() call:
#slider {
position: absolute;
top: -43px;
left: 200px;
width: 200px;
height: 50px;
border: 1px solid blue;
background: #dff;
}
<div id='slider'>This is a slidout</div>
<div id="clicker">Click me!</div>
var slider = document.getElementById('slider'),
clicker = document.getElementById('clicker');
clicker.onclick = function(){
Move(slider, -43);
};
function Move(x, i)
{
if (i < 0)
{
i++;
x.style.top = i + "px";
setTimeout(function(){
Move(x, i);
}, 50);
}
}
http://jsfiddle.net/h2C3A/