将 jQuery 缓动 easeInExpo 函数重写为普通的 javascript 和 css

Posted

技术标签:

【中文标题】将 jQuery 缓动 easeInExpo 函数重写为普通的 javascript 和 css【英文标题】:Rewrite jQuery easing easeInExpo function into plain javascript & css 【发布时间】:2021-11-22 17:54:39 【问题描述】:

所以我有这个很棒的小代码块,我正在尝试在没有 jQuery 的情况下将其重写为纯 javascript 和 CSS。

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');
    
)  

我正在查看一些使用缓动的 CSS 过渡,但只是想知道有哪些选项?在我的代码中,我有一个 slideDown 和 up 缓动函数。这在生产中用于移动菜单导航。

【问题讨论】:

使用 Google 很容易找到这些选项:CSS3 equivalent to jQuery slideUp and slideDown?、Need help changing from jquery to vanilla javascript。你有自己尝试过的 CSS 吗? 我的问题是专门针对easeInExpo 自定义功能,而不仅仅是上下滑动 当然,您可以使用该问题的答案作为您自己的实验解决问题的平台。目前,您正在请回答问题的志愿者为您对 jQuery 动画进行逆向工程,这似乎要求很多。 是的,这就是我要的帮助,帮助您朝哪个方向前进。如果您不想提供帮助,那很好,但可能还有其他人处理过与我类似的问题。 @drooh 我在更复杂的示例旁边添加了一个简化示例,以显示频谱的两端。因为您可能并不总是知道您正在处理的内容的内部高度,我们可以使用一点 JavaScript 来检索和存储可扩展内容区域的内部高度,然后在浏览器调整大小时随时刷新这些值. 【参考方案1】:

更新:

This github repo(你不需要 jQuery)有一个非常全面的常用 jQuery 函数列表,全部用原生 Javascript 重写。

它包括动画、查询选择器、ajax、事件和其他高级 jQuery 功能。

稍微老一点的youmightnotneedjquery.com也很好,特别是如果你需要支持老的IE版本的话。

您可以使用this website提供的以下CSS实现easeInExpo风格的动画:

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

以下示例说明了 div 的 height 上的缓动属性。我已对其进行了更新,以匹配您在 jQuery 中添加的单击延迟(35 毫秒)、时间(分别为 300 毫秒和 550 毫秒)以及 jQuery 的默认缓动('swing')——由this 答案提供——当它是关闭:

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>

【讨论】:

这看起来很不错,我会试着整理一个更完整的例子,谢谢! 我添加了几个链接,应该涵盖您正在寻找的内容。 谢谢,jQuery 可以很好地让复杂的东西变得干净。优雅而简单。看起来绝大多数 jQuery 已经过时了,但只有少数更复杂的块可以使用 CSS 选项和 JavaScript 选项以各种方式进行翻译。 如果您只需要支持 (IE11+) 较新的浏览器,您可能需要查看 cash 库。它具有 jQuery 的许多特性(并共享其语法),但只有 jQuery 的 10% 左右。 补充一点——如果你想为动画走 CSS 路线Animate.css 会让生活变得更轻松。 velocity 使用与 jQuery 的 animate 相同的 API,但通常性能更高。【参考方案2】:

这里需要一点魔法才能让您的可折叠组件展开到可变高度。如果您只是每次都扩展到相同的高度,比如 100 像素,那么这很简单,只需创建一个单独的类(例如“expanded”),然后将该类作为布尔开关添加和删除。

我们仍将使用布尔开关来实现可变高度,但我们还需要使用 JavaScript 获取每个可展开元素的高度,然后在用户窗口大小发生变化时刷新这些高度值文本换行、图像大小调整等。

我们可以很简单地使用自定义 CSS 属性(变量),将回退值设置为unset,这意味着当可变高度存在时,框将展开以显示所有内容而不作为最后的动画,但在大多数情况下(以及所有现代浏览器),自定义 CSS 变量应该是每个部分唯一值的理想解决方案。

这里正在运行(下图),带有切换和手风琴示例,三次贝塞尔曲线缓动函数用于与您在原始问题中提供的 easeInExpo 函数非常匹配。我从easings.net/en#easeInExpo 中提取了这个缓动函数cubic-bezier(0.95, 0.05, 0.795, 0.035),在那里他们为easeInExpo 和许多其他函数提供了纯CSS 缓动。

简单示例

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>

复杂示例

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>

easings.net 提供其他 CSS 缓动功能。

您还可以使用 Chrome 的 DevTools 以可视方式自定义您的缓动功能,方法是将 transitiontransition-timing-function 属性添加到值为 ease 的任何元素,然后单击单词 ease 旁边的方形曲线图标并拖动任一圆形手柄。

【讨论】:

以上是关于将 jQuery 缓动 easeInExpo 函数重写为普通的 javascript 和 css的主要内容,如果未能解决你的问题,请参考以下文章

帮助自定义 jquery 缓动功能

如何构建自定义 jQuery 缓动/弹跳动画?

修复了 jQuery 缓动的加速?

JS动画之缓动函数分析及动画库

jquery ui拖动缓动/惯性

Jquery 缓动动画在 iPad 上滞后