5

I'm trying to make a slide-toggle-fade animation like the slideToggle() method but with velocity.js - in the hopes that it will be much smoother.

Because I can't scroll to auto - I am putting the height in variable and using that to animate height. The issue I run into is that the height value is stored once and if the page is slightly resized, then the number is no longer correct. - Also - because the area is hidden on page load, (right after it gets the initial height) I can't check for height again (if window resize occurs)

Eventually I'd like to put it into a function, so keeping things relative it key.

Also, if you haven't used velocity.js, it's basically just like .animate() - so it's not really a part of the question.

HTML

<section>
  <div class="button-w">
    <button>Toggle</button>
  </div>

  <div class="box">

    <p>{{content}}</p>

    <div class="button-w">
      <button>Close</button>
    </div>

  </div>
</section>


CSS

.button-w {
  width: 100%;
  float: left;
}

.box {
  width: 100%;
  border: 1px solid lime;
  overflow: hidden;
  color: white;
}


jquery (velocity.js)

var boxxHeight = $('.box').outerHeight();

$('.box').hide();

$('button').on('click', function() {

    var boxx = $('.box');

    if ( boxx.is(":visible") ) {

      boxx.velocity({ opacity: 0, height: 0 }, { display: "none" });

    } else {

      boxx.velocity({ opacity: 1, height: boxxHeight }, { display: "block" });

    }

});

Any Ideas?

EDIT

I didn't really have any real reason to need the sections to be display: none. This means that I can have an outer box with overflow: hidden, and the content will always have it's natural height to retrieve and work with.

<div class="button-w">
    <button>Toggle</button>
</div>

<div class="box">

    <div class="inner-wrapper">

    {{content}}

    <div class="button-w">
      <button>Close</button>
    </div>

  </div>
</div>


CSS

.box {
  width: 100%;
  overflow: hidden;
  color: white;
  padding: 1rem 0;
  .inner-wrapper {
    float: left;
    opacity: 0;
    padding-bottom: 5em;
    background: white;
    color: black;
    padding: 1rem;
  }
}

button {
  display: block;
  padding: 1rem;
  border: 0;
  &:focus {
    outline: 0;
  }
}

.hidden-box {
  height: 0;
  opacity: 0;
  padding: 0;
  .inner-wrapper {
    opacity: 0;
  }
}


jQuery (with velocity.js)

"hide" the .inner-wrapper by setting the .box to height:0 and overflow hidden with .hidden-box class. The the height of the .inner-wrapper is stored and the height animation occurs just on the .box - and an opacity on the .inner-wrapper...

$('.box').addClass('hidden-box')

$('button').on('click', function() {

  var vBoxx = $(this).closest('section').find('.box');
  var vInner_wrapper = $(this).closest('section').find('.inner-wrapper');
  var vElementHeight = vInner_wrapper.outerHeight();

  if ( vBoxx.hasClass("hidden-box") ) {

    vBoxx.velocity({
      height: vElementHeight 
    }, {
      duration: 500,
    }).removeClass('hidden-box');

    setTimeout( function() {
      $(vInner_wrapper).velocity({opacity: 1});
    },250); 

  } else {

    $(vInner_wrapper).velocity({
      opacity: 0
    });

     setTimeout( function() {
      vBoxx.velocity({ height: 0 }).addClass('hidden-box');
    },250); 

  }

});

Working on CodePen HERE:

sheriffderek
  • 8,217
  • 6
  • 38
  • 64

3 Answers3

4

Why not use el.velocity("reverse"); to close it? The elements original height is already stored within Velocity.

suncat100
  • 1,930
  • 1
  • 13
  • 22
  • Yeah. That is really cool. The think is, there are multiple buttons that do different things with the height... a close button and an open button that also acts as a close button etc... + If someone resizes their browser --- I want a new height stored. – sheriffderek Jun 08 '14 at 19:21
  • Worked perfectly for my use case. Thanks! – James Hill Nov 11 '16 at 14:04
2

You could try changing the margin-top value in order to move them up and down instead. It's not the best performance wise, but I think it's the only way you can do what you want correctly

var boxx = $('.innerbox'),
    count = 0;

$('button').on('click', function() {
    if(++count % 2 == 1) {
        boxx.velocity({ marginTop: "-100%" }, { duration:1000 });
    } else {
        boxx.velocity({ marginTop: "0%" }, { duration:1000 });
    }
});

Demo

Zach Saucier
  • 21,903
  • 12
  • 75
  • 126
  • Pretty cool idea. It doesn't seem to work as expected though, when you have actual content in there. Check this out: http://jsfiddle.net/sheriffderek/WnHB6/ Your opacity suggestion made me wonder why I would really need to set the display property anyway. – sheriffderek May 03 '14 at 23:41
  • @sheriffderek I updated my answer, having ignored your velocity calls the first time (whoops) – Zach Saucier May 04 '14 at 03:35
  • It doesn't perform as well as `transform`ing it would, but `transform` only changes the *visual* state, not the layout – Zach Saucier May 04 '14 at 18:44
  • Thought I was clever and was gonna get some SO karma for this one but you beat me to it by a few years. :P `marginTop: '-100%'` doesn't do it for me though, I need to get the `-outerHeight(true)` for it. http://codepen.io/corysimmons/pen/xZvPab?editors=1010 – corysimmons Feb 24 '16 at 05:28
2

You can now use

$element.velocity("slideUp", options);
$element.velocity("slideDown", options);

For instance

$element
    .velocity("slideDown", { duration: 1500 })
    .velocity("slideUp", { delay: 500, duration: 1500 });

It's worth noting that the height of the element is set to auto after the animation is completed. So no worries about containers with dynamic size ...

Official documentation: http://julian.com/research/velocity/#fade

SepehrM
  • 961
  • 1
  • 16
  • 42