曾經想知道如果播放一個由 2048 個 SVG 節點組成的動畫,使用哪個前端框架會更流暢?這里有一些 GIF 動畫展示。
同樣是 畢達哥拉斯樹動畫 ,同樣是在2012年中的視膜屏 MacBook Pro 中。所有動畫都使用 LICEcap 錄制,使用常規設置,可以在 Chrome、Spotify、Emacs 中運行。點擊 GIF 可以看到它的代碼。
— Angular 2 和 CycleJS 在 12 月 23 日加入 —
人們都想知道這個測試的重要性及其重要的原因。
很多 GIF 都能在它們的GitHub 上找到鏈接。甚至可以復制庫然后在本地運行,非常有趣。
實現者: 本文作者
實現者: Jason Miller,Preact 的創造者
實現者: Dominic Gannaway,Inferno 的創造者
實現者: Evan You,Vue 的創造者
實現者: Tero Parviainen,JavaScript 顧問
實現者: Wayne Maurer,Lambda IT 的創始人
感謝 Jason、Dominic 和 Evan 創建分支,同樣感謝 Tero 和 Wayne 加入了他們自己的版本。如果有人能不使用框架,直接用原生 JavaScript 實現,那一定很酷。
讓我們通過 代碼 了解它是如何工作的。
在 Jason 和Evan 的提示下,對鼠標事件進行節流能使 demo 更快。結果證明原始版本的動畫樹運行緩慢不是 React 自身的原因,而是每個刷新周期內太多的請求讓渲染引擎不堪重負。
我嘗試過對 requestAnimationFrame 進行節流,但是實際效果并不好。相比之下限制 React 重繪周期的方法就簡單而有效。
onMouseMove(event) { if (this.running) return; this.running = true; // calculate stuff this.setState({ heightFactor: scaleFactor(y), lean: scaleLean(x) }); this.running = false; }
先檢查是否在進行更新,如果沒有就手動更新。這個之所以生效是因為 React 的引擎是同步的。
要是沒有 React Fiber,我覺得它可能會掛掉。macr;\ (ツ) /macr;
Jason 用preact-compat 層使得 Preact 看起來很像 React。這很有可能影響它的性能。
我喜歡 Preact 的示例是因為它使用異步渲染讓效果更流暢。鼠標移動后,你能看到重繪周期滯后而產生的神奇效果。我很喜歡這效果。
代碼實現: diff on github
在 package.json 中,他添加了 preact,preact-compat 和 React 庫的 preact-compat 克隆 ,后者能讓你不需要改變 imports。
他把無狀態的Pythagoras 功能組件轉換成一個有狀態的組件從而實現異步渲染。
// src/Pythagoras.jsexport default class { render(props) { return Pythagoras(props); } }
并啟用去抖動 異步渲染:
// src/index.js import { options } from 'preact'; options.syncComponentUpdates = false; //option 1: rIC setTimeout fallback let timer; options.debounceRendering = f =gt; { clearTimeout(timer); timer = setTimeout(f, 100); requestIdleCallback(f); };
我最喜歡 Preact 的部分是,它可以作為 React 的替代品,并且運行良好。從我目前的應用程序來看,它的性能優化會有不錯的發展前景。
你可以用 Inferno 替代 React。Dominic 說這會影響性能,所以他就創建了新分支。你可以 在 github 上比對差異 。
Dominic 把所有相關的react-scripts 改成inferno-scripts。他同時也把react改成inferno-beta36,這意味著我的CTO肯定不允許我在生產環境中使用它。
從上面看出,它最主要的是各種導入的改變——React 變成 Inferno,還把許多類方法改成綁定箭頭函數。我不知道這是出于風格的選擇還是 Inferno 的需要。
他也把基于字符串引用改成基于回調引用,Inferno 因為性能的原因不能使用基于字符串引用。取而代之,我們可以用 D3 來檢測 SVG 上的鼠標位置。這比起我們自己弄簡單很多。
// src/App.js class App extends Component { // ... svgElemeRef = (domNode) =gt; { this.svgElement = domNode; } // ... render() { // .. }
在Pythagoras 核心組件上,他添加兩個Inferno 特殊屬性:noNormalize和hasNonKeyedChildren.
從八天前的 issue 知道,noNormalize是提高性能的一個基準,hasNonKeyedChildren 的作用尚不明確。我猜想這兩個屬性都是用來為虛擬 DOM diffing 算法優化性能。
這項工作的工作量比較大,是由Evan和樹的創作者Phan An 完成的。
Vue 并沒有打算模仿 React 的 API,它擁有自己的一套代碼。我想通過 github 展示它們之間的差異,但這比較繁瑣。我建議你去 Github 自行查看 。
你可以識別出 Pythagoras 核心組件 。Evan 使用了 transform-vue-jsx,使得 JSX 能在 Vue 里使用。
main.app 文件 在此,你可以點擊查看然后理解其代碼。
讓我們來試一下吧。
把它分成lt;templategt;,lt;scriptgt; 和 lt;stylegt; 三個部分。這看起來有點像JSX 或者HTML,但實際上是模板帶上了冒號前綴。
Vue 似乎采用了 React 的 put-it-all-together 組件化提示,但按語言拆分。雖然這看起來很簡潔,但按照我以往的經驗,實際操作其實很麻煩。
App 組件還是跟過去類似,但它使用data() 來定義默認的狀態,用 $refs 代替 this.refs,用一個 name 屬性代替了命名類本身,components 屬性定義子節點,用methods 屬性來定義類方法。
雖然我不是 Angular 的粉絲,但我得承認它的確很好使用。
我不知道為什么,也許 TypeScript 的那些類型檢查在轉譯后增加了運行時額外開銷?
顯然代碼是重寫了,Tero 需要將代碼遷移到 TypeScript,這真厲害,我可做不到。
我很好奇,編程語言的隔閡會怎樣影響你在網上找的隨機庫的可重用性。
代碼看起來似乎包含很多文件。而 App 是分成 app.module.ts,app.component.ts,app.component.html和app.component.css 幾個文件。和Pythagoras一樣。
當你看到Angular 的 html 文件時,意味著Angular 堅持了一個文件對應一種語言的傳統。
lt;div class=quot;App-headerquot;gt; lt;h2gt;This is a dancing Pythagoras treelt;/h2gt; lt;/divgt; lt;p class=quot;App-introquot;gt; lt;svg #svg [attr.width]=quot;widthquot; [attr.height]=quot;heightquot; style=quot;border: 1px solid lightgrayquot;gt; lt;g app-pythagoras [w]=quot;baseWquot; [heightFactor]=quot;heightFactorquot; [lean]=quot;leanquot; [x]=quot;width / 2 - 40quot; [y]=quot;height - baseWquot; [lvl]=quot;0quot; [maxlvl]=quot;currentMaxquot; /gt; lt;/svggt; lt;/pgt;
這在 HTML 中看起來很有趣。
我對模塊和組件之間區別的還沒有完全理解。似乎模塊定義了某些確定的引入,子組件等。但是每個組件仍然會定義它自己的 CSS 和 模板導入。
也因為我對這一塊內容的了解不夠深入,在此不對其用例的好處做過多描述。
這在我的設備上顯示很流暢,可能是因為我剛剛看完 Angular 版本的演示。
Wayne 把所有東西都轉譯成 TypeScript,但似乎不是 CycleJS 要求的那樣。盡管如此,他能保持與原始文件相同的簡單結構,這一點深得人心。
在此我無法詳細說明 Wayne 因 TypeScript 和 CycleJS 做的一些改變,他沒有使用類定義 CycleJS 組件,其結構更像是在學校學的閉包結構。
export function App(sources: Sources): Sinks { const factorAndLean$ = sources.DOM.select('#the-svg') //... const args$ = xs.combine(factorAndLean$, xs.periodic(500) //... const pythagoras$ = Pythagoras(args$); const vtree$ = pythagoras$.map(x =gt; div(Styles.App, [ // ... return { DOM: vtree$ }; }
這需要時間適應。
Tags: SVG MVVM模式
文章來源:https://www.oschina.net/translate/animating-svg-no