前言:
今天我们对“vue怎么调分辨率”大约比较重视,姐妹们都想要分析一些“vue怎么调分辨率”的相关资讯。那么小编同时在网络上收集了一些对于“vue怎么调分辨率””的相关知识,希望同学们能喜欢,兄弟们一起来了解一下吧!该文章转载自公众号@前端时刻,
前言
之前看了雪碧大佬的将 React 渲染到嵌入式液晶屏觉得很有意思,React能被渲染到嵌入式液晶屏,那Vue是不是也可以呢?所以本文我们要做的就是:
如标题所示,就是将Vue渲染到嵌入式液晶屏。这里使用的液晶屏是0.96 寸大128x64分辨率的SSD1306。而要将Vue渲染到液晶屏,我们还需要一个桥梁,它必须具备控制液晶屏及运行代码的能力。而树莓派的硬件对接能力和可编程性天然就具备这个条件。最后一个问题来了,我们用什么技术来实现呢?
这里我选择了Node.js。原因:
Atwood定律:“任何可以使用JavaScript来编写的应用,最终会由JavaScript编写。” 驱动硬件我大Node.js 一行npm install 走天下。
这个有趣的实践可拆分为这几个步骤:
在Node.js运行Vue树莓派连接屏幕芯片Node.js驱动硬件
Talk is cheap,Let's Go!!!
跨端渲染
无论是 基于React的React Native 宣称的「Learn Once, Write Anywhere」,还是基于Vue的Weex宣称的「Write Once, Run Everywhere」口号,本质上强调的都是它们跨端渲染的能力。那什么是跨端渲染呢?
React: ReactNative Taro ...
Vue: Weex UniApp ...
各种五花八门的前端框架纷纷袭来,前端工程师们纷纷抱怨学不动了~
老板们看到纷纷笑嘻嘻, App单,前端分,小程序单,前端吞,PC/H5,前端昏。skr~
这些跨平台框架原理其实都大同小异,选定Vue/React作为DSL,以这个 DSL 框架为标准在各端分别编译,在运行时,各端使用各自的渲染引擎(Render Engines)进行渲染,底层渲染引擎中不必关心上层DSL的语法和更新策略,只需要处理 JS Framework 中统一定义的节点结构和渲染指令。也正是因为这一渲染层的抽象,使得跨平台/框架成为了可能。
Vue和React现在都实现了自定义渲染器,下面我们简单介绍一下:
React Reconciler
React16采用新的Reconciler,内部采用了Fiber的架构。react-reconciler模块正是基于v16的新Reconciler实现,它提供了创建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,让我们创建自定义渲染器。
createRenderer 函数接受两个泛型参数:HostNode 和 HostElement,对应于宿主环境中的 节点 和 元素 类型。
自定义渲染器可以传入特定于平台的类型,如下所示:
import { createRenderer } from 'vue'const { render, createApp } = createRenderer<Node, Element>({ patchProp, ...nodeOps})
在Node.js上运行Vue
SFC To JS
<template> <text x="0" y="0">Hello Vue</text> <text x="0" y="20">{{ time }}</text> <text x="0" y="40">Hi SSD3306</text></template><script>import { defineComponent, ref, toRefs, onMounted } from "vue";import dayjs from "dayjs";export default defineComponent({ setup() { const time = ref(dayjs().format("hh:mm:ss")); onMounted(() => { setInterval(() => { time.value = dayjs().format("hh:mm:ss"); }, 800); }); return { ...toRefs({ time, }), }; },});</script>
要将Vue渲染到液晶屏,我们首先需要让Vue能运行在Node.js上,但是上面这个SFC是没办法被Node.js识别的,它只是vue的编程规范,是一种方言。所以我们需要做的是先将SFC转为js。这里我使用Rollup打包将SFC转为JS(相关配置这里就不啰嗦了,贴个传送门)。到了这一步,Node.js就能成功运行打包后的js代码了,这还不够,这时候Vue组件的状态更新是没办法同步到Node.js的。
Create Custom Renderer
组件状态更新我们需要通知Node.js 更新并渲染液晶屏内容,我们需要创建自定义的"更新策略"。这里就需要用到了我们前面提到的自定义渲染器:createRenderer API。下面我们简单介绍下我们相关使用:
// index.js// 自定义渲染器import { createApp } from "./renderer.js";// 组件import App from "./App.vue";// 容器function getContainer(){// ...}// 创建渲染器,将组件挂载到容器上createApp(App).mount(getContainer());
// renderer.jsimport { createRenderer } from "vue";// 定义渲染器,传入自定义nodeOpsconst render = createRenderer({ // 创建元素 createElement(type) {}, // 插入元素 insert(el, parent) {}, // props更新 patchProp(el, key, preValue, nextValue) {}, // 设置元素文本 setElementText(node, text) {}, // 以下忽略,有兴趣的童鞋可自行了解 remove(el) {}, createText(type) {}, parentNode(node) {}, nextSibling(nide) {},});export function createApp(root) { return render.createApp(root);}
vue渲染器默认实现了Web平台DOM编程接口,将Virtual DOM 渲染为真实DOM。但是这个渲染器只能运行在浏览器中,不具备跨平台能力。所以我们必须重写nodeOps相关钩子函数,实现对应宿主环境元素的增删改查操作。接下来我们定义一个适配器,来实现相关逻辑。
Adapter
在实现前,我们先来理一下我们要实现的逻辑:
创建元素实例 (create)将元素实例插入容器,由容器进行管理 (insert)状态改变时,通知容器进行更新 (update)
// adapter.js// 文本元素export class Text { constructor(parent) { // 提供一个父节点用于寻址调用更新 (前面提到状态更新由容器进行) this.parent = parent; } // 元素绘制,这里需要实现文本元素渲染逻辑 draw(text) { console.log(text); }}// 适配器export class Adapter { constructor() { // 装载容器 this.children = []; } // 装载子元素 append(child) { this.children.push(child); } // 元素状态更新 update(node, text) { // 找到目标渲染进行绘制 const target = this.children.find((child) => child === node); target.draw(text); } clear() {}}// 容器 === 适配器实例export function getContainer() { return new Adapter();}
好了,基本的适配器已经完成了,接下来我们来实现渲染器。
Renderer Abstract
import { createRenderer } from "vue";import { Text } from "./adapter";let uninitialized = [];const render = createRenderer({ // 创建元素,实例化Text createElement(type) { switch (type) { case "text": return new Text(); } }, // 插入元素,调用适配器方法进行装载统一管理 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,即有机发光二极管( Organic Light Emitting Diode)。是一种液晶显示屏。而SSD1306就是一种OLED驱动芯片。ssd1306本身支持多种总线驱动方式:6800/8080 并口、SPI及IIC接口方式。这里我们选择IIC接口方式进行通信,理由很简单: 1. 接线简单方便(两根线就可以驱动OLED) 2.轮子好找...缺点就是IIC 传输数据效率太慢了,刷新率只有 10FPS 不到。而SPI刷新率最大能达到 2200FPS。
硬件接线
IIC 仅需要 4 根线就可以,其中 2 根是电源,另外 2 根是 SDA 和 SCL。我们使用 IIC-1 接口。下面是树莓派的 GPIO 引脚图。
注意:请一定以屏幕的实际引脚编号为准。
屏幕VCC接树莓派1号引脚。- 3.3v电源屏幕GND接树莓派9号引脚。- 地线屏幕SDA接树莓派3号引脚。- IIC 通信中为数据管脚屏幕SCL接树莓派5号引脚。- IIC 通信中为时钟管脚
树莓派启用I2C
1.安装工具包
sudo apt-get install -y i2c-tools2.启用I2Csudo raspi-config选择 Interfacing OptionsEnable I2C
3.检查设备挂载状态
i2c-tools提供的i2cdetect命令可以查看挂载设备
sudo i2cdetect -y 1
Node.js驱动硬件
Node.js Lib
我们先来看几个Node.js库,看完你会不得不感叹~任何可以使用JavaScript来编写的应用,最....
johnny-five
Johnnt-Five 是一个支持 JavaScript 语言编程的机器人和 IOT 开发平台,基于 Firmata 协议。Firmata 是计算机软件和微控制器之间的一种通信协议。使用它,我们可以很简单的架起树莓派和屏幕芯片之间的桥梁。
raspi-io
Raspi IO是一个为Johnny-Five Node.js机器人平台提供的I/O插件,该插件使Johnny-Five能够控制一个Raspberry Pi上的硬件。
oled-font-5x7
5x7 oled字体库,将字符转为16进制编码,让oled程序能够识别。用于绘制文字。
oled-js
兼容johnny-five的oled支持库 (johnny-five本身并不支持oled),提供了操作oled的API。
驱动程序实现
// oled.jsconst 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; } /** * 初始化: 创建一个Oled实例 * 创建后,我们就可以通过操作Oled实例来控制屏幕了 */ init() { const board = new five.Board({ io: new Raspi(), }); // 监听程序退出,关闭屏幕 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() { // 关闭显示 this.oled.turnOffDisplay(); this.oled = null; }}export function oledService() { return new OledService();}
接下来,我们就可以在适配器中调用oled程序渲染屏幕了~
// index.jsimport { 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.jsexport 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);}
到这一步,就可以成功点亮屏幕啦,来看看效果~
效果展示
参考
将 React 渲染到嵌入式液晶屏
在树莓派上使用 SSD1306 OLED 屏幕
结语
完整代码已上传到Github,如果你觉得这个实践对你有启发/帮助,点个star吧~
Vue已经成功渲染到嵌入式液晶屏了,那下一步是不是可以考虑接个摇杆写个贪吃蛇游戏了~哈哈哈,这很"Javascript"。
"阅读式"的学习使我犯困,所以我更倾向通过一些有趣的实践吸收知识。如果你和我一样爱折腾,欢迎关注~
标签: #vue怎么调分辨率