自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

將你的 Virtual dom 渲染成 Canvas

開發(fā) 后端
一個(gè)基于 vue 的 virtual dom 插件庫,按照Vue render 函數(shù)的寫法,直接將 Vue 生成的 Vnode 渲染到 canvas 中。支持常規(guī)的滾動(dòng)操作和一些基礎(chǔ)的元素事件綁定。

項(xiàng)目概述
一個(gè)基于 vue 的 virtual dom 插件庫,按照Vue render 函數(shù)的寫法,直接將 Vue 生成的 Vnode 渲染到 canvas 中。支持常規(guī)的滾動(dòng)操作和一些基礎(chǔ)的元素事件綁定。

demo 地址:https://muwoo.github.io/vnode2canvas/

背景
從一個(gè)小的需求說起:某一天,產(chǎn)品提了一個(gè)這樣的需求,需要制作一個(gè)微信活動(dòng)頁,活動(dòng)頁可以分享包含用戶相關(guān)信息的圖片。這些信息是需要從接口取的,而且每個(gè)人都不一樣。第一次碰到這種需求的時(shí)候,基本上都會(huì)去手?jǐn)] canvasAPI 去做渲染功能,這種情況的步驟大致如下:

  1. 寫一大串 dom template 標(biāo)簽
  2. 渲染 template 成 dom 標(biāo)簽
  3. 開始捕捉 dom 元素,繪制 canvas
  4. canvas 渲染圖片

面臨的主要問題是復(fù)用性太差,其次是性能上也有問題,用戶看到的界面不一定和正式渲染出的界面一致,可能存在渲染差異。作為一個(gè)有追求的前端,當(dāng)然得想想看有沒有更好的法子。于是乎了解到了一個(gè) html2canvas 這樣一個(gè)庫。但是總是感覺還是要轉(zhuǎn)成 dom 再去繪制,而且感覺性能和穩(wěn)定性也不是很好。

我們知道 vue 通過 vnode 實(shí)現(xiàn)了對(duì)不同端的渲染工作,那有沒有可能通過 vnode 實(shí)現(xiàn)對(duì) canvas 的渲染呢?也就是說,沒有 vnode -> html -> canvas 而是直接vnode -> canvas。同時(shí)利用 vue 的數(shù)據(jù)驅(qū)動(dòng),來達(dá)到繪制的數(shù)據(jù)驅(qū)動(dòng)。想法有了,下面開始實(shí)施。

調(diào)研
這篇文章對(duì)此有詳細(xì)的介紹:60 FPS on the mobile web 這里簡單的概括一下:

canvas 是一種立即模式的渲染方式,不會(huì)存儲(chǔ)額外的渲染信息。Canvas 受益于立即模式,允許直接發(fā)送繪圖命令到 GPU。但若用它來構(gòu)建用戶界面,需要進(jìn)行一個(gè)更高層次的抽象。例如一些簡單的處理,比如當(dāng)繪制一個(gè)異步加載的資源到一個(gè)元素上時(shí)會(huì)出現(xiàn)問題,如在圖片上繪制文本。

在 HTML 中,由于元素存在順序,以及 CSS 中存在 z-index,因此是很容易實(shí)現(xiàn)的。dom 渲染是一種保留模式,保留模式是一種聲明性 API,用于維護(hù)繪制到其中的對(duì)象的層次結(jié)構(gòu)。保留模式 API 的優(yōu)點(diǎn)是,對(duì)于你的應(yīng)用程序,他們通常更容易構(gòu)建復(fù)雜的場(chǎng)景,例如 DOM。通常這都會(huì)帶來性能成本,需要額外的內(nèi)存來保存場(chǎng)景和更新場(chǎng)景,這可能會(huì)很慢。

看來 canvas 繪制頁面的研究,很久之前就已經(jīng)有人付出過研究了。而且性能還是很不錯(cuò)的。那我們更要試試看,到底我們的想法能不能實(shí)現(xiàn)了!越來越期待....

開始
canvas 的渲染其實(shí)也是一種嘗試,既然前人以及做了充分的實(shí)踐,那么我們便站在巨人的肩膀上去基于 vue 來實(shí)現(xiàn)一個(gè)數(shù)據(jù)驅(qū)動(dòng)的canvas渲染。說做就做!(我們這里只提供思路,不做具體實(shí)現(xiàn)細(xì)節(jié)的討論,因?yàn)閷?shí)現(xiàn)起來有點(diǎn)復(fù)雜,如果有興趣可以參考我的項(xiàng)目實(shí)現(xiàn),或者一起交流探討 )

處理 vnode
熟悉 Vue 源碼的應(yīng)該都知道,Vue 通過 render 函數(shù),傳入 createElement 方法來構(gòu)造出一個(gè) vnode,通過發(fā)布--訂閱模式來實(shí)現(xiàn)對(duì)數(shù)據(jù)的監(jiān)聽,重新生成 vnode。vnode 最后被轉(zhuǎn)成各平臺(tái)所需的視圖。而我們要做的就是在 vnode 這一層開始。所以,我們基于 Vue 源碼的方式,實(shí)現(xiàn)一個(gè)監(jiān)聽函數(shù),并混入 Vue 實(shí)例中:

  1. Vue.mixin({ 
  2.     // ... 
  3.     created() {      if (this.$options.renderCanvas) { 
  4.         // ... 
  5.         // 監(jiān)聽vnode中引用的變化,重新渲染 
  6.         this.$watch(this.updateCanvas, this.noop) 
  7.         // ... 
  8.       }    },    methods: {      updateCanvas() {        // 模擬Vue render 函數(shù) 
  9.         // 尋找實(shí)例中定義的 renderCanvas 方法,并傳入createElement方法 
  10.         let vnode = this.$options.renderCanvas.call(this._renderProxy, this.$createElement) 
  11.       }}) 

這樣我們就可以愉快的在組件內(nèi)部使用:

  1. renderCanvas (h) { 
  2.   return h(...) 

canvas 元素處理
render 的 vnode 我們需要做額外的一些約束,也就是說我們需要怎么樣的渲染標(biāo)簽,來渲染對(duì)應(yīng)的 canvas 元素(舉個(gè) ):

  1. view/scrollView/scrollItem --> fillRect
  2. text --> fillText
  3. image --> drawImage

其中這些元素類分別都繼承于一個(gè) Super 類,并且由于它們各有不同的展示方式,因此它們分別實(shí)現(xiàn)自己的 draw 方法,做定制化的展示。

繪制對(duì)象的布局機(jī)制實(shí)現(xiàn)
繪制 canvas 布局最基礎(chǔ)的寫法是為 canvas 元素傳入一系列坐標(biāo)點(diǎn)和相關(guān)的基礎(chǔ)寬高,這樣寫到實(shí)際項(xiàng)目中可能是這樣的:

  1. renderCanvas(h) { 
  2.   return h('view', { 
  3.      style: { 
  4.        left: 10, 
  5.        top: 10, 
  6.        width: 100, 
  7.        height: 100 
  8.      }  })} 

這樣寫確實(shí)有點(diǎn)不方便維護(hù),目前有好幾種解決方案,一種是使用 css-layout去做管理。css-layout 支持的轉(zhuǎn)換屬性如下:

這樣也只是做了一層轉(zhuǎn)換,幫我們更好的用 css 思維去寫 canvas,但是如果我們很不爽 css in js 的寫法,其實(shí)我們還可以寫一個(gè)webpack loader 來加載外部 css:

  1. const css = require('css'
  2. module.exports = function (source, other) { 
  3.   let cssAST = css.parse(source) 
  4.   let parseCss = new ParseCss(cssAST) 
  5.   parseCss.parse()  this.cacheable(); 
  6.   this.callback(null, parseCss.declareStyle(), other); 
  7. };class ParseCss { 
  8.   constructor(cssAST) { 
  9.     this.rules = cssAST.stylesheet.rules 
  10.     this.targetStyle = {} 
  11.   }  parse () {    this.rules.forEach((rule) => { 
  12.       let selector = rule.selectors[0] 
  13.       this.targetStyle[selector] = {} 
  14.       rule.declarations.forEach((dec) => { 
  15.         this.targetStyle[selector][dec.property] = this.formatValue(dec.value) 
  16.       })    })  }  formatValue (string) { 
  17.     string = string.replace(/"/g, '').replace(/'/g, '') 
  18.     return string.indexOf('px') !== -1 ? parseInt(string) : string 
  19.   }  declareStyle (property) {    return `window.${property || 'vStyle'} = ${JSON.stringify(this.targetStyle)}` 
  20.   }} 

簡單的來說:主要也就是將 css 文件轉(zhuǎn)成 AST 語法樹,之后再對(duì)語法樹做轉(zhuǎn)換,轉(zhuǎn)成 canvas 需要的定義形式,并以變量的形式注入到組件中。

實(shí)現(xiàn)列表滾動(dòng)
如果我們的元素很多,需要滾動(dòng)時(shí),我們必須解決 canvas 內(nèi)部元素滾動(dòng)的問題。這里我選擇了使用Zynga Scroller 來模擬用戶滾動(dòng)方法,通過他返回的滾動(dòng)坐標(biāo)點(diǎn),來對(duì) canvas 進(jìn)行重繪。有興趣的可以參考這里我的實(shí)現(xiàn):

https://github.com/muwoo/vnode2canvas/blob/master/src/core/shape/scrollView.js

事件模擬
對(duì)于 click,touch 等 dom 事件的模擬,我們采用的方案是根據(jù)點(diǎn)擊區(qū)域進(jìn)行檢測(cè),并找出最底層的元素,遞歸尋找父元素并觸發(fā)對(duì)應(yīng)事件處理程序,從而模擬事件冒泡。詳細(xì)的實(shí)現(xiàn)可以參考這里:

https://github.com/muwoo/vnode2canvas/blob/master/src/core/event.js

最后
canvas 繪制頁面也是一種創(chuàng)新的嘗試,希望這里的研究對(duì)你有啟發(fā),也歡迎你的 PR。這里也做了很多性能優(yōu)化,限于篇幅不在贅述了,有興趣也可以一起探討。

最后:它并不意味著完全取代基于DOM的渲染,這仍然需要文本輸入,復(fù)制/粘貼,可訪問性和SEO。出于這些原因,我們可以使用canvas和基于DOM的渲染的組合。

 

責(zé)任編輯:姜華 來源: 今日頭條
相關(guān)推薦

2022-05-06 19:42:53

DOM

2023-02-28 11:43:35

2022-08-14 23:04:54

React前端框架

2022-12-12 09:01:13

2021-06-21 07:36:32

Virtual DOMDOMvue.js

2021-05-26 05:22:09

Virtual DOMSnabbdom虛擬DOM

2021-07-04 10:07:04

Virtual DO閱讀源碼虛擬DOM

2018-10-22 16:21:50

ChromeHTMLCSS

2024-01-15 09:23:16

框架方式原生

2023-09-25 10:26:05

DOMCSS

2012-11-23 17:20:43

Linux服務(wù)器

2021-04-20 20:09:56

LinuxScrcpy桌面應(yīng)用

2021-12-12 18:31:35

VNode組件Vue3

2021-02-04 13:00:40

樹莓派Linux

2010-06-28 09:53:17

Linux操作系統(tǒng)U盤

2010-09-09 17:19:07

HTML DOMXML DOM

2021-08-31 08:32:40

開源項(xiàng)目開發(fā)

2010-09-28 10:24:50

HTML DOMXML DOM

2020-11-25 10:42:57

Python代碼工具

2017-05-10 16:09:12

MySQL數(shù)據(jù)庫查詢
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)