有道互動內容引擎Ceramics的業務實踐

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"前言","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Ceramics 是一款 HTML5 互動內容引擎,主要爲有道互動教學場景提供 Web 互動內容開發解決方案幫助開發者快速生產高質量的題目內容。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文將從業務場景出發,介紹 Ceramics 的技術實現,並講述其如何高效地爲高質量的互動內容生產賦能,希望能給對 Web 互動技術感興趣的前端開發人員提供一些參考。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"一、背景","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1. 互動內容","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"互動內容是有道在線教學中的重要組成部分,應用場景廣泛例如:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"直播課堂","attrs":{}},{"type":"text","text":"中,通過教師端控制向學生端推送互動內容,學生在觀看直播或課後回放時,均可收到互動題目,作答後可提交答案至教師端;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"AI教學視頻","attrs":{}},{"type":"text","text":"中,穿插互動內容,根據用戶的作答結果播放不同的後續視頻片段;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"App、小程序","attrs":{}},{"type":"text","text":"中,課後通過複習闖關、自由練習、錯題本等形式讓用戶進行知識點鞏固,輔導老師在後臺可查看用戶答題情況進行專項指導;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"對外合作、市場活動、用戶學前測試","attrs":{}},{"type":"text","text":"等頁面中穿插互動題目來進行拉新引流。互動內容通過趣味性的交互過程、及時合理的反饋來激發學習興趣,吸引注意力,讓用戶在互動式、探索式的趣味課堂中更高效地獲取知識,鞏固學習。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/18/1870ae4e8bb8d3fc1d4f85806ce725bc.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"圖1:互動內容的應用場景","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2. 題目模板","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一道互動題目由題目模板和題目數據組成:研發人員根據教研需求開發出包含交互邏輯、動畫效果、正誤判斷的題目模板,教研根據題目模板的數據結構配置題目數據。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"目前團隊已開發近300個題目模板(下文簡稱題板),涵蓋","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong","attrs":{}}],"text":"填空、選擇、拖拽、繪圖、連線","attrs":{}},{"type":"text","text":"等題型。通過良好的接口設計和數據結構定義,一個題目模板可以適用於多種教學模式,題目模板之間也可以互相組合,形成交互更豐富的題目模板。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/50/502d98f0fd0ad9e5508c0522e95607dd.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"圖2:互動題目、題目模板、互動引擎之間的關係","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以圖3的兩類點選消除題目爲例,","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong","attrs":{}}],"text":"判題邏輯、場景風格、動畫效果","attrs":{}},{"type":"text","text":"等有差異,但題目交互相似度較高。基於此,將兩類題目用一個題目模板開發,保留較高的配置度,數據結構中定義背景圖片、選項圖片、動效形態、判題模式、題幹文本等字段,即可通過一個題目模板支持不同玩法的多道題目。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/2e/2e88539bbb4fe0b26b6f102ac6ff230f.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"圖3:一個題目模板支持多道題目","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"3.互動引擎","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"互動引擎是互動研發的基礎,一款好的互動引擎能大大降低研發成本。從上文的圖2可以看到,互動題目引擎提供了渲染、用戶交互、動畫、聲音、資源管理等能力,開發人員基於其可以開發出符合業務場景的題板,題板根據不同配置數據來渲染出定制化的題目內容。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了應對業務的高速發展,保障高效穩定地生產高質量的互動內容,少兒項目組的前端團隊自研了一款高性能、好拓展、易於開發的互動內容引擎:","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong","attrs":{}}],"text":"Ceramics","attrs":{}},{"type":"text","text":"。Ceramics 是基於TypeScript+React 開發的 HTML5 互動引擎,包含用戶交互、聲音、動畫、渲染、資源管理等功能,Web 開發人員利用其提供的 API 和組件,僅需較低的學習成本即可快速開發出 iOS、Android、Web、H5 多端適配的互動題板。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"自 Ceramics 投入使用後,目前已陸續爲少兒組的多學科多條業務線提供內容生產服務。下文將從架構設計、用戶交互、動畫效果、組件設計等方面展開介紹 Ceramics 的技術實現,希望能給對互動技術感興趣的前端開發人員提供一些參考。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"二、技術實現","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1. 基礎架構","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先我們先來看一下Ceramics的整體架構。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/4c/4cc3a30928ab25c2fe08521678a209b0.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"圖4:Ceramics架構","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"互動引擎簡單來說就是將題目數據渲染到界面的工具,由數據層、視圖層和業務邏輯層構成。數據帶動視圖,渲染引擎根據邏輯將數據渲染到畫布上。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"數據以","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}}],"text":" ","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong","attrs":{}}],"text":"JSON","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}}],"text":" ","attrs":{}},{"type":"text","text":"形式存儲於配置文件中,配置文件通常包含以下信息:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"UI層配置","attrs":{}},{"type":"text","text":":如頁面佈局,元素數量、尺寸、定位等。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"腳本配置","attrs":{}},{"type":"text","text":":如元素出現的時機,動畫播放的順序,用戶交互行爲,反饋等。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"資源配置","attrs":{}},{"type":"text","text":":如背景圖、音效、動效的資源地址等。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"題目數據配置","attrs":{}},{"type":"text","text":":題目具體的信息,如文案,題幹,圖片,答案,讀題語音等。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Web 開發人員的主要工作在視圖層和業務邏輯層,即如何將配置文件可視化,包括題板的核心邏輯、具體交互的實現、以及判題的計算。我們通常在 Canvas 區域實現複雜動畫效果和繪製,使用 DOM 實現佈局,使用 TSX+hooks 編寫邏輯代碼。爲了減少這一步的工作量,引擎本身提供了:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"動畫能力","attrs":{}},{"type":"text","text":":包括 CSS 動畫、JS 動畫、骨骼動畫、AE 動畫的顯示和控制,並將常用動效封裝成動效庫。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"音頻播放能力","attrs":{}},{"type":"text","text":":支持 mp3 音頻文件的播放和語音合成 tts 文件的播放。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"渲染能力","attrs":{}},{"type":"text","text":":基於 HTML、Canvas、WebGL 的 2D、3D 渲染。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"用戶交互","attrs":{}},{"type":"text","text":":封裝了拖拽、按壓、點擊、輸入、手寫、切割、3D交互等用戶交互事件。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"適配與兼容","attrs":{}},{"type":"text","text":":經過完善測試的移動端適配和兼容,支持 iOS 9(Safari 9),Android23(Chrome 44),Web、微信小程序等平臺。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"性能優化","attrs":{}},{"type":"text","text":":通過懶加載、資源打包優化等手段對引擎進行了優化,通過 Sentry 監控、3D 動畫幀率檢測等保障引擎的可靠性。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基於上述基礎能力,我們封裝了一些常用的原子組件和功能插件,如手寫板、計算器、算盤等,及遊戲化常用的的闖關、倒計時、引導動畫等;還封裝了通用題板組件,實現拖拽、選擇、填空、連線等基礎題型的邏輯和交互。原子組件之間可互相組合封裝成功能插件,通用題板組件和功能插件可組合搭配形成業務題板組件。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上述能力和組件及詳細的接入文檔和開發文檔,降低了互動題板的開發門檻,Web開發人員無需提前學習 2D、3D、圖形學、遊戲開發的知識,也可以快速上手開發滿足業務需求的互動題板。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2、交互能力","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"交互能力是用戶和屏幕產生互動的基礎,常見的用戶交互包括拖拽、按壓、點擊、輸入、手寫、切割、3D 場景下的交互等,互動題板在 Web 端和移動端都可使用,需要同時考慮鼠標和手指觸摸的交互。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Ceramics 在交互能力的設計中,採用了遊戲開發中流行的","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":" ECS","attrs":{}},{"type":"text","text":"(entity-component-system)設計模式作爲底層架構。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ECS 的一個核心理念是","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"組合優於繼承,","attrs":{}},{"type":"text","text":"即將不變的部分使用繼承以方便複用,將多變的部分用組合來方便拓展。這樣的設計模式可以保證引擎的高擴展性。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在互動題目中,每一個元素(小動物、草地、石塊、氣球等)都是一個實體 entity,每個實體可以綁定一個或多個組件,每個組件描述不同的能力(移動、拖拽、點擊等),通過組合的方式給實體賦予一個或多個能力,也可以動態添加或刪除能力。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以一個最簡單的按鈕爲例,它本身是一個沒有任何能力的實體,用一個
元素表示。按鈕可以有按壓按鈕和開關按鈕,分別應用於不同場景。我們自定義了 usePress 的 hook 和 useToggle 的 hook ,統一處理 Web 端和移動端的 mouse 和 touch 交互,抹平瀏覽器間的差異,對外暴露簡潔 API ;基於 usePress 封裝了 useButton 組件,表示按鈕可被按壓的能力;基於 useToggle 和 useButton 封裝了 useToggleButton 組件,表示開關按鈕的基本能力。最後,給按鈕組件綁定 useButton 或 useToggle ,成爲 Button 組件和 ToggleButton 組件,即爲按鈕賦予了對應的能力。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f7/f7a8baa6d37e32234ba4c1257539b054.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"圖5:按鈕組件交互能力設計","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"例如拖拽的交互,如圖6所示,簡單來說就是物體拖到指定的熱區的行爲,正誤判斷就是在指定時機對熱區中的物體與答案設定中的物體進行比較。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e3/e30ae3f5862e13d79e0795e1a2256791.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"圖6:拖拽物體與熱區","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可拖拽物體、不可拖拽物體本質上都是同樣沒有能力的實體。將拖拽能力封裝於獨立的組件,處理 mouse 和 touch 時的移動行爲以及拖拽物體與熱區的碰撞檢測。給可拖拽物體賦予了 draggable 的能力,給目標熱區賦予 droppable 的能力,並通過拖拽組件暴露的接口進行後續邏輯處理,即可實現拖拽行爲。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除了交互能力的實現之外,ECS 的設計思想在 Ceramics 的很多模塊中都有體現,例如通過自定義的 hooks 來表示能力,可以實現更大程度的邏輯複用。Ceramics 中有很多自定義 hooks ,如 useQuestion hooks ,將做題流程整合在一起,包括校驗答案、根據校驗結果正誤反饋、累計錯誤次數等;useViewport hooks ,封裝移動端適配能力;useAnime hooks ,封裝了動畫操作能力,如播放、暫停、重播等。不同題板引入相同 hooks 可以統一代碼風格,提高開發效率,新的開發者也更好理解,易於上手。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/0d/0df36e80f56333462a6789dcef11153f.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"圖7:useQuestion hooks","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"3. 動畫效果","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"動畫是互動內容的一個重要組成部分。逼真趣味的動畫效果不僅能輔助展示教學內容,還可以幫助提升用戶沉浸感,吸引注意力,激發學習興趣,更好地留存用戶。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"常見的 Web 動畫方案主要有序列幀、CSS 動畫、JS 動畫、Canvas、WebGL 等,Ceramics 通過引入常用的動畫庫來支持多種動畫形式,針對業務場景和兼容進行二次封裝。對於一些簡單動畫,研發只需要簡單的API調用即可實現效果。一些前端實現成本較高的複雜動效,由設計師通過 AE、Spine 等工具製作,研發引入 Ceramics 中相應的組件即可實現動畫的渲染和控制。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"3.1 Lottie","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Lottie 是 Airbnb 推出的可應用與 Web, Android, iOS 等多個平臺的動效解決方案。動畫師用AE(After Effects)製作出動畫後,通過 AE Bodymovin 插件可以導出在移動端和 Web端渲染動畫的 json 文件。前端通過 Lottie 庫可以對包含動畫信息的 json 文件進行解析和渲染。Lottie 動畫使用簡單,高效高性能,效果還原能力強,跨平臺支持性好,在 iOS,Android, Web,小程序端都有良好的呈現。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d8/d8935597851ac9afd96ad9f8ee4452c5.gif","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"圖8:lottie動畫","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"3.2 Spine","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Spine 是遊戲開發中常用的2D 骨骼動畫製作工具,通過 Spine 製作的動畫可以導出爲 json + atlas + png 文件。Spine Runtime 是 Spine 官方提供的可以解析 spine 導出的動畫文件的庫,其中 HTML Canvas runtime 提供了 Web 端渲染 Spine 動畫的能力。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Spine 動畫可應用於不同的引擎和語言,因此在需要跨端協作的業務線,設計師可以用一套 spine 動畫適配不同應用,提升工作效率,避免重複勞動。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e2/e2a4baf08a153220f1f78761f0c723e3.gif","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"圖9:spine動畫","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"3.3 基於Anime.js的動效庫","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong","attrs":{}}],"text":"Anime.js","attrs":{}},{"type":"text","text":" 是一個輕量的 Javascript 動畫庫,可對 CSS 屬性、SVG、DOM 和 JavaScript 對象進行動畫。Anime.js 的內置交錯系統使複雜的動畫和重疊動畫變得簡單。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Ceramics 基於 Anime.js 開發了更符合業務需求的動效庫,封裝了題板通用的動效,如:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"強調(縮放、更換樣式、抖動、閃爍)","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"元素入場出場動畫(漸入、漸出、閃現、掉落)","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"元素入場出場動畫(漸入、漸出、閃現、掉落)","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"動效庫也是獨立發佈的 ","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong","attrs":{}}],"text":"npm","attrs":{}},{"type":"text","text":" 包,可以服務於多個工程,提高開發效率。項目中直接引入@ceramics-ae中的動效組件使用,給","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong","attrs":{}}],"text":"DOM","attrs":{}},{"type":"text","text":"元素添加動效名稱和參數配置即可實現效果。利用","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}}],"text":" ","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong","attrs":{}}],"text":"timeline","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":" ","attrs":{}},{"type":"text","text":"可以對單步動畫進行串聯,支持單次播放、循環播放、時間軸等功能。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"import { Teeter } from \"@ceramics-ae/core\"\nTeeter.play({ el }) //抖動\nStyleChange.play({el,config: {style: { color: \"#F5636C\",background: `center/100% no-repeat url(${OptionStyle[type].bg_wrong})`}}}) //更改css樣式\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"3.4 WEB 3D","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在Web端實現3D效果,當前最流行的技術就是 WebGL 。WebGL基於 OpenGL ,OpenGL 是一個跨平臺的 2D、3D 繪圖標準,WebGL 把 OpenGL 與 JS 結合在一起,從而在 Web 端展示 3D 場景和模型。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WebGL 功能強大,提供了大量的基礎繪圖 API ,但上手門檻較高,代碼的複雜度高,需要開發者具有前置的圖形學知識和數學知識。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Three.js 是一款 WebGL 開源引擎,對 WebGL 的底層渲染細節和複雜的數據結構進行了封裝,開發人員通過簡單的 API 調用即可配置燈光,頂點着色器、片元着色器,自動生成矩陣,視角控制、射線等,快速進行 3D 模型製作。我們基於 Three.js 製作了魔方、立體圖形等3D效果,開發簡單,效果還原度高,在移動端也能保持良好的性能和兼容。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/1d/1da69cca3f96afb8a715aa6177bad8e3.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"圖10:web 3D","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一組高質量的動效往往是視覺、動效師、前端開發合力的產物,開發者根據不同的應用場景、素材依賴、開發成本、性能和兼容性選擇不同的動畫方案,可以在表達出豐富的動畫效果的同時降低設計師與研發的協作成本,提高團隊工作效率。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"4. 題板組件設計","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Ceramics 作爲一款互動內容引擎,目標之一是高效地爲多業務方生產題目模板,我們希望每一個題目模板都能夠服務於更多業務方,當新的業務方有需求時可以更快地接入。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基於此,我們將每個題板封裝成題板組件,獨立開發,獨立發佈爲 npm 包;題板與業務解耦、題板與題板解耦,由上層業務方負責控制多個題板間的組合、切換等邏輯;題板組件也可以作爲子組件被其他題板引入,進行二次封裝,更大程度提高題板的複用性。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c8/c83526f6a9c61dcc57c3d51f302d3eac.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"圖11:題板組件與業務方的調用關係","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通常當上層業務方使用題板時,需要收集題板內部的狀態來實現外部的業務邏輯,例如:題板加載成功時去除過渡動畫和背景音樂、用戶答錯時計入錯題本、答對時切換下一題等,題板內部也需要根據外部的數據更新狀態,例如:用戶在不同的遊戲模式中進入題目,題板應切換對應的界面和邏輯。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"針對以上業務場景,題板組件定義了一套通用的接口:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/7e/7e52e9852e13b0864b1b62c8764a0fa5.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"圖12:題板組件接口","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上層業務方安裝 npm 包,按需引入題板,根據接口定義傳入 props 即可完成接入。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"import { CeramicsProvider, DragCetegoryQuestion } from \"@ceramics-math/core\"\n\n //設備配置數據\n\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"題板組件之間相互獨立,但又擁有相似的邏輯和組件依賴。爲了更好地重用代碼、管理項目依賴,我們引入了","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":" ","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong","attrs":{}}],"text":"Lerna","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":" ","attrs":{}},{"type":"text","text":"框架進行多包管理,將多個題板組件作爲子項目合併到一個父項目中,重複的代碼邏輯也抽取成該父項目下的另一個獨立子項目。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/75/75f3f06646020c0296a2642331d31981.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"圖13:多包管理的結構","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"多包管理帶來的","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong","attrs":{}}],"text":"收益","attrs":{}},{"type":"text","text":"主要有:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所有子項目可以共享同一份打包、測試配置;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"子項目內部可以像引入其他依賴包一樣引入其他子項目,Lerna 會自動識別並導向內部項目,實時同步更新;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"處理依賴包的依賴關係更加方便,通過 Lerna bootstrap 命令安裝所有子項目的依賴包,在安裝依賴時依賴提升,即把所有項目 npm 依賴文件都提升到根目錄下,避免相同依賴包在不同項目安裝多次。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過Lerna的命令可以很方便地執行所有組件的build、test和組件發佈流程,更新組件中相應的依賴版本號併發布,還會在組件發佈之後打 tag 以及生成 changelog。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此外,由於不同的題板在打包發佈配置、接口定義、移動端適配規則、組件依賴等存在較多的相似性,爲了方便新同事上手,避免接口遺漏,統一代碼風格和數據結構,我們還定義了初始化模板,引入自動化構建工具 Plop 協助快速創建題板組件。通過交互式命令行輸入,即可動態地在指定目錄下生成題板組件,包括初始化代碼和配置文件。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/cb/cbad27a2f590d12e1b41068d1238a249.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"圖14:交互式命令行","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/05/05d4698c89fa202c2f22fa8bf5c7bbbf.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"圖15:初始化模板與生成的文件","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如本章節所述,明確的接口定義、方便快捷的接入方式、多包管理和自動化構建的引入,使得上層業務方可以快速接入題板組件庫,題板組件的開發、迭代和維護也更加容易。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"5. 原子組件和插件","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了進一步提高開發效率,我們根據常見的業務場景,封裝了許多原子組件和功能插件,開發人員通過簡單的組件引入和傳參即可引入。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/5d/5d5951c5213c1ca970218f4a99685596.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"圖16:原子組件和功能插件","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們把界面中的最小元素理解爲","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong","attrs":{}}],"text":"原子組件","attrs":{}},{"type":"text","text":",如按鈕、標題、展示板、選項、進度條等,根據 props 的不同配置樣式和交互。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/6d/6dd376b79db77ed60ae339fee5579750.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"圖17:原子組件","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"功能插件由多個原子組件組合而成,耦合了一些業務邏輯,常用插件如手寫板、計算器、算盤等,及遊戲化常用的的闖關、倒計時、引導動畫等。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/21/2131669e164c25bd32250af548aa7746.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"圖18:功能插件","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根據不同業務場景,功能插件可以適配不同的UI和動效。下圖爲","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong","attrs":{}}],"text":"鍵盤組件","attrs":{}},{"type":"text","text":"在不同題板中的樣式:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/38/38fad08e4d1bf8dba31b60c5b757c3e1.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"圖19:鍵盤組件","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一個題板組件中會有多個原子組件和功能插件。原子組件和功能插件內部可能依賴其他原子組件,也可能會依賴一些通用的 hooks 、全局 Context 、動畫、 utils 等。原子組件和功能插件也是多包工程的子項目,可以獨立導出併發布爲 npm 包,不僅可被任意題板組件引入,也可被其他原子組件引入,還可以被引入到其他項目中。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"6. 通用題板","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除了上文中的原子組件和插件,Ceramics 還內置了通用題板組件來進一步提高開發效率。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通用題板爲一些最基礎的題目模板,如通用拖拽、通用選擇、通用填空、通用連線等。通用題板封裝了基本邏輯和交互,更多的業務邏輯則以數據的形式表達,這樣可以提高單個題目模板的複用性,使得一個通用題板支持N種交互和樣式相似度較高的題板需求。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過對多個題板的歸類和抽象,我們總結出來幾類基礎題板的基本要素:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如下圖中的多個拖拽題,由畫布、熱區、可拖拽物體、不可拖拽物體組成。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/87/87137171873ba09ac37dd9149021db70.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"圖20:拖拽題基本元素","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同理,選擇題板的基礎元素有畫布、可點擊物體、不可點擊物體;填空有畫布、物體、填空框、選項框;連線有畫布、可連線物體、不可連線物體。物體的類型包括圖片、動畫、文字等。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除了基礎元素外,題板的反饋也有相似之處:例如答對後出現正確動畫,答錯後出現錯誤動畫、連線匹配成功或拖拽正確時A物體消失替換成B物體等。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"與上文中提到的 ECS 設計模式一致,當給物體賦予了可拖拽、可點擊、可連線等交互能力,並給每次交互都添加相應的反饋,即可實現一個題目的基本互動。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基於此,我們抽象出了通用題板,在題板中處理元素渲染、用戶交互和提交判斷邏輯,至於頁面中哪些元素可點擊、哪些可拖拽、每個元素的數量、樣式、位置和動畫,不同時機的反饋等,都可以用數據來表達,通過數據帶動視圖進行渲染。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們封裝了一些基礎類,比較常用的有 Entity(物體),Feedback(反饋),Area(熱區)、ShowCondition(出現時機)等。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"頁面中的所有元素都屬於 Entity 類,EntityType 表示物體的類型,有圖片、動畫、文本、標題、提交按鈕等,不同類型的物體有不同的配置項和渲染方式。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"給entity添加不同的屬性即可賦予其不同的交互,如 ","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong","attrs":{}}],"text":"clickable ","attrs":{}},{"type":"text","text":"表示時可被點擊的選項,","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong","attrs":{}}],"text":"draggable ","attrs":{}},{"type":"text","text":"表示可被拖拽的物體,","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong","attrs":{}}],"text":"droppable ","attrs":{}},{"type":"text","text":"表示可被放置的熱區,droppable 的熱區還可以配置形狀、容量、吸附精確度等屬性。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"export interface Entity extends Rectangle {\n id: string | number // 物體的唯一id\n name: string // 物體的名稱\n entityType?: EntityType // 物體的類型\n draggable?: boolean // 可拖拽\n droppable?: boolean // 可放置\n clickable?: boolean // 可點擊\n linkable?: boolean //可連線\n zIndex?: number // 層級,0-999,越大層級越高\n show?: ShowCondition // 何時顯示,默認always\n feedbacks?: Feedback[]\n [key: string]: any\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"feedbacks 字段用來配置一組反饋事件,反饋主要由時機和行爲定義。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"\nexport interface Feedback {\n type: FeedbackType | string // 反饋時機, FeedbackType和AND,OR組合而成的字符串\n action: FeedbackAction // 反饋的行爲\n data: FeedbackData // 反饋行爲需要的數據\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"反饋的時機(type)有:點擊、放入熱區、熱區已滿、拖對熱區、拖錯熱區、開始連線、連線結束、動畫播放完成、整體判題正確、整體判題錯誤等。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"反饋的行爲(action)有:顯示物體、隱藏物體、播放音頻、替換圖片、檢查答案等。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同一個動作可能觸發多個反饋,一個反饋也可以在不同的觸發時機被觸發。通過Feedbacks可以把反饋步驟串聯起來,類似於動畫腳本,使得一個常規的題板具有更豐富的玩法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"經過上述拆解,通用題板的設計思路也逐漸清晰了起來。以下圖中的通用選擇題板爲例:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/55/550bebc46be7d46324dc9156d0a45811.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"圖21:通用選擇題板","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong","attrs":{}}],"text":"選擇題","attrs":{}},{"type":"text","text":"的核心交互流程爲:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"black"}}],"text":"點擊可點元素,觸發點擊反饋;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"black"}}],"text":"滿足提交條件,觸發提交反饋,進行判題,觸發正/誤反饋;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"black"}}],"text":"滿足題目結束條件,題目結束不可作答,否則可以繼續作答。","attrs":{}}]}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong","attrs":{}}],"text":"提交答案","attrs":{}},{"type":"text","text":"的時機主要有3種:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"black"}}],"text":"無提交按鈕時,點擊可點元素立即提交;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"black"}}],"text":"無提交按鈕時,點擊元素數量與允許提交的數量相等後立即提交;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"black"}}],"text":"有提交按鈕按鈕時,點擊提交按鈕提交。","attrs":{}}]}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"選擇題支持單選、多選、部分選擇、無正確答案的選擇(如投票)。因此","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong","attrs":{}}],"text":"判斷答案的條件","attrs":{}},{"type":"text","text":"也不同:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"black"}}],"text":"單選 & 多選;用戶答案與題目正確答案一致;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"black"}}],"text":"部分選擇:用戶答案與題目正確答案部分一致;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"black"}}],"text":"無正確答案的選擇:任意選項均爲正確。","attrs":{}}]}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面是通用選擇的基本數據結構:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"interface QuestionConfig {\n canvas: Canvas //畫布\n answer: Answer //正確答案\n entities: Entity[] //物體\n submit?: SubmitEntity //提交按鈕\n audio?: { //音效\n correct?: string\n wrong?: string\n click?: string\n }\n aeTemplate?: string //動畫模板\n maxCheckNum?: number //最大錯誤次數\n allowMultipleChoice?: boolean //是否允許多選\n allowPartChoice?: boolean // 是否允許部分選擇\n showHint?: boolean // 是否出現引導\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如上文所述,題目由題板和數據組成,通用題板中主要的業務邏輯包括:處理上述渲染和交互,定義配置數據的數據結構,根據配置數據進行相應的展示和反饋。例如,根據 entities 渲染物體,給 clickable 的物體綁定點擊事件,根據 entity 中的 feedbacks 字段展示反饋;根據 submit 字段處理提交邏輯和提交按鈕的渲染;根據 answer、allowPartChoice、allowMultipleChoice 等字段進行題目判斷。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過數據驅動頁面的渲染,使得一個題板可以滿足大部分同類型的教研需求,教研可以在配套的題板可視化配置界面中配置數據來創作新的題板,無需研發介入;研發人員也可以基於通用題板進行二次封裝,通用題板可以作爲組件引入到其他題板中,多個通用題板可以組合形成新的題板,大幅降低了新題板的開發成本。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"三、總結與展望","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文主要介紹了有道互動題目引擎Ceramics在","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong","attrs":{}}],"text":"架構設計、用戶交互、動畫效果、組件設計","attrs":{}},{"type":"text","text":"等方面的技術實現,從研發視角講述互動題目的生產過程,以及題目引擎是如何讓互動內容的生產鏈路更高效地運轉起來的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"目前 Ceramics 還在成長階段,未來我們將持續迭代,不斷豐富開發工具鏈。我們將打造低代碼開發平臺,拼搭式可視化開發題板,使得編寫交互邏輯更加容易;打通設計圖到代碼的轉換通道,開發素材庫,優化 UI 和研發的合作體驗;不斷提高高效運行和渲染能力,向遊戲化邁進,支持更復雜的遊戲玩法,更炫酷的動畫效果;增加實時互動,多人對戰的能力,進一步提升直播課等場景的互動性與趣味性。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們的願景是是打造一款高性能的 Web 互動引擎,讓前端工程師更低成本地開發高質量的互動內容。未來我們將不斷擴大 Ceramics 的影響力,爲更多業務賦能。","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章