<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在Fiberplane,我們最近遇到了一個有趣的挑戰:我們正在使用的富文字編輯器庫已經過時了。我們曾經使用Slate.js——一個很好的編輯器——但是當我們為共同作業編輯實現我們自己的富文字基元時,我們發現我們自己的基元和Slate的資料模型之間的脫節是一個阻礙因素。所以我們開始思考——如果我們建立自己的富文字編輯器(RTE, Rich Text Editor)會怎樣?
從一個非常高層次的角度來看,一個富文字編輯器是由兩個部分組成的。
我們在檢視中使用了Slate,但結果是它也拉入了自己的資料模型。如果我們可以直接在React中實現檢視,我們可以大大簡化我們的堆疊,並完全控制它的每個方面。缺點是什麼?RTEs因為需要支援複雜的使用者互動而臭名昭著,而現在我們需要自己處理每一個互動。
在這篇文章中,我們將討論我們所面臨的挑戰以及我們如何解決這些問題。
我們的產品是一個共同作業式的筆電編輯器。筆電是一個基於塊的編輯器,由不同型別的單元組成,從文字單元到圖片和圖表。因此,我們確定了一個資料模型,它既有利於我們的共同作業功能,也有利於為我們在單元格內使用的任何富文字欄位提供動力的RTE。在這篇文章中,我們將重點討論TextCell
。
struct TextCell { pub id: String, pub content: String, pub formatting: Option<Formatting>, }
這裡的content
只是純文字內容,而formatting
是將純文字變成富文字的東西。"多汁"的部分都在格式化型別裡面。
type Formatting = Vec<AnnotationWithOffset>; struct AnnotationWithOffset { annotation: Annotation, offset: u32, } enum Annotation { StartBold, EndBold, StartItalics, EndItalics, StartLink { url: String }, EndLink, /* more like these... */ }
正如你所看到的,這只不過是一個註釋列表,它定義了要應用的格式化型別和它開始的偏移量。我們有意不選擇類似於HTML的樹狀結構,因為格式化範圍可以重疊,這將導致複雜的樹狀操作。此外,每個註釋只有一個偏移量的簡單性使我們很容易實現我們用於共同作業的操作轉換(OT)演演算法。
隨著資料模型的出現,也帶來了與之互動的程式碼。當你在一個單元格中打字時,我們在哪裡插入新打的字元?這如何影響content
和相關的formatting
?如果你在一個選擇上切換格式,應該發生什麼?如果你將一個單元格從中間分割開來,又該怎麼辦?所有這些以及更多都在Rust的核心邏輯中實現。
你要知道,無論如何我們都需要這些邏輯,因為我們的OT演演算法也需要它。但現在我們也能用同樣的原語來驅動我們的編輯器。
為了使這個邏輯易於測試,它被實現為純函數,我們在TypeScript的Redux reducer中呼叫。我們建立了fp-bindgen來生成Rust程式碼和呼叫它的TypeScript程式碼之間的繫結關係。
為了適應RTE(當我們還在使用Slate時還不需要),我們不得不自己引入一段邏輯,就是遊標管理。例如,當用戶按下左方向鍵時,我們分派一個MoveCursor
動作,其有效載荷如下。
struct MoveCursorPayload { pub delta: i32, pub extend_selection: bool, pub unit: CursorUnit, }
delta
指定遊標是向前還是向後移動,通過指定一個1
或-1
的值。extend_selection
屬性是在使用者按住Shift
鍵時使用的,用來擴充套件當前的選擇,或者在還沒有選擇的情況下建立一個。這個unit
決定了我們是按Unicode字母群(使用者通常稱之為 "字元")還是按單詞移動遊標,用於使用者按住Ctrl
/⌥
鍵時。然後,我們的Rust還原器會處理這些動作,並處理所有的邊緣情況,包括確保遊標不會出現在@
的中間。
在我們RTE的大部分開發過程中,我們的編輯器甚至不是一個編輯器。至少從瀏覽器的角度來看不是。這是因為瀏覽器通常只識別兩種型別的編輯器:純文字編輯器,如<input>
和<textarea>
元素,以及使用一種叫做contenteditable
的屬性建立的自由格式編輯器。我們的編輯器兩者都不是。
我們在最終版本中仍然使用contenteditable
屬性,因為我們很快會討論一些實際的影響,但我們有意識地決定儘可能少地依賴它。這對我們最初構建RTE的方式產生了深遠的影響,你將在本節中看到。
如果我們最初的版本根本沒有使用contenteditable
,那麼我們怎麼能夠建立一個富文字編輯器?從使用者的角度來看,RTE只不過是一個看起來像文字欄位的東西,有一個遊標,允許他們輸入任何他們喜歡的內容。
所以我們建立了一個普通的React元件,並根據單元格的content
和formatting
生成了富文字內容,然後使用React.createElement()
插入實際的元素,這些元素只是一個應用了樣式的<span>
元素的平面列表(偶爾會有<a>
元素灑在連結上)。然後,我們新增了必要的事件處理程式來捕捉使用者的互動,這又將再次呼叫資料模型上的適當邏輯。
那麼使用者的遊標呢?只是另一個我們自己插入的小React元件。我們會在useLayoutEffect()
勾點中測量它需要的位置,然後根據這個來定位它。
所以......很簡單,很容易,對嗎?好吧,我們現在需要處理的大量的互動使這成為一個重大的挑戰。例如,讓我們再看一下游標導航。上一節中的例子顯示瞭如何向左和向右移動遊標。但是如果使用者按了向下的箭頭,他們的遊標最終會在哪兩個字元之間呢?這不是一個簡單的問題,因為保持遊標的垂直位置需要測量上面那一行的字元的位置。但你如何定義什麼是 "上面那一行"?無論是content
還是formatting
都不包含這些資訊。然後記住我們還必須支援選擇。還有滑鼠互動...
這當然會讓人感到不知所措,在開發過程中,可能很難保持對哪些工作和哪些不工作的概述。而這正是我們覺得最初沒有contenteditable
的工作很好的原因。我們自己做所有的事情,使我們非常清楚自己的位置。任何不工作的互動都是我們仍然需要實現的。沒有什麼會意外地工作,因為瀏覽器為我們解決了這個問題--瀏覽器在這裡處於次要地位。
當然,對於最終的版本,很難繞過使用contenteditable
。這是因為如果沒有它,瀏覽器擴充套件將無法識別你的編輯器。而移動瀏覽器甚至會頑固地拒絕調出螢幕鍵盤......
所以我們確實需要contenteditable
,但是還有一個問題。React不支援對已啟用contenteditable
的元素的內容進行修補。這是有原因的:contenteditable
基本上是告訴瀏覽器去玩吧。這就像一個沒有規則的操場。
React並不喜歡這樣。它依靠虛擬DOM來決定它需要如何更新實際的DOM,但當瀏覽器可以在它不知情的情況下把地毯從它下面拉出來並更新實際的DOM時,這種方法就陷入了困境。這也是我們一開始就避免的原因。為了在更新我們的資料模型時能夠保留使用者的意圖(OT演演算法的一個重要方面),最好是瞭解導致任何變化的互動。但是,如果你試圖理解瀏覽器對DOM在內容可編輯元素中的變化,你最多隻能是猜測。
所以我們借鑑了React的玩法,實現我們自己的差異演演算法。但我們不是針對虛擬DOM進行差分,而是在useLayoutEffect()
勾點函數中針對真實DOM進行差分和修補。這相對簡單,因為我們的用例非常專業,而且它還有一個好處,如果真實DOM中發生任何意外(可能是由於瀏覽器擴充套件),我們的演演算法將簡單地將檢視恢復到我們基於資料模型的預期。
上述所有內容可能會讓你對編輯器的工作原理有一個較高的認識,但魔鬼是在細節中的。下面是我們需要解決的一些小問題。
Selection
物件,並通過這種方式設定一個(透明的)本地遊標。然後我們使用getBoundingClientRect()
來測量瀏覽器渲染遊標的位置,然後我們就可以在那裡定位我們自己的遊標。建立你自己的富文字編輯器是一項艱鉅的任務,但只要有正確的架構和良好的規劃,它肯定是可以做到的。如果你發現自己處於必須選擇或開發一個富文字編輯器的位置,我們希望你能發現這篇文章的有用資訊。
注:特別感謝技術指導dazhao(趙達)對本文翻譯的審閱指正。
作者:Arend van Beelen
原文連結:Creating a Rich Text Editor using Rust and React
以上就是Rust+React建立富文字編輯器的詳細內容,更多關於Rust React富文字編輯器的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45