Changing Opacity of Div With javascript timer - javascript

Hey guys the solution to this should be simple, but im having difficulty figuring out what's going on.
I have a timerScript.js file that looks like this
//global variables
var timerInterval = null; // the timer that changes opacity every 0.1 seconds.
function StartTimer()
{
//disable the button
document.getElementById('startOpacityTimerButton').disabled=true;
timerInterval = window.setInterval(ChangeOpacity(), 100);
}
function StopTimer()
{
window.clearInterval(timerInterval);
timerInterval = 0;
}
function ChangeOpacity()
{
var object = document.getElementById('opacityZone');
var currentOpacity = (+object.style.opacity);
var newOpacity = currentOpacity + 0.1;
object.style.opacity = newOpacity;
if(newOpacity == 1.0)
{StopTimer();}
}
This is what my code is supposed to do
Click button -> Calls StartTimer
StartTimer -> Disables button, calls ChangeOpacity every 100 milliseconds.
ChangeOpacity -> gets the div element(opacityZone), gets its current opacity,
increments by 0.1 and checks if it is at max opacity in which case it calls StopTimer.
StopTimer -> clears the timer.
This is what it does:
Timer starts, changes opacity to 0.1, and just seems to stop!?!
I tried debugging with safari Web Inspector, but im not too sure what's going on, maybe one of you JavaScript experts can help me out (im a noob at js). Thanks!

Your problem is here:
window.setInterval(ChangeOpacity(), 100);
Instead of passing a reference to the function, you're now executing it inline and scheduling its return value. Change it to:
window.setInterval(ChangeOpacity, 100);
Apart from that, you should really use CSS transitions for stuff like this.

Thanks guys, i'll take a look at the suggestions. Was just trying to do it with JavaScript for the purpose of learning the language, here are the JavaScript functions i came up with to solve the problem.
//global variables
var opacityIncreasing; //boolean to know if opacity is increasing or decreasing.
var animationInterval;//time in millseconds to do animation.
var timerInterval;//the timer that changes opacity depending on interval.
var object;//object we are doing the animation on.
var currentOpacity;//currentOpacity of object.
//var buttonMessage;//message to make object appear or dissapear depending on animation.
function init(elementName,rateOfAnimation)
{
var object = document.getElementById(elementName);
animationInterval = rateOfAnimation;
currentOpacity = Truncate((+object.style.opacity),1);
document.getElementById('messageContainer').innerHTML=currentOpacity;
if (currentOpacity==0)
{
opacityIncreasing = true;
}
else
{
opacityIncreasing = false;
}
StartTimer();
}
function StartTimer()
{
//disable the button
document.getElementById('startOpacityTimerButton').disabled=true;
timerInterval = window.setInterval(ChangeOpacity, animationInterval);
}
function StopTimer()
{
window.clearInterval(timerInterval);
timerInterval = 0;
//enable Button
document.getElementById('startOpacityTimerButton').disabled=false;
}
function Truncate (number, digits)
{
var multiplier = Math.pow(10, digits),
adjustedNum = number * multiplier,
truncatedNum = Math[adjustedNum < 0 ? 'ceil' : 'floor'](adjustedNum);
return truncatedNum / multiplier;
}
function ChangeOpacity()
{
var object = document.getElementById('opacityZone');
var stringOpValue = "";
if(opacityIncreasing)
{
currentOpacity += 1/10;
stringOpValue = String(currentOpacity.toFixed(1));
object.setAttribute("style","opacity:"+currentOpacity+"; -moz-opacity:"+currentOpacity+";");// filter:alpha(opacity="++")");
document.getElementById('messageContainer').innerHTML= stringOpValue;
if(currentOpacity.toFixed(1) == 1.0)
{
document.getElementById('startOpacityTimerButton').value = "Disappear";
StopTimer();
}
}
else
{
currentOpacity -= 1/10;
stringOpValue = String(currentOpacity.toFixed(1));
object.setAttribute("style","opacity:"+currentOpacity+"; -moz-opacity:"+currentOpacity+";");// filter:alpha(opacity="++")");
document.getElementById('messageContainer').innerHTML= stringOpValue;
if(currentOpacity.toFixed(1) == 0.0)
{
document.getElementById('startOpacityTimerButton').value = "Appear";
StopTimer();
}
}
}
This is the HTML and CSS
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="X-UA-Compatible" content="chrome=1">
<meta charset="utf-8">
<title>Opacity Test</title>
<style>
body
{
text-align: center;
}
#opacityZone
{
width: 350px;
height: 25px;
background-color: #F50;
text-align: center;
margin:0 auto;
margin-top: 10px;
margin-bottom: 10px;
padding-top: 5px;
/*opacity number between 0.0 and 1.0*/
opacity: 0.0;
}
#messageContainer
{
width: 100px;
min-height: 100px;
background-color:red;
color: white;
font-weight: bolder;
font-size: 72px;
text-align: center;
margin:0 auto;
padding-top: 10px;
}
.roundedContainer
{
-webkit-border-radius: 15px;
-moz-border-radius: 15px;
border-radius: 15px,15px,15px,15px;
}
</style>
</head>
<body>
<h2>Opacity Test</h2>
<form>
<input type="button" id="startOpacityTimerButton" value="Appear" onclick="init('opacityZone',50);" />
</form>
<div id="opacityZone">Do you see me?</div>
<p id="messageContainer" class="roundedContainer"></p>
</body>
</html>

pass a function reference to window.setInterval. so pass ChangeOpacity and not ChangeOpacity()
timerInterval = window.setInterval(ChangeOpacity, 100);

Have you considered using CSS3 transition effects instead of making it using JavaScript? Performance wise it should be much better:
For example:
-webkit-transition: opacity 1s ease-in-out;
-moz-transition: opacity 1s ease-in-out;
-o-transition: opacity 1s ease-in-out;
transition: opacity 1s ease-in-out;

What everyone else above has been saying I completely agree with.
Just use CSS3 Animations to change the opacity of the button.
Simply use something along these lines:
#keyframes opacityChange{
from {opacity: 0.1}
to {opacity: 1}
}
You can also declare the timeframe in which the change would take place.
And add a class via javascript/jquery to your button.
(class = "opacityChange")
And when clicking on a new button be sure to remove that class, so that it can be reimplemented to the button later on.
However, to fix your particular problem.
(If for some reason you can't use css3)
Simply add this to the Change Opacity function:
if(newOpacity == 1.0){
StopTimer();
}else{
ChangeOpacity();
}
Looking at how you have it set up, that should work, unless i'm looking over something.

I had same problem and after so many time searching this is my solution:
instead of this line
var currentOpacity = (+object.style.opacity);
var newOpacity = currentOpacity + 0.1;
you have to use this line:
let newOpacity = String(parseInt(window.getComputedStyle(Object).getPropertyValue('opacity'))+0.2))
for alternative answer you can do this (if you have a white background :) ):
let i =0 ;
let interval = setInterval(()=>{
i+=0.1
Object.style.color = `rgba(0,0,0,${i})`;
},1000)
if(Object.style.color === 'rgba(0,0,0,1)')
clearInterval(interval)
console.log()

Related

Fading in iframe using JavaScript

I am trying to fade in my iframe smoothly with JavaScript. I have seen links online and most of the answers were related to jQuery. Why must I use jQuery for a simple fading? Can anyone explain?
Also I do not mind using JQuery, after all its a part of javascript. I am just looking for a simple solution. Since I am using the below fadeIn() function, does jQuery perform better than the below function?
<iframe id="iframe" style="
position: fixed;
opacity: 0;
height: 100%;
width: 100%;
overflow: hidden;
z-index: 10;
display: none;
">
</iframe>
<script>
//Creation of button before this code
button_01.onPointerUpObservable.add(function () {
let iframe = document.getElementById("iframe");
iframe.src = "LINK";
fadeIn(iframe, 2000);
iframe.style.display = 'Block';
});
function fadeIn(el, duration) {
/*
* #param el - The element to be faded out.
* #param duration - Animation duration in milliseconds.
*/
var step = 10 / duration;
var opacity = 0;
function next() {
if (opacity >= 1) { return; }
el.style.opacity = (opacity += step);
setTimeout(next, 10);
}
next();
}
</script>
Some people suggest jQuery because there's a function called x.fadeIn(300) which causes it to animate pretty nicely, you don't actually need this and it can be accomplished by adding a class or using vanilla animations and removing it later
Both:
Option 1: Class
.shown {
opacity: 1;
}
iframe {
transition: .3s opacity;
}
iframe.classList.add('shown');
Option 2: JS only
iframe.style.transition = '.3s';
iframe.style.opacity = '1';
// remove it after it's done
setTimeout(() => {
iframe.style.transition = '';
}, 300);

Opacity transition css animation works from higher to lower but not vice versa

I have what should be a very simple problem, perhaps a typo that I just can't see.I have a page blocker that grays the page out with an opacity transition and blocks any clicks when I launch a form. This works in hideForm, but does not in showForm and it immediately become 0.4 opacity. Weird because all they are doing is the opposite of each other with a timeout in hideForm to set's it to display to none when opacity transition is finished.
I think this will end being a simple solution and I'll end up being asked to delete the question, which I will gladly do, but I've been trying to solve this for too long and I need a second pair of eyes.
I tried to minimize the amount of code shown but will post more if asked.
#pageCover {
opacity: 0;
-moz-transition: opacity 1s;
-webkit-transition: opacity 1s;
transition: opacity 1s;
display:none
}
This works great
function hideForm() {
if (form.style.top > '0px') {
pageCover.style.opacity = 0.0;
setTimeout(function () { pageCover.style.display = 'none'; }, 1000);
}
}
This displays the pageCover but ignores the transition and goes right to 0.4 . How can the transition work one way, but not the opposite way? I'm stumped.
function showForm() {
if (form.style.top < '0px') {
pageCover.style.opacity = 0.4;
pageCover.style.display = 'block';
}
}
The opacity attribute animates but not the "display" attribute. When they are set at the same time, the opacity transition will not be observed. You can play with the numbers but for illustration's sake, let's add a 10ms gap between the two operations:
pageCover.style.display = 'block';
setTimeout(function(){
pageCover.style.opacity = 0.4;
},10);
You can push the number to even 0, but the browser might "optimize" it away.
Live Example:
const pageCover = document.getElementById("pageCover");
function hideForm() {
pageCover.style.opacity = 0;
setTimeout(function () {
pageCover.style.display = 'none';
}, 1000);
}
function showForm() {
pageCover.style.display = 'block';
setTimeout(function () {
pageCover.style.opacity = 0.4;
}, 10);
}
document.getElementById("btn-show").addEventListener("click", showForm);
document.getElementById("btn-hide").addEventListener("click", hideForm);
#pageCover {
opacity: 0;
-moz-transition: opacity 1s;
-webkit-transition: opacity 1s;
transition: opacity 1s;
display: none;
}
<div id="pageCover">
This is the page cover
</div>
<input type="button" id="btn-show" value="Show">
<input type="button" id="btn-hide" value="Hide">

Proper way to sync setInterval and transition

Problem statement
To move the square along the perimeter of the viewport on click of the button as can be seen in the example:
https://codepen.io/vineetrok/pen/XRowdB
What do I need?
I'm using this code in combination with the transition property in the CSS. I think combination of transition and setInterval() is causing a delay. Is there a better and efficient method to accomplish this only using javascript?
Following is my code:
HTML
<div class="box" style="left:0;top:0"></div>
<button type="button" name="button" onclick="init()">Start!</button>
CSS
.box{
transition: all 1s linear;
}
JS
var elem = document.querySelector(".box");
var viewportWidth = window.innerWidth;
var viewportHeight = window.innerHeight;
var dimension = elem.clientWidth;
var deltaX = viewportWidth - dimension;
var deltaY = viewportHeight - dimension;
function move(x,y){
if(x <=0 && y==0){
elem.style.left=(deltaX)+"px";
}
else if(x==(deltaX) && y==0){
elem.style.top=(deltaY)+"px";
}
else if(x==(deltaX) && y==(deltaY)){
elem.style.left="0px";
}
else if(x==0 && y==(deltaY)){
elem.style.top="0px";
}
}
function getCoordinates(elem){
return {
x: elem.getBoundingClientRect().left,
y: elem.getBoundingClientRect().top
}
}
var init = function(){
var clearTimer = 1;
var startTimer = setInterval(function(){
move(getCoordinates(elem).x,getCoordinates(elem).y )
}, 1000);
clearTimer++;
if(clearTimer>=4){
clearInterval(startTimer);
}
}
I would generally say that using both css and javascript to manage a transition is going to cause trouble. Part of the problem is that javascript timers aren't very precise. If you set a timer for 1 second it doesn't actually sleep for exactly one second. The exact amount of time it sleeps can vary depending on how busy the CPU is, what the user is doing, etc. It is very easy for the javascript timer to take longer than the CSS animation.
Since you are using jQuery I would use the jQuery.animate function to run things. It has a callback function that is invoked when the animation completes, and you can use that to execute the next step of the animation without any timers at all. That will make sure there aren't any delays. It should also be fairly performant. CSS animations are usually the slowest in terms of computer performance, so I expect jQuery.anmiate to probably be a bit better. There are other libraries out there designed for high performance animations, but unless performance actually becomes a problem, I wouldn't worry about it. Right now your issue is likely the imprecise timing of the timeout method, and not any performance issues.
Here's my go at it (I developed something from scratch instead of reusing your code) :
let box=document.getElementById("box"),
isLeft = false,
isTop = false
const toggleLeft = () => {
box.style.left = (isLeft=!isLeft) ? "calc( 100% - 50px )" : "0";
setTimeout(toggleTop, 2000);
}
const toggleTop = () => {
box.style.top = (isTop=!isTop) ? "calc( 100% - 50px )" : "0";
setTimeout(toggleLeft, 2000);
}
setTimeout(toggleLeft, 1000)
#box {
width: 50px;
height: 50px;
background: #00f;
position: absolute;
top: 0;
left: 0;
-webkit-transition: all 2s ease-in-out;
transition: all 2s ease-in-out;
}
<div id="box"></div>
And a more condensed and recursive version :
let box=document.getElementById("box"),
is = { left : false, top : false }
const toggle = what => {
box.style[what] = (is[what]=!is[what]) ? "calc( 100% - 50px )" : "0";
setTimeout(()=>toggle(what==="left"?"top":"left"), 2000);
}
setTimeout(()=>toggle("left"), 100)
#box {
width: 50px;
height: 50px;
background: #00f;
position: absolute;
top: 0;
left: 0;
-webkit-transition: all 2s ease-in-out;
transition: all 2s ease-in-out;
}
<div id="box"></div>

Why do we need to clear the animation when running it multiple times in JavaScript?

Yesterday I asked a question (original question) that was promptly answered, but even though a solution was found, I don't understand why this is working the way it is. I can duplicate this solution for other things I need to do but before I continue on I would like to understand why this works the way it does.
So basically I made three functions that called each other. The 1st called the second upon "animationend" and the second called the third upon an "animationend" and the finally the third function called the first to start the cycle all over again - BUT My original code though lacked;
document.getElementById("rightBoxTwo").style.animation = "none";
which was needed in-order for the third function to call the first so the cycle starts all over again. Without the above code in each function the three functions would work only once and then stop. The answer that StackOverFlow user; ScientiaEtVeritas gave me included a CodePen which had a working example of what I needed and a brief explanation
So, I think you have several options: What could work is that you
reset the the animation of rightBox in function runTwo with
animation: none. If you assign scrollTextTwo 10s back to the
rightBox it should start again. Equivalent for the other ones.
So finally my question is WHY does the animation need to be cleared, and why does the .style.animation = "none"; accomplish this?
below is the working code after a solution was presented...
<body onload="runOne()">
function runOne() {
var x = document.getElementById("rightBox");
x.addEventListener("animationend",runTwo);
document.getElementById("rightBox").style.animation = "scrollTextTwo 10s";
document.getElementById("rightBoxTwo").style.animation = "none";
}
function runTwo() {
var x = document.getElementById("rightBoxTwo");
x.addEventListener("animationend",runThree);
document.getElementById("rightBoxTwo").style.animation =
"scrollTextTwo 10s";
document.getElementById("rightBoxThree").style.animation = "none";
}
function runThree() {
var x = document.getElementById("rightBoxThree");
x.addEventListener("animationend",runOne);
document.getElementById("rightBoxThree").style.animation =
"scrollTextTwo 10s";
document.getElementById("rightBox").style.animation = "none";
}
The simplest reason is because setting the animation to the same thing twice (or more times) in a synchronous manner like a for loop is the same as doing it once:
let box = document.getElementById('box');
// animation happens once
for (let i = 0; i < 5; i++) {
box.style.animation = 'fade .5s';
}
#keyframes fade {
from {
opacity: 1
}
to {
opacity: 0
}
}
#box {
height: 200px;
width: 200px;
margin: 50px;
background: #018bbc;
}
<div id="box"></div>
The behavior is the same even if you delay the animation so each time it runs after a possible render:
let box = document.getElementById('box');
// animation still happens once
for (let i = 0; i < 5; i++) {
setTimeout(function() {
box.style.animation = 'fade .5s';
}, i * 1000);
}
#keyframes fade {
from {
opacity: 1
}
to {
opacity: 0
}
}
#box {
height: 200px;
width: 200px;
margin: 50px;
background: #018bbc;
}
<div id="box"></div>
But if I reset the animation before each step, the engine has to re-set the animation, which in a way means to "install the animation again", meaning it will be animated again:
let box = document.getElementById('box');
box.addEventListener('animationend', function() {
box.style.animation = 'none';
});
// animation now happens every time
for (let i = 0; i < 5; i++) {
setTimeout(function() {
box.style.animation = 'fade .5s';
}, i * 1000);
}
#keyframes fade {
from {
opacity: 1
}
to {
opacity: 0
}
}
#box {
height: 200px;
width: 200px;
margin: 50px;
background: #018bbc;
}
<div id="box"></div>
You don't really need javascript for something like this. Keyframes let you define styles by percent complete. Using that you can time 2 animations for a similar result:
#keyframes progress {
0% { width: 0px;}
50% { width: 600px;}
100% {width: 600px;}
}
#keyframes progress2 {
0% { width: 600px;}
49% { width:600px;}
50% { width: 0px;}
100% {width: 600px;}
}
div {
width:600px;
height:50px;
background-color:black;
}
#rightBox {
animation: progress 4s infinite;
}
#rightBoxTwo {
animation: progress2 4s infinite;
}
<div id="rightBox"></div>
<div id="rightBoxTwo"></div>

Running functions in intervals

I want to animate a set of elements (.col-n) in intervals. Let's say I have four .col elements (.col-1 etc) and I want to animate each of them class, but not after one is complete, but earlier.
I tried doing some for with setInterval, but it's not working (it's just looping):
container.children('.col').last().on('click', function() {
for(var i = 1; i <= columns; i++) {
var int = self.setInterval(function() {
console.log('a')
},500*i)
}
});
I tried each from jQuery with this and it's not this also. Can someone point me to right direction?
fiddle: http://jsfiddle.net/s5DMe/
Please note that I don't want to run functions one after another, but run one, wait (no matter if the first one has finished or not), run another one.
Okay, made it by myself ;) It was all the matter of using the interval and then creating a pseudo-loop inside:
container.children('.col').last().on('click', function() {
var i = 0;
setInterval(function() {
if(i != 4) {
i++;
$('.col-'+i).addClass('act')
}
},500)
});
Thanks anyway :)
.col-1 { background: #ccffff; transition-delay: 200ms }
.col-2 { background: #b3ffff; transition-delay: 400ms }
.col-3 { background: #99ffff; transition-delay: 600ms }
.col-4 { background: #80ffff; transition-delay: 800ms }
You can add your animating class to all of the .col-*'s at the same time. The transition-delay handles the faux sequencing.
Of course, you'll need to add the browser-prefixed versions of the property as well.

Categories

Resources