聊聊CSS 缓动函数的新成员linear()

Posted 饭特稠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了聊聊CSS 缓动函数的新成员linear()相关的知识,希望对你有一定的参考价值。

CSS 缓动函数是一种用于控制 CSS 动画过渡效果的函数,可以让动画变得更加自然。这篇文章将介绍一种新的 CSS easing function,即 linear(),它可以模拟出更复杂的缓动效果,文中demo请在chrome 113+中观看。

什么是 easing function?

在动画中,有一种叫做“缓动效果”的技术,它可以让动画变得更加自然。在实现缓动效果时,通常会使用 easing function。easing function 是一种函数,它可以将动画的进度(通常是一个 0 到 1 之间的数字)映射为动画的值。

在 CSS 中,可以使用 easing function 控制 CSS 动画的过渡效果。在 CSS 中,可以使用 transition-timing-function 属性来指定 easing function。例如:

.element 
  transition-property: width;
  transition-duration: 1s;
  transition-timing-function: linear;

在这个示例中,transition-timing-function 属性的值为 linear,这意味着动画的过渡效果将呈现线性的变化。在动画中,同样有一个animation-timing-function属性来控制动画的缓动效果。除了 linear 这个关键字之外,还有linear, ease, ease-in, ease-out, ease-in-out等关键字,以及cubic-bezier(p1, p2, p3, p4), steps(n, )等函数。今天我们要说的是一种新的缓动函数 linear(),注意和前面的关键字 linear 区分

linear缓动函数的语法


假设我们有一个动画,要在3s内对一个div放大到原来的10倍:

linear(0, 1) //等同于关键字linear
linear(0, 0.9, 1)  // 0-1.5s 放到到9倍,1.5s-3s放大到10倍
linear(0, 0.5 20%, 0.8 60%, 1)  // 0-20%的时间 放到到5倍,20%-60%的时间放大到8倍,最后放大到10倍

画出图来分别是这样的:

linear
linear(0, 1)
linear(0, 0.9, 1)
linear(0, 0.5 20%, 0.8 60%, 1)

linear()模拟跳动效果

虽然我们在上面最多只有4个关键点,但是linear函数可以接收更多的关键点参数来实现更复杂的动画,比如跳动效果。如果你使用过animate.css 的 bounceInDown效果,会发现直接使用之前的CSS的缓动函数是比较难以实现的。在animate.css的实现里面, 是使用一系列关键帧再加上cubic-bezier缓动函数来实现的:

@keyframes bounceInDown 
  from,
  60%,
  75%,
  90%,
  to 
    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
  

  0% 
    opacity: 0;
    transform: translate3d(0, -3000px, 0) scaleY(3);
  

  60% 
    opacity: 1;
    transform: translate3d(0, 25px, 0) scaleY(0.9);
  

  75% 
    transform: translate3d(0, -10px, 0) scaleY(0.95);
  

  90% 
    transform: translate3d(0, 5px, 0) scaleY(0.985);
  

  to 
    transform: translate3d(0, 0, 0);
  


.bounceInDown 
  animation-name: bounceInDown;

现在有了linear()缓动函数,我们可以在一个关键帧的情况下再结合linear缓动函数来实现:

linear(
    0, 0.063, 0.25 18.2%, 1 36.4%, 0.813, 0.75, 0.813, 1, 0.938, 1, 1
  );
上面的缓动函数对应的图像如下:


很明显,如果让我们手写这样一个linear函数还是比较复杂的,好在有大佬帮我们做了一个生成工具:https://linear-easing-generator.netlify.app/

兼容性

最后来看看兼容性,目前在chrome 113+可用,暂时还不能用于生产,但两年应该就可以愉快地使用了,本文完

参考文档

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

【中文标题】将 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 旁边的方形曲线图标并拖动任一圆形手柄。

【讨论】:

以上是关于聊聊CSS 缓动函数的新成员linear()的主要内容,如果未能解决你的问题,请参考以下文章

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

tween.js的动画效果

JS动画公式

caurina缓动类

关于css3中linear-gradient中的百分

深入理解 CSS linear-gradient