搜尋
首頁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 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 19, 2025 am 11:39 AM

這裡有一個帶子元素的容器:

菜單具有'動態命中區域”菜單具有'動態命中區域”Apr 19, 2025 am 11:37 AM

飛翔的菜單!您需要在第二個菜單中實現懸停事件以顯示更多菜單項的菜單,即您在棘手的領域中。一方面,他們應該

通過Webvtt改善視頻可訪問性通過Webvtt改善視頻可訪問性Apr 19, 2025 am 11:27 AM

“網絡的力量在於其普遍性。每個人的訪問無論殘疾是一個重要方面。” - 蒂姆·伯納斯 - 李

每周平台新聞:CSS :: Marker偽元素,預先渲染的Web組件,向您的網站添加Webmention每周平台新聞:CSS :: Marker偽元素,預先渲染的Web組件,向您的網站添加WebmentionApr 19, 2025 am 11:25 AM

在本週的綜述中:datepickers正在讓鍵盤用戶頭痛,一個新的Web組件編譯器,有助於與Fouc進行戰鬥,我們終於獲得了造型列表項目標記,以及在您的網站上獲得網絡攻擊的四個步驟。

使寬度和靈活的物品一起玩得很好使寬度和靈活的物品一起玩得很好Apr 19, 2025 am 11:23 AM

簡短的答案:您可能要尋找的是彈性折射和彈性基礎。

位置粘性和桌子標頭位置粘性和桌子標頭Apr 19, 2025 am 11:21 AM

您可以位置:粘性;一個

每周平台新聞:HTML在搜索控制台,全局腳本範圍中的HTML檢查,Babel Envs添加默認查詢查詢每周平台新聞:HTML在搜索控制台,全局腳本範圍中的HTML檢查,Babel Envs添加默認查詢查詢Apr 19, 2025 am 11:18 AM

在本週的Web平台新聞世界中,Google搜索控制台可以更輕鬆地查看爬行的標記,我們了解到自定義屬性

Indieweb和網絡企業Indieweb和網絡企業Apr 19, 2025 am 11:16 AM

Indieweb是一回事!他們將舉行會議和一切。紐約客甚至在寫這件事:

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 無盡。

熱工具

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

Dreamweaver Mac版

Dreamweaver Mac版

視覺化網頁開發工具

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。