Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rewrite jQuery easing easeInExpo function into plain javascript & css

So I have this great little nugget of code that I am trying to rewrite into plain JavaScript and CSS without jQuery.

jQuery.extend(jQuery.easing,{
    easeInExpo: function (x, t, b, c, d) {
        return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
    }
}); 

var nset = false;
$('button#hmenu').click(function(){
    if(!nset){
        $('ul#nav').delay(35).slideDown(300,'easeInExpo');
        $('button#hmenu').addClass('active');
        nset = true;
    } else {
        $('ul#nav').slideUp(550);
        nset = false;
        $('button#hmenu').removeClass('active');
    }
})  

I'm looking at some CSS transitions that use easing but just wondering what the options are? In my code I have a slideDown and up easing functions. This is used in production for a mobile menu nav.

like image 612
drooh Avatar asked Sep 30 '21 23:09

drooh


People also ask

What is the use of easing in jQuery?

Introduction to jQuery Easing. The jQuery easing functions are used to specify the speed at which an animation show at different points within the animation. The jQuery easing functions are built-in functions in the jQuery UI library. In simple words, the jQuery easing functions specify the speed of animation progress.

How to create animation with easing with jQuery?

Jquery Easing Plugin: the jQuery Easing Plugins is the easiest way to provide animation with easing with the help of jQuery. Next, we write the HTML code to understand the UI easing functions more clearly with the following example, where all different types of easing functions will use to show the style of animation, as below:

How do I change the CSS of an element in jQuery?

If you’re calling .css () on an element to change its inline CSS with jQuery, you’d use .style in JavaScript and assign values to its different properties to achieve the same effect: With jQuery, you can pass an object with key-value pairs to style many properties at once.

How do I style multiple properties at once in jQuery?

With jQuery, you can pass an object with key-value pairs to style many properties at once. In JavaScript you can set the values one at a time, or set the entire style string: The .hide () and .show () convenience methods are equivalent to accessing the .style property and setting display to none and block:


Video Answer


2 Answers

Update:

This github repo (You don't need jQuery) has a really comprehensive list of common jQuery functions, all rewritten in vanilla Javascript.

It includes animations, query selectors, ajax, events and other advanced jQuery features.

The slightly older youmightnotneedjquery.com is also very good, particularly if you need to support older IE versions.

You can achieve an easeInExpo style animation by using the following CSS, as provided by this website:

transition: all 500ms cubic-bezier(0.950, 0.050, 0.795, 0.035);

The below example illustrates this easing property on the height of a div. I've updated it to match the delay you've added in jQuery when clicked (35ms), the timings (300ms and 550ms respectively), and jQuery's default easing ('swing') – as provided by this answer – when it's closed:

let expandable = document.getElementById('expandable');
let expandButton = document.getElementById('expand-button');

expandButton.addEventListener('click', () => {
  expandable.classList.toggle('expanded');
});
#expandable {
  background: red;
  transition: all 550ms cubic-bezier(.02, .01, .47, 1);
  height: 0px;
  width: 100px;
  transition-delay: 0ms;
}

#expandable.expanded {
  height: 100px;
  transition-delay: 35ms;
  transition: all 300ms cubic-bezier(0.950, 0.050, 0.795, 0.035);
}
<div id="expandable"></div>
<br />
<button id="expand-button">Toggle expand</button>
like image 109
dave Avatar answered Oct 24 '22 07:10

dave


There's a little bit of magic needed here in order to get your collapsible component to expand to a variable height. If you are just expanding to the same height each time, say 100px, that's a simple as creating a single class such as "expanded" and then adding and removing that class as a boolean switch.

We'll still be using a boolean switch for variable heights, but we'll also need to get the heights of each expandable element using JavaScript, and then refresh those height values if the size of the user's window changes to account for text-wrapping, image resizing, etc.

We can achieve rather simply using custom CSS properties (variables), with a fallback value to unset, meaning that when to variable height is present, the box will expand to show all contents without an animation as a last resort, but in most if not all cases (and for all modern browsers) the custom CSS variable should be the ideal solution for unique values per section.

Here it is in action (below), with both a toggle and accordion example, with a cubic bezier easing function used to closely match the easeInExpo function you supplied in your original question. I pulled this easing function cubic-bezier(0.95, 0.05, 0.795, 0.035) from easings.net/en#easeInExpo where they have a pure CSS easing for easeInExpo and many others.

Simple Example

const expandables = document.querySelectorAll('.expandable');
const setInnerHeights = () => {
  for (const expandable of expandables) {
    expandable.style.setProperty('--inner-height', Array.from(expandable.children).map(child => child.offsetHeight).reduce((a, c) => a + c, 0) + 'px');
  }
};
setInnerHeights();
document.addEventListener('click', e => {
  if (e.target?.matches('.expand-trigger')) {
    const expandable = e.target.nextElementSibling;
    expandable.classList[expandable.classList.contains('expanded') ? 'remove' : 'add']('expanded');
  }
});
window.addEventListener('resize', setInnerHeights);
html {
  height: 100%;
  box-sizing: border-box;
}
*, *::before, *::after {
  box-sizing: inherit;
}
body {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-start;
  min-height: 100%;
  padding: 20px;
}
.expandable {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.25s cubic-bezier(0.95, 0.05, 0.795, 0.035);
  text-align: left;
}
.expandable > p {
  margin: 0;
  padding: 10px 0;
}
.expandable.expanded {
  --content-height: calc(var(--inner-height) + 20px);
  max-height: var(--content-height, unset);
}
<button class="expand-trigger">Expand #1</button>
<div class="expandable">
  <p>Lorem ipsum dolor sit amet.</p>
</div>
<button class="expand-trigger">Expand #2</button>
<div class="expandable">
  <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam mi purus, interdum id mattis et, posuere nec urna. Mauris in ornare sem. Phasellus non eros augue. Fusce tempus bibendum mauris, vel luctus est viverra eget. Cras vitae lectus magna. Integer vulputate est ut felis dictum consectetur. Nunc vitae enim at sem rhoncus aliquet et id risus. Etiam faucibus quis turpis eu pellentesque. Aliquam dictum lorem nec orci finibus commodo. Etiam tincidunt lacinia consectetur. Praesent tortor lorem, imperdiet sed varius vel, varius ac quam. Vestibulum aliquam lorem sem, sit amet imperdiet purus commodo eu. Integer a iaculis tortor.</p>
</div>
<button class="expand-trigger">Expand #3</button>
<div class="expandable">
  <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam mi purus, interdum id mattis et, posuere nec urna. Mauris in ornare sem. Phasellus non eros augue. Fusce tempus bibendum mauris, vel luctus est viverra eget. Cras vitae lectus magna. Integer vulputate est ut felis dictum consectetur. Nunc vitae enim at sem rhoncus aliquet et id risus. Etiam faucibus quis turpis eu pellentesque. Aliquam dictum lorem nec orci finibus commodo. Etiam tincidunt lacinia consectetur. Praesent tortor lorem, imperdiet sed varius vel, varius ac quam. Vestibulum aliquam lorem sem, sit amet imperdiet purus commodo eu. Integer a iaculis tortor.</p>
  <p>Integer convallis lectus eu felis bibendum, vel lacinia metus imperdiet. Maecenas vulputate, quam vitae tempus pretium, erat felis euismod risus, nec blandit leo mi eget purus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer at neque laoreet, egestas dui ut, bibendum lorem. Maecenas elementum odio a congue facilisis. Vivamus risus urna, vestibulum egestas sem nec, lacinia volutpat metus. Suspendisse potenti. Suspendisse ullamcorper commodo libero, sed rhoncus nibh porta in. Donec mi felis, posuere luctus varius ac, faucibus vitae erat.</p>
</div>

Complex Example

const initAccordions = () => {
  const getNode = selector => document.querySelector(selector),
      getNodes = selector => Array.from(document.querySelectorAll(selector)),
      findChildren = (node, selector) => Array.from(node.children).filter(e => e.matches?.(selector)),
      findChild = (node, selector) => Array.from(node.children).find(e => e.matches?.(selector)),
      _addInput = (node, position, id, checked) => node.insertAdjacentHTML(position, `<input type="radio" name="accordion-${id}"${checked ? ' checked="checked"' : ''}>`),
      setInnerHeight = node => {
        const height = Array.from(node.children).map(child => child.offsetHeight).reduce((a, c) => a + c, 0) + 'px';
        node.style.setProperty('--inner-height', height);
      },
      accordions = Array.from(document.querySelectorAll('.accordion'));
  let accordionIndex = 0;
  for (const accordion of accordions) {
    const isToggle = accordion.dataset?.type === 'toggle',
        panels = findChildren(accordion, '.accordion--panel');
    let panelIndex = 0;
    for (const panel of panels) {
      const title = findChild(panel, '.accordion--panel--title'),
          content = findChild(panel, '.accordion--panel--content'),
          addInput = (node, position, checked) => _addInput(node, position, accordionIndex + (isToggle ? '-' + panelIndex : ''), checked);
      setInnerHeight(content);
      addInput(title, 'beforebegin');
      addInput(title, 'afterbegin', true);
      panelIndex++;
    }
    accordionIndex++;
  }
  window.addEventListener('resize', () => {
    const panelContents = Array.from(document.querySelectorAll('.accordion > .accordion--panel > .accordion--panel--content'));
    for (const content of panelContents) setInnerHeight(content);
  });
};
initAccordions();
html {
  height: 100%;
  box-sizing: border-box;
}
*, *::before, *::after {
  box-sizing: inherit;
}
body {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  min-height: 100%;
  padding: 20px;
}
.accordion--panel > [type=checkbox],
.accordion--panel > [type=radio], .accordion--panel--title > [type=checkbox],
.accordion--panel--title > [type=radio] {
  -webkit-appearance: none;
     -moz-appearance: none;
          appearance: none;
  display: block;
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  cursor: pointer;
}
.accordion--panel > [type=checkbox]:checked,
.accordion--panel > [type=radio]:checked, .accordion--panel--title > [type=checkbox]:checked,
.accordion--panel--title > [type=radio]:checked {
  display: none;
}
.accordion--panel {
  border-radius: 7px;
}
.accordion--panel--title {
  background-color: #ccc;
}
.accordion--panel--content {
  box-shadow: inset 0 0 0 2px #ccc;
  border-radius: 0 0 7px 7px;
}
.accordion {
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: 100%;
  max-width: 500px;
}
.accordion--panel {
  display: flex;
  flex-direction: column;
  position: relative;
  overflow: hidden;
}
.accordion--panel > [type=checkbox],
.accordion--panel > [type=radio] {
  z-index: 1;
}
.accordion--panel--title, .accordion--panel--content {
  padding-inline: 15px;
}
.accordion--panel--title {
  position: relative;
  padding-block: 10px;
}
.accordion--panel--content {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.25s cubic-bezier(0.95, 0.05, 0.795, 0.035);
}
.accordion--panel--content--inner > p:first-child {
  margin-top: 10px;
}
.accordion--panel--content--inner > p:last-child {
  margin-bottom: 10px;
}
[type=checkbox]:checked ~ .accordion--panel--content, [type=radio]:checked ~ .accordion--panel--content {
  --content-height: calc(var(--inner-height) + 20px);
  max-height: var(--content-height, unset);
}
<h2>Accordion Demo</h2>
<div class="accordion" data-type="accordion">
  <div class="accordion--panel">
    <div class="accordion--panel--title">Title #1</div>
    <div class="accordion--panel--content">
      <div class="accordion--panel--content--inner">
        <p>Lorem ipsum dolor sit amet.</p>
      </div>
    </div>
  </div>
  <div class="accordion--panel">
    <div class="accordion--panel--title">Title #2</div>
    <div class="accordion--panel--content">
      <div class="accordion--panel--content--inner">
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam mi purus, interdum id mattis et, posuere nec urna. Mauris in ornare sem. Phasellus non eros augue. Fusce tempus bibendum mauris, vel luctus est viverra eget. Cras vitae lectus magna. Integer vulputate est ut felis dictum consectetur. Nunc vitae enim at sem rhoncus aliquet et id risus. Etiam faucibus quis turpis eu pellentesque. Aliquam dictum lorem nec orci finibus commodo. Etiam tincidunt lacinia consectetur. Praesent tortor lorem, imperdiet sed varius vel, varius ac quam. Vestibulum aliquam lorem sem, sit amet imperdiet purus commodo eu. Integer a iaculis tortor.</p>
      </div>
    </div>
  </div>
  <div class="accordion--panel">
    <div class="accordion--panel--title">Title #3</div>
    <div class="accordion--panel--content">
      <div class="accordion--panel--content--inner">
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam mi purus, interdum id mattis et, posuere nec urna. Mauris in ornare sem. Phasellus non eros augue. Fusce tempus bibendum mauris, vel luctus est viverra eget. Cras vitae lectus magna. Integer vulputate est ut felis dictum consectetur. Nunc vitae enim at sem rhoncus aliquet et id risus. Etiam faucibus quis turpis eu pellentesque. Aliquam dictum lorem nec orci finibus commodo. Etiam tincidunt lacinia consectetur. Praesent tortor lorem, imperdiet sed varius vel, varius ac quam. Vestibulum aliquam lorem sem, sit amet imperdiet purus commodo eu. Integer a iaculis tortor.</p>
        <p>Integer convallis lectus eu felis bibendum, vel lacinia metus imperdiet. Maecenas vulputate, quam vitae tempus pretium, erat felis euismod risus, nec blandit leo mi eget purus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer at neque laoreet, egestas dui ut, bibendum lorem. Maecenas elementum odio a congue facilisis. Vivamus risus urna, vestibulum egestas sem nec, lacinia volutpat metus. Suspendisse potenti. Suspendisse ullamcorper commodo libero, sed rhoncus nibh porta in. Donec mi felis, posuere luctus varius ac, faucibus vitae erat.</p>
      </div>
    </div>
  </div>
</div>
<h2>Toggle Demo</h2>
<div class="accordion" data-type="toggle">
  <div class="accordion--panel">
    <div class="accordion--panel--title">Title #1</div>
    <div class="accordion--panel--content">
      <div class="accordion--panel--content--inner">
        <p>Lorem ipsum dolor sit amet.</p>
      </div>
    </div>
  </div>
  <div class="accordion--panel">
    <div class="accordion--panel--title">Title #2</div>
    <div class="accordion--panel--content">
      <div class="accordion--panel--content--inner">
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam mi purus, interdum id mattis et, posuere nec urna. Mauris in ornare sem. Phasellus non eros augue. Fusce tempus bibendum mauris, vel luctus est viverra eget. Cras vitae lectus magna. Integer vulputate est ut felis dictum consectetur. Nunc vitae enim at sem rhoncus aliquet et id risus. Etiam faucibus quis turpis eu pellentesque. Aliquam dictum lorem nec orci finibus commodo. Etiam tincidunt lacinia consectetur. Praesent tortor lorem, imperdiet sed varius vel, varius ac quam. Vestibulum aliquam lorem sem, sit amet imperdiet purus commodo eu. Integer a iaculis tortor.</p>
      </div>
    </div>
  </div>
  <div class="accordion--panel">
    <div class="accordion--panel--title">Title #3</div>
    <div class="accordion--panel--content">
      <div class="accordion--panel--content--inner">
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam mi purus, interdum id mattis et, posuere nec urna. Mauris in ornare sem. Phasellus non eros augue. Fusce tempus bibendum mauris, vel luctus est viverra eget. Cras vitae lectus magna. Integer vulputate est ut felis dictum consectetur. Nunc vitae enim at sem rhoncus aliquet et id risus. Etiam faucibus quis turpis eu pellentesque. Aliquam dictum lorem nec orci finibus commodo. Etiam tincidunt lacinia consectetur. Praesent tortor lorem, imperdiet sed varius vel, varius ac quam. Vestibulum aliquam lorem sem, sit amet imperdiet purus commodo eu. Integer a iaculis tortor.</p>
        <p>Integer convallis lectus eu felis bibendum, vel lacinia metus imperdiet. Maecenas vulputate, quam vitae tempus pretium, erat felis euismod risus, nec blandit leo mi eget purus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer at neque laoreet, egestas dui ut, bibendum lorem. Maecenas elementum odio a congue facilisis. Vivamus risus urna, vestibulum egestas sem nec, lacinia volutpat metus. Suspendisse potenti. Suspendisse ullamcorper commodo libero, sed rhoncus nibh porta in. Donec mi felis, posuere luctus varius ac, faucibus vitae erat.</p>
      </div>
    </div>
  </div>
</div>

Other CSS easing functions are available at easings.net.

You can also customize your easing function visually using Chrome's DevTools by adding either the transition or transition-timing-function properties to any element with the value of ease, then clicking the square curve icon next to the word ease and dragging either of the circular handles.

like image 28
Brandon McConnell Avatar answered Oct 24 '22 08:10

Brandon McConnell