最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Rewrite jQuery easing easeInExpo function into plain javascript & css - Stack Overflow

programmeradmin3浏览0评论

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.

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.

Share Improve this question edited Sep 30, 2021 at 23:52 drooh asked Sep 30, 2021 at 23:44 droohdrooh 6784 gold badges20 silver badges52 bronze badges 5
  • The options are easily found with Google: CSS3 equivalent to jQuery slideUp and slideDown?, Need help changing from jquery to vanilla javascript. Do you have some attempted CSS of your own? – Sebastian Simon Commented Sep 30, 2021 at 23:47
  • my question is specifically regarding the easeInExpo custom function, not just slideUp and down – drooh Commented Sep 30, 2021 at 23:50
  • Certainly you can use that question’s answers as a platform for your own experiments in solving the issue. Currently, you’re asking the volunteers who answer questions to reverse engineer jQuery animation for you, which seems to be asking a lot. – Heretic Monkey Commented Oct 1, 2021 at 0:15
  • Yep, that's what I'm asking for is help in which direction to go. If you don't want to help that is fine but there may be others who have dealt with an issue similar to mine. – drooh Commented Oct 1, 2021 at 0:19
  • @drooh I added a simplified example alongside my more plex example to show two ends of the spectrum. Because you may not always know the inner height of the content you're dealing with, we can use a little bit of JavaScript to retrieve and store the inner height of your expandable content areas, and then refresh those values any time the browser is resized. – Brandon McConnell Commented Oct 5, 2021 at 20:58
Add a ment  | 

2 Answers 2

Reset to default 9 +500

Update:

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

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

The slightly older youmightnotneedjquery. 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>

There's a little bit of magic needed here in order to get your collapsible ponent 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/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 modo. Etiam tincidunt lacinia consectetur. Praesent tortor lorem, imperdiet sed varius vel, varius ac quam. Vestibulum aliquam lorem sem, sit amet imperdiet purus modo 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 modo. Etiam tincidunt lacinia consectetur. Praesent tortor lorem, imperdiet sed varius vel, varius ac quam. Vestibulum aliquam lorem sem, sit amet imperdiet purus modo 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 modo 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 modo. Etiam tincidunt lacinia consectetur. Praesent tortor lorem, imperdiet sed varius vel, varius ac quam. Vestibulum aliquam lorem sem, sit amet imperdiet purus modo 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 modo. Etiam tincidunt lacinia consectetur. Praesent tortor lorem, imperdiet sed varius vel, varius ac quam. Vestibulum aliquam lorem sem, sit amet imperdiet purus modo 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 modo 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 modo. Etiam tincidunt lacinia consectetur. Praesent tortor lorem, imperdiet sed varius vel, varius ac quam. Vestibulum aliquam lorem sem, sit amet imperdiet purus modo 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 modo. Etiam tincidunt lacinia consectetur. Praesent tortor lorem, imperdiet sed varius vel, varius ac quam. Vestibulum aliquam lorem sem, sit amet imperdiet purus modo 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 modo 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.

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.

发布评论

评论列表(0)

  1. 暂无评论