<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          將 Vue 渲染到嵌入式液晶屏

          共 8176字,需瀏覽 17分鐘

           ·

          2020-12-08 22:10


          作者:null仔
          來源:SegmentFault 思否社區(qū)




          前言


          之前看了雪碧大佬的將 React 渲染到嵌入式液晶屏覺得很有意思,React能被渲染到嵌入式液晶屏,那Vue是不是也可以呢?所以本文我們要做的就是:


          如標題所示,就是將Vue渲染到嵌入式液晶屏。這里使用的液晶屏是0.96 寸大128x64分辨率的SSD1306。要將Vue渲染到液晶屏,我們還需要一個橋梁,它必須具備控制液晶屏及運行代碼的能力。而樹莓派的硬件對接能力和可編程性天然就具備這個條件。最后一個問題來了,我們用什么技術(shù)來實現(xiàn)呢?



          這里我選擇了 Node.js。原因:


          • Atwood 定律:“任何可以使用 JavaScript 來編寫的應用,最終會由 JavaScript 編寫。” ?
          • 驅(qū)動硬件我大 Node.js 一行npm install走天下。?


          這個有趣的實踐可拆分為這幾個步驟:

          • 在 Node.js 運行 Vue
          • 樹莓派連接屏幕芯片
          • Node.js 驅(qū)動硬件

          Talk is cheap,Let's Go!!!



          跨端渲染


          無論是 基于 React 的 React Native 宣稱的「Learn Once, Write Anywhere」,還是基于 Vue 的 Weex 宣稱的「Write Once, Run Everywhere」口號,本質(zhì)上強調(diào)的都是它們跨端渲染的能力。那什么是跨端渲染呢?

          • React: ReactNative Taro ...

          • Vue: Weex UniApp ...

          • 各種五花八門的前端框架紛紛襲來,前端工程師們紛紛抱怨學不動了~


          老板們看到紛紛笑嘻嘻, App 單,前端分,小程序單,前端吞,PC/H5,前端昏。skr~

          這些跨平臺框架原理其實都大同小異,選定 Vue/React 作為 DSL,以這個 DSL 框架為標準在各端分別編譯,在運行時,各端使用各自的渲染引擎(Render Engines)進行渲染,底層渲染引擎中不必關(guān)心上層 DSL 的語法和更新策略,只需要處理 JS Framework 中統(tǒng)一定義的節(jié)點結(jié)構(gòu)和渲染指令。也正是因為這一渲染層的抽象,使得跨平臺/框架成為了可能。


          Vue 和 React 現(xiàn)在都實現(xiàn)了自定義渲染器,下面我們簡單介紹一下:

          React Reconciler


          React16 采用新的 Reconciler,內(nèi)部采用了 Fiber 的架構(gòu)。react-reconciler模塊正是基于 v16 的新 Reconciler 實現(xiàn),它提供了創(chuàng)建 React 自定義渲染器的能力.

          const?Reconciler?=?require("react-reconciler");

          const?HostConfig?=?{
          ??//?You'll?need?to?implement?some?methods?here.
          ??//?See?below?for?more?information?and?examples.
          };

          const?MyRenderer?=?Reconciler(HostConfig);

          const?RendererPublicAPI?=?{
          ??render(element,?container,?callback)?{
          ????//?Call?MyRenderer.updateContainer()?to?schedule?changes?on?the?roots.
          ????//?See?ReactDOM,?React?Native,?or?React?ART?for?practical?examples.
          ??},
          };

          module.exports?=?RendererPublicAPI;

          Vue createRenderer


          vue3 提供了createRender API,讓我們創(chuàng)建自定義渲染器。

          createRenderer 函數(shù)接受兩個泛型參數(shù):HostNode 和 HostElement,對應于宿主環(huán)境中的 節(jié)點 和 元素 類型。

          自定義渲染器可以傳入特定于平臺的類型,如下所示:

          import?{?createRenderer?}?from?'vue'
          const?{?render,?createApp?}?=?createRenderer({
          ??patchProp,
          ??...nodeOps
          })




          在 Node.js 上運行 Vue


          SFC To JS





          要將 Vue 渲染到液晶屏,我們首先需要讓 Vue 能運行在 Node.js 上,但是上面這個 SFC 是沒辦法被 Node.js 識別的,它只是 vue 的編程規(guī)范,是一種方言。所以我們需要做的是先將 SFC 轉(zhuǎn)為 js。這里我使用 Rollup 打包將 SFC 轉(zhuǎn)為 JS(相關(guān)配置這里就不啰嗦了,貼個傳送門)。到了這一步,Node.js 就能成功運行打包后的 js 代碼了,這還不夠,這時候 Vue 組件的狀態(tài)更新是沒辦法同步到 Node.js 的。

          Create Custom Renderer


          組件狀態(tài)更新我們需要通知 Node.js 更新并渲染液晶屏內(nèi)容,我們需要創(chuàng)建自定義的"更新策略"。這里就需要用到了我們前面提到的自定義渲染器:createRenderer API。下面我們簡單介紹下我們相關(guān)使用:

          //?index.js
          //?自定義渲染器
          import?{?createApp?}?from?"./renderer.js";
          //?組件
          import?App?from?"./App.vue";
          //?容器
          function?getContainer()?{
          ??//?...
          }
          //?創(chuàng)建渲染器,將組件掛載到容器上
          createApp(App).mount(getContainer());
          //?renderer.js

          import?{?createRenderer?}?from?"vue";
          //?定義渲染器,傳入自定義nodeOps
          const?render?=?createRenderer({
          ??//?創(chuàng)建元素
          ??createElement(type)?{},
          ??//?插入元素
          ??insert(el,?parent)?{},
          ??//?props更新
          ??patchProp(el,?key,?preValue,?nextValue)?{},
          ??//?設(shè)置元素文本
          ??setElementText(node,?text)?{},
          ??//?以下忽略,有興趣的童鞋可自行了解
          ??remove(el)?{},
          ??createText(type)?{},
          ??parentNode(node)?{},
          ??nextSibling(nide)?{},
          });

          export?function?createApp(root)?{
          ??return?render.createApp(root);
          }

          vue 渲染器默認實現(xiàn)了 Web 平臺 DOM 編程接口,將 Virtual DOM 渲染為真實 DOM。但是這個渲染器只能運行在瀏覽器中,不具備跨平臺能力。所以我們必須重寫 nodeOps 相關(guān)鉤子函數(shù),實現(xiàn)對應宿主環(huán)境元素的增刪改查操作。接下來我們定義一個適配器,來實現(xiàn)相關(guān)邏輯。

          Adapter


          在實現(xiàn)前,我們先來理一下我們要實現(xiàn)的邏輯:

          • 創(chuàng)建元素實例 (create)
          • 將元素實例插入容器,由容器進行管理 (insert)
          • 狀態(tài)改變時,通知容器進行更新 (update)

          //?adapter.js

          //?文本元素
          export?class?Text?{
          ??constructor(parent)?{
          ????//?提供一個父節(jié)點用于尋址調(diào)用更新?(前面提到狀態(tài)更新由容器進行)
          ????this.parent?=?parent;
          ??}
          ??//?元素繪制,這里需要實現(xiàn)文本元素渲染邏輯
          ??draw(text)?{
          ????console.log(text);
          ??}
          }
          //?適配器
          export?class?Adapter?{
          ??constructor()?{
          ????//?裝載容器
          ????this.children?=?[];
          ??}
          ??//?裝載子元素
          ??append(child)?{
          ????this.children.push(child);
          ??}
          ??//?元素狀態(tài)更新
          ??update(node,?text)?{
          ????//?找到目標渲染進行繪制
          ????const?target?=?this.children.find((child)?=>?child?===?node);
          ????target.draw(text);
          ??}
          ??clear()?{}
          }
          //?容器?===?適配器實例
          export?function?getContainer()?{
          ??return?new?Adapter();
          }

          好了,基本的適配器已經(jīng)完成了,接下來我們來實現(xiàn)渲染器。

          Renderer Abstract


          import?{?createRenderer?}?from?"vue";

          import?{?Text?}?from?"./adapter";
          let?uninitialized?=?[];
          const?render?=?createRenderer({
          ??//?創(chuàng)建元素,實例化Text
          ??createElement(type)?{
          ????switch?(type)?{
          ??????case?"text":
          ????????return?new?Text();
          ????}
          ??},
          ??//?插入元素,調(diào)用適配器方法進行裝載統(tǒng)一管理
          ??insert(el,?parent)?{
          ????if?(el?instanceof?Text)?{
          ??????el.parent?=?parent;
          ??????parent.append(el);
          ??????uninitialized.map(({?node,?text?})?=>?el.parent.update(node,?text));
          ????}
          ????return?el;
          ??},
          ??//?props更新
          ??patchProp(el,?key,?preValue,?nextValue)?{
          ????el[key]?=?nextValue;
          ??},
          ??//?文本更新,重新繪制
          ??setElementText(node,?text)?{
          ????if?(node.parent)?{
          ??????console.log(text);
          ??????node.parent.clear(node);
          ??????node.parent.update(node,?text);
          ????}?else?{
          ??????uninitialized.push({?node,?text?});
          ????}
          ??},
          ??remove(el)?{},
          ??createText(type)?{},
          ??parentNode(node)?{},
          ??nextSibling(nide)?{},
          });

          export?function?createApp(root)?{
          ??return?render.createApp(root);
          }



          樹莓派連接屏幕芯片


          SSD1306 OLED


          OLED,即有機發(fā)光二極管( Organic Light Emitting Diode)。是一種液晶顯示屏。而 SSD1306 就是一種 OLED 驅(qū)動芯片。ssd1306 本身支持多種總線驅(qū)動方式:6800/8080 并口、SPI 及 IIC 接口方式。這里我們選擇 IIC 接口方式進行通信,理由很簡單: 1. 接線簡單方便(兩根線就可以驅(qū)動 OLED) 2.輪子好找...缺點就是 IIC 傳輸數(shù)據(jù)效率太慢了,刷新率只有 10FPS 不到。而 SPI 刷新率最大能達到 2200FPS。

          硬件接線


          IIC 僅需要 4 根線就可以,其中 2 根是電源,另外 2 根是 SDA 和 SCL。我們使用 IIC-1 接口。下面是樹莓派的 GPIO 引腳圖。


          注意:請一定以屏幕的實際引腳編號為準。

          • 屏幕 VCC 接樹莓派 1 號引腳。- 3.3v 電源
          • 屏幕 GND 接樹莓派 9 號引腳。- 地線
          • 屏幕 SDA 接樹莓派 3 號引腳。- IIC 通信中為數(shù)據(jù)管腳
          • 屏幕 SCL 接樹莓派 5 號引腳。- IIC 通信中為時鐘管腳


          樹莓派啟用 I2C


          1.安裝工具包


          sudo?apt-get?install?-y?i2c-tools

          2.啟用 I2C


          • sudo raspi-config
          • 選擇 Interfacing Options
          • Enable I2C

          3.檢查設(shè)備掛載狀態(tài)


          i2c-tools 提供的 i2cdetect 命令可以查看掛載設(shè)備

          sudo?i2cdetect?-y?1



          Node.js 驅(qū)動硬件


          Node.js Lib


          我們先來看幾個 Node.js 庫,看完你會不得不感嘆~任何可以使用 JavaScript 來編寫的應用

          johnny-five(http://johnny-five.io/)

          Johnnt-Five 是一個支持 JavaScript 語言編程的機器人和 IOT 開發(fā)平臺,基于 Firmata 協(xié)議。Firmata 是計算機軟件和微控制器之間的一種通信協(xié)議。使用它,我們可以很簡單的架起樹莓派和屏幕芯片之間的橋梁。


          raspi-io(https://github.com/nebrius/raspi-io)

          Raspi IO 是一個為 Johnny-Five Node.js 機器人平臺提供的 I/O 插件,該插件使 Johnny-Five 能夠控制一個 Raspberry Pi 上的硬件。

          oled-font-5x7(https://github.com/noopkat/oled-font-5x7)

          5x7 oled 字體庫,將字符轉(zhuǎn)為 16 進制編碼,讓 oled 程序能夠識別。用于繪制文字。

          oled-js(https://github.com/noopkat/oled-js)

          ? 兼容 johnny-five 的 oled 支持庫 (johnny-five 本身并不支持 oled),提供了操作 oled 的 API。

          驅(qū)動程序?qū)崿F(xiàn)


          //?oled.js
          const?five?=?require("johnny-five");
          const?Raspi?=?require("raspi-io").RaspiIO;
          const?font?=?require("oled-font-5x7");
          const?Oled?=?require("oled-js");
          const?OPTS?=?{
          ??width:?128,?//?分辨率??0.96寸?ssd1306?128*64
          ??height:?64,?//?分辨率
          ??address:?0x3c,?//?控制輸入地址,ssd1306?默認為0x3c
          };
          class?OledService?{
          ??constructor()?{
          ????this.oled?=?null;
          ??}
          ??/**
          ???*?初始化:?創(chuàng)建一個Oled實例
          ???*?創(chuàng)建后,我們就可以通過操作Oled實例來控制屏幕了
          ???*/
          ??init()?{
          ????const?board?=?new?five.Board({
          ??????io:?new?Raspi(),
          ????});
          ????//?監(jiān)聽程序退出,關(guān)閉屏幕
          ????board.on("exit",?()?=>?{
          ??????this.oled?&&?this.remove();
          ????});
          ????return?new?Promise((resolve,?reject)?=>?{
          ??????board.on("ready",?()?=>?{
          ????????//?Raspberry?Pi?connect?SSD?1306
          ????????this.oled?=?new?Oled(board,?five,?OPTS);
          ????????//?打開屏幕顯示
          ????????this.oled.turnOnDisplay();
          ????????resolve();
          ??????});
          ????});
          ??}
          ??//?繪制文字
          ??drawText({?text,?x,?y?})?{
          ????//?重置光標位置
          ????this.oled.setCursor(+x,?+y);
          ????//?繪制文字
          ????this.oled.writeString(font,?2,?text,?1,?true,?2);
          ??}
          ??clear({?x,?y?})?{
          ????this.oled.setCursor(+x,?+y);
          ??}
          ??//?刷新屏幕
          ??update()?{
          ????this.oled.update();
          ??}
          ??remove()?{
          ????//?關(guān)閉顯示
          ????this.oled.turnOffDisplay();
          ????this.oled?=?null;
          ??}
          }
          export?function?oledService()?{
          ??return?new?OledService();
          }

          接下來,我們就可以在適配器中調(diào)用 oled 程序渲染屏幕了~

          //?index.js
          import?{?createApp?}?from?"./renderer.js";
          import?{?getContainer?}?from?"./adapter";
          import?{?oledService?}?from?"./oled";
          import?App?from?"./App.vue";
          const?oledIns?=?oledService();
          oledIns.init().then(()?=>?{
          ??createApp(App).mount(getContainer(oledIns));
          });

          //?adapter.js
          export?class?Text?{
          ??constructor(parent)?{
          ????this.parent?=?parent;
          ??}
          ??draw(ints,?opts)?{
          ????ints.drawText(opts);
          ????ints.update();
          ??}
          }

          export?class?Adapter?{
          ??constructor(oledIns)?{
          ????this.children?=?[];
          ????this.oled?=?oledIns;
          ??}
          ??append(child)?{
          ????this.children.push(child);
          ??}
          ??update(node,?text)?{
          ????const?target?=?this.children.find((child)?=>?child?===?node);
          ????target.draw(this.oled,?{
          ??????text,
          ??????x:?node.x,
          ??????y:?node.y,
          ????});
          ??}
          ??clear(opts)?{
          ????this.oled.clear(opts);
          ??}
          }
          export?function?getContainer(oledIns)?{
          ??return?new?Adapter(oledIns);
          }

          到這一步,就可以成功點亮屏幕啦,來看看效果~



          效果展示





          結(jié)語


          完整代碼已上傳到?Github,如果你覺得這個實踐對你有啟發(fā)/幫助,點個?star?吧~

          Vue 已經(jīng)成功渲染到嵌入式液晶屏了,那下一步是不是可以考慮接個搖桿寫個貪吃蛇游戲了~哈哈哈,這很"Javascript"。



          點擊左下角閱讀原文,到?SegmentFault 思否社區(qū)?和文章作者展開更多互動和交流。

          -?END -

          瀏覽 25
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  射了好多别动哦初恋哥vs | 尻屄视频在线免费看 | 含羞草一区 | 国产婷婷综合视频网站 | 老熟女乱伦视频 |