1

I've been trying to create a fadeIn & fadeOut animation using VanillaJS in my project but I literally don't understand what's the problem. I'm using SCSS. I made it simple for you.

I tried visibility but it didn't work too. like it appears e.g. for 200ms but then immediately disappears. In another way of explanation, it appears whenever I click on it (stable) and then goes away after 200ms (unstable).

const fade = () => {
  const box = document.querySelector('#box');
  
  box.classList.toggle('fade');
};

document.querySelector('#fadebtn').addEventListener('click', fade);
#box {
  width: 70px;
  height: 50px;
  background: #FD7A6B;
  display: none;
  opacity: 0;
  -webkit-transition: 200ms ease-in-out;
  -moz-transition: 200ms ease-in-out;
  -o-transition: 200ms ease-in-out;
  transition: 200ms ease-in-out;
}

#box.fade {
  display: block !important;
  opacity: 1 !important;
}

// I also tried this, wondered it may work, but didn't.

// .fade {
  // display: block !important;
  // opacity: 1 !important;
// }
<button type="button" id="fadebtn">Fade</button>
<div id="box"></div>
E_net4
  • 27,810
  • 13
  • 101
  • 139
Amini
  • 1,620
  • 1
  • 8
  • 26
  • 1
    `display` cannot do smooth CSS transitions. See https://stackoverflow.com/questions/3331353/transitions-on-the-css-display-property – StardustGogeta Jul 13 '21 at 15:17
  • I tried ``visibility`` but as I explained in the new (edited) question, it disappears immediatly. – Amini Jul 13 '21 at 15:40
  • 1
    Would it be enough to change only the `opacity`? If not (because it still takes up space when opacity is 0), then how exactly do you want it to shrink? – StardustGogeta Jul 13 '21 at 15:44
  • I know what do you mean. It's exactly defining ``position: absolute;`` & ``visibility: hidden``. As you know it doesn't occupy any spaces because of the ``position absolute``. My element is also ``absolute`` so feel free to add whatever you want between the three of them. Summary of the comment : it doens't matter using ``display: none;`` or ``visibility: hidden;`` or ``opacity: 0;`` cause it doesn't occupy the space. – Amini Jul 13 '21 at 15:49
  • So I tried this ``.foo { transition: 200ms ease-in-out; opacity: 1; } .fade { opacity: 0; }`` ``const fade = () => { const elm = document.querySelector('#foo'); elm.classList.toggle('fade'); }; document.getElementById('bar').addEventListener.('click', fade);`` It worked well. but you know what. when I refresh the page as you know the element is visible. I only want it to be visible whenever I click on it. – Amini Jul 13 '21 at 16:11
  • 1
    In that case, can't you add `class="fade"` to the HTML tag so that it starts with `opacity: 0`? You can probably remove `opacity: 1` from `.foo` as well. (It's also unclear to me whether `foo` is a class name or ID in your code fragment there. It would probably be better to add an edited snippet to the question.) – StardustGogeta Jul 13 '21 at 16:14

1 Answers1

3

I wrote this due to the title of the question: "Fade in ... pure javascript ... simple way."

tl;dr https://jsfiddle.net/nqfud4j0/

The following solution is a basic example of how you can use only Javascript to fade in/out to a desired value. You could also use this with other values/properties, but it also serves as an example for basic tweening.

It's intentionally using setInterval rather than requestAnimationFrame to demonstrate the example's use of time + controlled framerate rather than a delta or 'fast as possible.' A good solution would abstract this logic into a tweening library that combines both RAF + intervals to manage latency between frames.

function fadeTo(element, toValue = 0, duration = 200) {
    // Store our element's current opacity (or default to 1 if null)
    const fromValue = parseFloat(element.style.opacity) || 1;

    // Mark the start time (in ms). We use this to calculate a ratio
    // over time that applied to our supplied duration argument
    const startTime = Date.now();

    // Determines time (ms) between each frame. Sometimes you may not
    // want a full 60 fps for performance reasons or aesthetic 
    const framerate = 1000 / 60; // 60fps
    
    // Store reference to interval (number) so we can clear it later
    let interval = setInterval(() => {
        const currentTime = Date.now();

        // This creates a normalized number between now vs when we
        // started and how far into our desired duration it goes
        const timeDiff = (currentTime - startTime) / duration;

        // Interpolate our values using the ratio from above
        const value = fromValue - (fromValue - toValue) * timeDiff;
        
        // If our ratio is >= 1, then we're done.. so stop processing
        if (timeDiff >= 1) {
            clearInterval(interval);
            interval = 0;
        }
        
        // Apply visual. Style attributes are strings.
        element.style.opacity = value.toString();
    }, framerate)
}


// Element reference
const element = document.querySelector('div');

// Fade in and out on click
element.addEventListener('click', e => {
    // Animates our element from current opacity (1.0) to 0.25 for 1000ms
    fadeTo(element, 0.25, 1000);
    
    // Waits 1s, then animates our element's opacity to 1.0 for 500ms
    setTimeout(() => {
        fadeTo(element, 1.0, 500);
    }, 1000);
});
Matt Kenefick
  • 1,173
  • 1
  • 13
  • 22