搜索
首页web前端css教程表演者可扩展动画:即时建立关键框架

Performant Expandable Animations: Building Keyframes on the Fly

CSS动画技术日益成熟,为开发者提供了更强大的工具。特别是CSS动画,已经成为解决大多数动画用例的基础。然而,有些动画需要更精细的处理。

众所周知,动画应在合成层运行(此处不再赘述,如有兴趣,请参考相关文献)。这意味着动画的变换或不透明度属性不会触发布局或绘制层。而动画高度和宽度等属性则会触发这些层,迫使浏览器重新计算样式,这是需要避免的。

此外,即使是动画变换属性,如果要实现真正的60 FPS动画,可能也需要借助JavaScript,例如使用FLIP技术来实现更流畅的动画!

然而,使用变换属性进行可展开动画的问题在于,缩放函数与动画高度/宽度属性并不完全相同。它会对内容产生倾斜效果,因为所有元素都会被拉伸(向上缩放)或挤压(向下缩放)。

因此,我的常用解决方案(可能仍然是,原因将在后面详细说明)是Brandon Smith文章中的方法3。这种方法仍然对高度进行过渡,但使用JavaScript计算内容大小,并使用requestAnimationFrame强制进行过渡。在OutSystems,我们实际上使用此方法为OutSystems UI手风琴模式构建动画。

使用JavaScript生成关键帧

最近,我偶然发现了Paul Lewis的另一篇精彩文章,详细介绍了一种新的展开和折叠动画解决方案,这促使我撰写本文并推广这种技术。

用他的话说,主要思想是生成动态关键帧,逐步……

[…] 从0到100,并计算元素及其内容所需的缩放值。然后,这些值可以简化为一个字符串,并将其作为样式元素注入页面。

要实现这一点,主要有三个步骤。

步骤1:计算起始和结束状态

我们需要计算两种状态的正确缩放值。这意味着我们对将作为起始状态代理的元素使用getBoundingClientRect(),并将其除以结束状态的值。它应该类似于:

function calculateStartScale () {
  const start= startElement.getBoundingClientRect();
  const end= endElement.getBoundingClientRect();
  return {
    x: start.width / end.width,
    y: start.height / end.height
  };
}

步骤2:生成关键帧

现在,我们需要运行一个for循环,使用所需的帧数作为长度。(为了确保动画流畅,它不应该少于60帧。)然后,在每次迭代中,我们使用缓动函数计算正确的缓动值:

function ease (v, pow=4) {
  return 1 - Math.pow(1 - v, pow);
}

let easedStep = ease(i / frame);

使用该值,我们将使用以下数学公式获得元素在当前步骤的缩放比例:

const xScale = x   (1 - x) * easedStep;
const yScale = y   (1 - y) * easedStep;

然后我们将步骤添加到动画字符串中:

animation  = `${step}% {
  transform: scale(${xScale}, ${yScale});
}`;

为了避免内容被拉伸/倾斜,我们应该对其进行反向动画,使用反向值:

const invXScale = 1 / xScale;
const invYScale = 1 / yScale;

inverseAnimation  = `${step}% {
  transform: scale(${invXScale}, ${invYScale});
}`;

最后,我们可以返回已完成的动画,或直接将它们注入新创建的样式标签中。

步骤3:启用CSS动画

在CSS方面,我们需要在正确的元素上启用动画:

.element--expanded {
  animation-name: animation;
  animation-duration: 300ms;
  animation-timing-function: step-end;
}

.element-contents--expanded {
  animation-name: inverseAnimation ;
  animation-duration: 300ms;
  animation-timing-function: step-end;
}

您可以在Codepen上查看Paul Lewis文章中的菜单示例(由Chris提供)。

构建可展开部分

掌握了这些基本概念后,我想检查一下是否可以将此技术应用于不同的用例,例如可展开部分。

在这种情况下,我们只需要动画高度,特别是在计算缩放比例的函数中。我们从节标题获取Y值,作为折叠状态,并获取整个节来表示展开状态:

    _calculateScales () {
      var collapsed = this._sectionItemTitle.getBoundingClientRect();
      var expanded = this._section.getBoundingClientRect();

      // create css variable with collapsed height, to apply on the wrapper
      this._sectionWrapper.style.setProperty('--title-height', collapsed.height   'px');

      this._collapsed = {
        y: collapsed.height / expanded.height
      }
    }

由于我们希望展开的部分具有绝对定位(为了避免它在折叠状态下占用空间),我们使用折叠高度为其设置CSS变量,并将其应用于包装器。这将是唯一具有相对定位的元素。

接下来是创建关键帧的函数:_createEaseAnimations()。这与上面解释的内容差别不大。对于这个用例,我们实际上需要创建四个动画:

  1. 展开包装器的动画
  2. 内容的反向展开动画
  3. 折叠包装器的动画
  4. 内容的反向折叠动画

我们遵循与之前相同的方法,运行长度为60的for循环(以获得平滑的60 FPS动画),并基于缓动步骤创建关键帧百分比。然后,我们将它推送到最终的动画字符串:

outerAnimation.push(`
  ${percentage}% {
    transform: scaleY(${yScale});
  }`);

innerAnimation.push(`
  ${percentage}% {
    transform: scaleY(${invScaleY});
  }`);

我们首先创建一个样式标签来保存完成的动画。由于这是作为一个构造函数构建的,为了能够轻松添加多个模式,我们希望所有这些生成的动画都在同一个样式表中。因此,首先,我们验证元素是否存在。如果不存在,我们创建它并添加一个有意义的类名。否则,您最终会为每个可展开的部分获得一个样式表,这不是理想的。

 var sectionEase = document.querySelector('.section-animations');
 if (!sectionEase) {
  sectionEase = document.createElement('style');
  sectionEase.classList.add('section-animations');
 }

说到这一点,您可能已经在想,“嗯,如果我们有多个可展开的部分,它们是否仍然使用相同名称的动画,并且其内容可能具有错误的值?”

你完全正确!因此,为了防止这种情况,我们还生成动态动画名称。很酷,对吧?

当进行querySelectorAll('.section')查询以向名称添加唯一元素时,我们使用传递给构造函数的索引:

var sectionExpandAnimationName = "sectionExpandAnimation"   index;
var sectionExpandContentsAnimationName = "sectionExpandContentsAnimation"   index;

然后我们使用此名称在当前可展开部分设置CSS变量。由于此变量仅在此范围内,我们只需要在CSS中将动画设置为新的变量,每个模式将获得其各自的animation-name值。

.section.is--expanded {
  animation-name: var(--sectionExpandAnimation);
}

.is--expanded .section-item {
  animation-name: var(--sectionExpandContentsAnimation);
}

.section.is--collapsed {
  animation-name: var(--sectionCollapseAnimation);
}

.is--collapsed .section-item {
  animation-name: var(--sectionCollapseContentsAnimation);
}

脚本的其余部分与添加事件监听器、切换折叠/展开状态的函数以及一些辅助功能改进有关。

关于HTML和CSS:需要额外的工作才能使可展开功能正常工作。我们需要一个额外的包装器作为不进行动画的相对元素。可展开的子元素具有绝对位置,因此在折叠时不会占用空间。

请记住,由于我们需要进行反向动画,因此我们使其缩放全尺寸以避免内容出现倾斜效果。

.section-item-wrapper {
  min-height: var(--title-height);
  position: relative;
}

.section {
  animation-duration: 300ms;
  animation-timing-function: step-end;
  contain: content;
  left: 0;
  position: absolute;
  top: 0;
  transform-origin: top left;
  will-change: transform;
}

.section-item {
  animation-duration: 300ms;
  animation-timing-function: step-end;
  contain: content;
  transform-origin: top left;
  will-change: transform;  
}

我想强调animation-timing-function属性的重要性。它应该设置为linearstep-end以避免每个关键帧之间的缓动。

will-change属性——正如您可能知道的那样——将为变换动画启用GPU加速,以获得更流畅的体验。使用contains属性,值为contents,将帮助浏览器独立于DOM树的其余部分处理元素,限制其重新计算布局、样式、绘制和大小属性的区域。

我们使用visibilityopacity来隐藏内容,并在折叠时阻止屏幕阅读器访问它。

.section-item-content {
  opacity: 1;
  transition: opacity 500ms ease;
}

.is--collapsed .section-item-content {
  opacity: 0;
  visibility: hidden;
}

最后,我们有了可展开的部分!以下是完整的代码和演示供您查看:

性能检查

每当我们处理动画时,性能都应该铭记于心。因此,让我们使用开发者工具来检查所有这些工作在性能方面是否值得。使用Performance选项卡(我使用的是Chrome DevTools),我们可以分析动画期间的FPS和CPU使用率。

结果非常好!

使用FPS测量工具更详细地检查值,我们可以看到它始终达到60 FPS的标记,即使是滥用使用也是如此。

最终考虑

那么,最终结论是什么?这是否取代了所有其他方法?这是“圣杯”解决方案吗?

在我看来,不是。

但是……这没关系!它只是列表中的另一个解决方案。并且,与任何其他方法一样,应该分析它是否是针对用例的最佳方法。

这种技术肯定有其优点。正如Paul Lewis所说,这确实需要大量工作来准备。但是,另一方面,我们只需要在页面加载时执行一次。在交互过程中,我们仅仅是切换类(在某些情况下,为了辅助功能,还切换属性)。

然而,这给元素的UI带来了一些限制。正如您在可展开部分元素中看到的那样,反向缩放使其更可靠地用于绝对和画布外元素,例如浮动操作或菜单。由于它使用overflow: hidden,因此难以设置边框样式。

尽管如此,我认为这种方法潜力巨大。请告诉我您的想法!

以上是表演者可扩展动画:即时建立关键框架的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
'订阅播客”链接应在哪里?'订阅播客”链接应在哪里?Apr 16, 2025 pm 12:04 PM

有一段时间,iTunes是播客中的大狗,因此,如果您将“订阅播客”链接到喜欢:

浏览器引擎多样性浏览器引擎多样性Apr 16, 2025 pm 12:02 PM

当他们在2013年去Chrome时,我们失去了歌剧。与Edge今年早些时候也进行了同样的交易。迈克·泰勒(Mike Taylor)称这些变化为“减少

网络共享的UX注意事项网络共享的UX注意事项Apr 16, 2025 am 11:59 AM

从垃圾点击诱饵网站到大多数出版物的最多,共享按钮长期以来一直无处不在。然而,这些

每周平台新闻:Apple部署网络组件,渐进的HTML渲染,自托管关键资源每周平台新闻:Apple部署网络组件,渐进的HTML渲染,自托管关键资源Apr 16, 2025 am 11:55 AM

在本周的综述中,Apple进入Web组件,Instagram如何插入脚本以及一些思考的食物,以进行自托管关键资源。

git Pathspecs以及如何使用它们git Pathspecs以及如何使用它们Apr 16, 2025 am 11:53 AM

当我查看GIT命令的文档时,我注意到其中许多人都有选择。我最初以为这只是一个

产品图像的彩色拾取器产品图像的彩色拾取器Apr 16, 2025 am 11:49 AM

听起来有点像一个困难的问题,不是吗?我们经常没有成千上万种颜色的产品镜头,以便我们可以随身携带。我们也不是

黑暗模式与React和Themeprovider切换黑暗模式与React和Themeprovider切换Apr 16, 2025 am 11:46 AM

我喜欢网站具有“暗模式”选项时。黑暗模式使我更容易阅读网页,并帮助我的眼睛更放松。许多网站,包括

带有HTML对话框元素的一些动手带有HTML对话框元素的一些动手Apr 16, 2025 am 11:33 AM

这是我第一次查看HTML元素。我已经意识到了一段时间,但是尚未将其旋转。它很酷,

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
1 个月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
1 个月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
1 个月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它们
1 个月前By尊渡假赌尊渡假赌尊渡假赌

热工具

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

将Eclipse与SAP NetWeaver应用服务器集成。

安全考试浏览器

安全考试浏览器

Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。

Atom编辑器mac版下载

Atom编辑器mac版下载

最流行的的开源编辑器

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

Dreamweaver Mac版

Dreamweaver Mac版

视觉化网页开发工具