如何从Sketch导出选中内容到SVG

错误示范

在左侧树状栏中右键选中图案,点击 Copy SVG Code。这种方式导出的 SVG 图片可能会与 Sketch 中的原图案出入较大。

正确示范

双击选中想要导出的图案,然后在菜单栏中找到并点击 Layer - Make Exportable

此时,页面右下角会有导出面板,选择导出格式为 svg 然后点击 Export Selected 即可正确导出图案为 SVG。

聊聊腾讯的跨端开源框架 Hippy

Hippy 简介

hippy /'hipi/

hippy的跨端方案思路和 React Native 一致,使用 v8 引擎执行 JavaScript 逻辑代码,使用原生组件的方式渲染UI以提升性能。但是hippy相较 React Native 而言,有以下优势:

1. 更轻量。更小的安装包、打出的JS bundle包体积也更小,只有React Native的一半左右。

2. 渲染性能比React Native 更好。

3. 和 Web 接近的开发体验。

    > Hippy 在开发体验上也进行了大量优化,包含但不限于,跟浏览器一样的 onClick、onTouch 系列触屏事件,更加简单的动画方案,hippy-vue 提供了和 Vue 的完全兼容等等。

    在开发体验这一点上,hippy也是远远优于React Native 的。后文介绍了hippy跨端原理,

为什么 hippy 可以跨端?

我们看看 hippy-react 和 hippy-vue 的架构图就明白了。

因为两者均支持 vDOM并支持自定义渲染器,hippy-react基于React 的React-Reconciler 实现了自定义渲染器,而 hippy-vue 基于 Vue 3 的 createRenderer() 实现自定义渲染器。

这其实也解释了为什么 hippy 到目前为止也只支持这两个前端框架,不支持 svelte 等其他框架。如果需要 hippy 支持一个前端框架,那么至少得让这个框架有自己的 vDOM 并支持第三方自定义渲染器去渲染它的 vDOM 组件节点树 。市面上也恰好只有 React 和 Vue 支持这些,并且技术相对比较成熟。

Hippy 作为 React Native 的后继者,同样基于 Facebook React 的官方自定义渲染器 react-reconciler 重新开发了 React 到终端的渲染层,可以使用React 的全部特性,在语法上和 React Native 类似,但更轻量、性能相对而言更好。

渲染中最重要的是什么——布局 (layout)。Facebook 为 React Native 用C++ 实现了 yoga 布局引擎。QQ浏览器团队也是比较厉害,参考了 chromium 中的布局引擎、Facebook 的 yoga 布局引擎硬是为 hippy 实现了自己的一套非常简洁的布局引擎 HippyLayout 。而且在开发中他们也是以 yoga 为性能基准,对 HippyLayout 进行性能调优的。

此处注意,yoga 和 HippyLayout 都是 Flex 布局引擎。这意味着,在一个 hippy 项目中,你应该尽可能地使用 flex 去实现布局需求(事实上,在我经手的大部分 Web 项目中,我都是 Flex 一把梭,基本可以解决80% 的布局需求)。

我们关心的几个问题

  1. hippy是不是同时支持ios和android?

    结论:既然是跨端框架,当然是都支持的。Android端集成文档iOS端集成文档

  1. hippy的定制化程度高不高,比如他的长列表标签里面是不是前端随便怎么写都行?

    结论:是的,可定制化程度非常高,甚至可以一个列表中渲染多种样式的列表项。

需要注意的点

1. 样式

    - Hippy 的所有样式支持由终端直接提供,基本和浏览器一致,但不支持百分比布局。目前Hippy支持具体数值,单位是 dp 。具体换算公式为:

        \(实际真机长度值 = 屏幕缩放比例 * Hippy 样式长度值\)

    - Hippy采用了CSS标准盒模型构建,对应的是 CSS 的box-sizing: border-box,这也意味着你不能在 Hippy 中通过修改 box-sizing 来修改 Hippy 盒模型的行为 (注:在浏览器CSS盒模型中, box-sizing 的默认值是 content-box )。

    - 使用 Flex布局。前面提到,Hippy用C++实现了自己的 Flex 布局引擎 HippyLayout。因为 Hippy 仅支持 Flex 布局,所以我们不需要在样式中手写 display: flex

    - Hippy 使用 CSS-in-JS 的方式编写样式,我们主要通过 @hippy/react 提供的 StyleSheet 类来统一管理组件的样式。

2. 动画

    由于 Hippy 替换掉了 Webkit 使用 vDOM 映射到原生 Native RenderPipeLine (原生渲染管线)的方式渲染界面。这意味很多 CSS 特性我们都不能使用,包括 CSS 动画。Hippy 动画的原理是由前端传入动画参数,由原生终端去控制每一帧的计算和排版更新,减少了前端与终端的通信次数,减少了动画的卡顿。

总结

得益于 React 和 Vue 的 vDOM 。采用 JSEngine + Native RenderPipeLine (JS 引擎负责执行通用业务主逻辑、原生渲染管线负责渲染界面),思路和 React Native 一致,

参考资料

  1. https://cloud.tencent.com/developer/article/1558201

  2. https://litslink.com/blog/new-react-native-architecture

  3. https://hippyjs.org/#/

  4. https://github.com/Tencent/Hippy

  5. https://blog.csdn.net/z4909801/article/details/88943851

  6. https://blog.csdn.net/wsyzxxn9/article/details/110161042

  7. https://zhuanlan.zhihu.com/p/107947462

  8. https://zhuanlan.zhihu.com/p/52459664

  9. https://www.bilibili.com/video/av37464898

  10. https://cloud.tencent.com/developer/article/1369800

  11. https://juejin.cn/post/6844904115462471693

酷狗小程序开启X5内核 Inspector 调试

酷狗小程序目前是采用x5内核渲染的,我们可以通过以下方式开启x5内核调试选项,然后使用 Chrome Devtools 远程调试以提升酷狗小程序的调试体验。

  1. 写一个最简单的酷狗小程序,调用下面的函数:
1
2
3
4
5
6
function() openDebugOptions() {
if (!window.MiniApp) {
throw new Error('MiniApp SDK 未初始化')
}
return window.MiniApp.navigateToHtmlPage({ url: 'http://debugtbs.qq.com' })
}
  1. 打开手机酷狗扫码打开这个小程序后进入 TBS 调试页

点击安装线上内核,重启小程序后再次进入点击查看版本信息,如果成功显示x5内核版本则说明x5内核已成功安装。

  1. 接着点击 Debug X5 进入X5内核调试选项配置页

最重要的是打开 信息 标签页,开启

  • 打开TBS内核Inspector调试功能
  • 打开TBS内核X5jscore Inspector调试功能

这两个选项。

其他配置项视自己需求开启,如 打开vConsole调试功能、渲染中的 内核网格线FPS meter 都是很有用的功能。

如果打开 FPS meter 显示字体是方框的话,可以开启 信息-关闭X5内核Custom Font功能 修复这个问题。

  1. 开启手机调试选项,使用USB连接电脑和手机,在 Chrome 地址栏输入 chrome://inspect 打开远程设备调试列表页

保持待调试的酷狗小程序在前台,开启 Discover USB devices 选项再刷新一下,这时候 Chrome 应该就可以发现这个小程序了。

如果设备显示已连接,但可调试页面列表一直为空,可以先打开 Android Chrome 再返回酷狗小程序,这样可以强制 Chrome 刷新列表。

  1. 点击 WebView in com.kugou.android 下面的 inspect 链接

注意,此步可能需要科学上网,因为 Chrome 需要去谷歌的网站下载适合这个 Webview 版本远程调试的 Chrome Devtools,否则会一直显示空白。

如果能成功打开 Chrome Devtools,那么后续的步骤就和使用 ChromeDevtools 正常调试一个桌面端网页的步骤一样了。我们可以使用 Element 查看 DOM 树和样式表,使用 Console 来查看日志,使用 Performance Monitor 来监控页面性能,进行针对性的优化,使用 JavaScript Profiler 来分析 JavaScript 虚拟机的执行情况。

MacOS信任自签证书

我厂一些内部网站使用了自签证书,但是自签证书在最新版本的 Chrome (Version 92.0.4515.59 beta) 上已经不被认可,无法打开这些网站。

实测使用 Firefox 仍可以访问,但是也需要展开高级选项才能打开。解决这个问题有两个方案,一个是使用 http 协议的链接去访问,另一个方案就是从系统层面信任这个证书。

下面简单介绍一下如何在系统层面信任一个自签证书的步骤:

导出证书

首先,使用 Chrome 打开目标网站,点击地址栏左侧的不安全的锁的图标,查看当前网站使用的证书,打开 Finder 在一侧备用:

按住 Option 键,长按证书图标拖到 Finder 中,接着这个证书会自动被保存为一个 pem 格式的证书文件:

导入并信任自签证书

打开,KeyChain Access 应用

点击右键左侧的 System Keychains -> System 选项卡,后右键点击 unlock 解锁当前系统证书配置

然后将 Finder 中的那个 pem 证书拖到证书列表中,双击证书列表中刚刚导入的自签证书:

选择总是信任后保存,重新锁定 System 证书配置。

检查

最后,我们打开原网址,点击 Advanced Options (高级选项) 按钮,点击仍要打开。之后,Chrome 就会记住你的选择,不会阻止你打开这类网站了。

唯一有点美中不足的是,Chrome 仍会在左上角显示不安全图标,如果不是强迫症的话就可以忽略了。

注意这种方法也是有弊端的,万一真的有一天这个网站的证书是被伪造了,由于你之前选择了总是信任该网站,所有不排除有一天你会打开钓鱼网站可能性。对于我个人的使用场景而言,由于这是公司的内网,选择总是信任该网站的证书没有太大的问题。

STM32驱动0.96寸OLED显示图片

  1. 将需要显示的图片(以jpeg格式为例)通过 convertio 转换为 bmp 格式;
  2. 打开 Windows 画图编辑图片,将图片缩放为 128x64 分辨率大小;
  3. 打开 搞定设计在线PS(photopea 中国版),点击菜单栏 图像->调整->阈值 可以在二值化过程中顺便去除噪点,保存成 bmp 格式;
  4. 打开取模工具 PCDtoLCD2002,导入第三步保存的 bmp 图片后预览效果。打开字模选项,按下图红圈方式设置后点击确定按钮保存设置,最后点击生成生成一个长度为 1024 的一维数组,复制备用;

  1. 粘贴下面代码到源文件中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
const unsigned char images[1][1024] = {
{...你的图片...},
};

// 显示图片(通过改变索引值和图片对应的像素可以显示不同像素的图片)
// x,y: 图片的起点坐标
// px,py: 图片的像素(与实际取模的图片像素一致)
// index: 图片索引
void OLED_ShowBMP(u8 x, u8 y, u8 px, u8 py, u8 index, u8 mode)
{
u8 temp,t1;
u16 j,i;
u8 y0=y;

i = (px/2)*(py/4);

for(j = 0; j < i;j++)
{
temp = images[index][j]; //调用图片
for(t1=0;t1<8;t1++)
{
if(temp&0x80)OLED_DrawPoint(x,y,mode);
else OLED_DrawPoint(x,y,!mode);
temp<<= 1;
y++;
if((y-y0) == py)
{
y=y0;
x++;
break;
}
}
}
}
  1. 调用方法显示图片
1
2
OLED_ShowBMP(0, 0, 128, 64, 0, 1);
OLED_Refresh_Gram(); //更新显示到OLED
NOTES

如果你需要显示多张图片,可以把其它图片加到 images 数组中,然后在调用 OLED_ShowBMP 函数时注意指定 index 为相应的值即可。

参考资料

  1. 0.96寸OLED显示图片
  2. 怎样在Photoshop中二值化

怎么跑起来一个docker项目

用户反馈他们的应用不能正常发布,原因是线上编译器报错。我拿到用户的应用源文件后,本地测试可以正常编译部署。于是联系 Cloud 团队是否能提供 docker 镜像方便我复现线上环境,看看是不是环境问题。Cloud 团队负责人直接丢给我一个项目连接,打开后只有代码源文件和一个 Dockerfile 配置文件。

怎么从一个 Dockerfile 文件启动一个 docker 容器呢?

首先,我们构建 (build) 这个项目成一个镜像 (container image):

1
2
cd <project-folder> # 切换到项目根目录
docker build -t <image-name> . # 构成镜像

接着,我们就可以运行这个镜像了:

1
docker run <image-name>

修复ClashX HTTP代理端口和Socks5端口总是为0的问题

因为,ClashX 默认监听的是 7890 端口,怀疑是端口被占用了,我们使用 lsof 命令来查看端口被占用情况。

1
2
3
> lsof -i :7890
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
privoxy 579 lolimay 3u IPv4 0x527fa2f554e1edb 0t0 TCP localhost:7890 (LISTEN)

可以看到,端口被 privoxy 这个应用给占用了。接着我们用 ps 命令查看占用这个端口的进程。

1
2
3
> ps -ef | grep privoxy
UID PID PPID C STIME TTY TIME CMD
501 94421 1 0 10:43AM ?? 0:00.01 /Users/lolimay/Library/Application Support/ShadowsocksX-NG/privoxy --no-daemon privoxy.config

可以看到是 ShadowsocksX-NG 这个软件占用的,我们打开这个应用把它的监听端口改成 8790 或其他端口后,重启 ClashX 发现 ClashX 已经可以正常监听 7890 端口了 🎉

STM32F1的IO口简介

基本介绍

STM32的引脚可设置为可设置为:普通IO功能、复用功能、重映射功能。不过普通IO功能、复用功能用得比较多。

  1. 普通 IO 功能一般需要使能 GPIOAGPIOBGPIOC 时钟
  2. 需要使用复用功能,如 STM32F103RCT6 的串口1功能,需要使用 PA9PA10 两个引脚,此时需要使用串口1时钟 USART1
  3. 需要用到外设的重映射功能时需要使能 AFIO 的时钟。

关于重映射功能举例如下:

1
2
3
4
重映射USART2
USART2的TX/RX在PA.2/3
PA.2已经被Timer2的channel3使用
需要把USART2的TX/RX重映射到PD.5/6

库函数的调用步骤如下:

  1. 使能被重新映射到的I/O端口时钟

    1
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);

  2. 使能被重新映射的外设时钟

    1
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);

  3. 使能AFIO功能的时钟(勿忘!)

    1
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

  4. 进行重映射

    1
    GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);

初始化步骤

  1. 使用 RCC_APB2PeriphClockCmd 函数使能相关 APB2 外设时钟
  2. 通过 GPIO_Init 函数初始化并配置各IO口
  3. 使用 GPIO_ReadInputDataBit 读取IO口电平状态,使用 GPIO_SetBitsGPIO_ResetBits 来设置各IO口的值 (实际我们使用 PAout(0)=1, PBout(2)=0; 这种写法去设置 IO 口的电平状态)

相关函数定义和说明如下:

1. GPIO_Init 初始化函数: 配置IO口的模式和速度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);

typedef enum {
GPIOA,
GPIOB,
GPIOC
} GPIO_TypeDef;

typedef struct {
unit16_t GPIO_Pin;
GPIOSpeed_TypeDef GPIO_Speed;
GPIOMode_TypeDef GPIO_Mode;
} GPIO_InitTypeDef;

typedef enum {
GPIO_Mode_AIN = 0x0, // 模拟输入
GPIO_Mode_IN_FLOATING = 0x04, // 浮空输入
GPIO_Mode_IPD = 0x28, // 下拉输入
GPIO_Mode_IPU = 0x48, // 上拉输入
GPIO_Mode_Out_OD = 0x14, // 开漏输出
GPIO_Mode_Out_PP = 0x10, // 通用推挽输出
GPIO_Mode_AF_OD = 0x1C, // 复用开漏输出
GPIO_Mode_AF_PP = 0x18 // 复用推挽输出
} GPIOMode_TypeDef;

typedef enum {
GPIO_Speed_10MHz = 1,
GPIO_Speed_2MHz,
GPIO_Speed_50MHz
} GPIOSpeed_TypeDef;

示例:

1
2
3
4
5
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; // PB5 端口设置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 速度 50MHz
GPIO_Init(GPIOB, &GPIO_InitSture); // 根据参数配置IO口 PB5

2. GIPO_ReadInputDataBit: 读取IO口的电平状态

1
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, unit16_t GPIO_Pin);

示例:

1
2
// 返回值是 1(Bit_SET) 或者 0(Bit_RESET)
GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1); // 读取IO口 PA1 的电平状态

3. GPIO_Write: 一次性设置GPIO的多个端口值(通过ODR寄存器实现)

1
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);

4. 通过 GPIO_SetBits 和 GPIO_ResetBits 来操作各IO口的值

1
2
void GPIO_SetBits(GPIO_TypeDef* GPIOx, unit16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, unit16_t GPIO_Pin);

示例:

1
2
GPIO_SetBits(GPIOC, GPIO_Pin_3); // 设置IO口 PC3 输出 1
GPIO_ReSetBits(GPIOC, GPIO_Pin_3); // 设置IO口 PC3 输出 0

5. 通过 RCC_APB2PeriphClockCmd 使能IO时钟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* RCC_APB2Periph_AFIO 功能复用IO时钟
* 需要用到外设的重映射功能时才需要使能AFIO时钟
*
* RCC_APB2Periph_GPIOA GPIOA时钟
* RCC_APB2Periph_GPIOB GPIOB时钟
* RCC_APB2Periph_GPIOC GPIOC时钟
* RCC_APB2Periph_USART1 USART1时钟(串口1时钟)PA9(TX)、PA10(RX)
*/
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
typedef enum {
Enable = 1,
Disable = 0
} FunctionalState;

参考链接

  1. STM32端口复用和重映射
  2. 什么情况下需要使能AFIO的时钟呢?

SSD1306 8080并口读写过程

  1. 先根据要写入/读取的数据类型,设置 RS 为1(数据)/0(命令)
1
OLED_RS = cmd; // cmd = 0(命令)/1(数据)
  1. 然后拉低片选 CS
1
OLED_CS = 0;
  1. 接着我们根据要读数据还是要写数据置RD/WR为低,然后
    • 读数据:在RD上升沿,读取数据线上的数据
    • 写数据:在WR上升沿,使数据写入到驱动IC里面
1
2
3
4
5
6
// 读数据
OLED_RD = 0;
OLED_RD = 1;
// 写数据
OLED_WR = 0;
OLED_WR = 1;

参考资料

  1. SSD1306 官方手册
  2. Proteus仿真库中英文对照

Keil uVision5+Proteus STM32 开发仿真调试一条龙服务

调试技巧

Source Code Debugging (源码联调)

首先 Keil 端,输出二进制文件格式选择 elf 格式,而不是 hex 或者 axf (axf 其实就是 elf 格式,但是后缀名不一样),因为 elf 格式的二进制文件是带有调试信息的,包含C代码和编译后的汇编代码的映射。这样我们可以直接在更具有可读性的 C 代码上打断点和调试,而不是直接怼汇编代码。

生成好 elf 文件后,我们打开 Proteus 双击 CMU 导入 elf 文件。点击 Start VSM Debugging 可以直接在 C 代码中打断点单步调试。在进入调试模式后,点击菜单栏 Debug 按钮,弹出的列表最下面几项会显示原理图中主要元件的调试选项,如 CM3 -> Source Code - U1 可以进入源代码调试、CM3 -> Registers - U1 可以查看 Cortex-M3 处理器的各个寄存器的状态、SSD1306 LCD Controller RAM - LCD 可以查看 LCD 控制器 SSD1306 内存状态。

Design Explorer

画好原理图后,如果不确定连线是不是都连接上了,可以打开 Design Explorer 视图查看元件各个端口的网络标号 (Net),以此判断是否已连接上。

Probes (探针)

其次,我们可以使用 Proteus 提供的 Probes (探针) 元件 (在左侧工具栏中可以找到),给待测试的线路标上电压探针会电流探针来方便我们判断电路状态。

Terminal Voltages & Terminal Logic States

除了使用探针,在 Proteus 进入单步调试后,我们可以点击各个元件,此时会显示该元件的各个端口电压 (Terminal Voltages) 和各个端口逻辑状态 (Terminal Logic States),也可以方便我们调试。

其中,Proteus 逻辑状态表如下:

逻辑状态 说明 数字逻辑
FLT 悬空态,高阻态 -
WUD 未定义态,与模拟电压混联 -
CON 竞争态,与数字电压冲突 -
SHI 主动输出高逻辑 1
SLO 强电低态,主动输出低逻辑 0
PHI 电源高态,电源高逻辑 1
PLO 电源低态,电源低逻辑 0
WHI 弱电高态,被动输出高逻辑 1
WLO 弱电低态,被动输出低逻辑 0

(非常好用) Diagnostics Configuration

Proteus 还提供一个杀手级的功能,Configure Diagnostics 可以在 Debug 菜单列表中找到或选中元件后右键菜单中找到。通过这个功能,我们可以配置各个元件在日志中输出各种事件,比如我们需要查看 UG-2864HSWEG01 (LCD, SSD1306 Controller included) 关于 Contoller DiagnosticsTrace Information,我们可以把这个 Diagnostics 的 Trace Information Level 改成 Debug 级别。这样我们就可以根据这些事件有没有发生,来判断我们的代码有没有生效。

如通过下面这条日志,我们可以知道 SSD1306 此时使用的是 8080 并行接口的连接方式:

Animation Circuits Configuration

进入 System -> Set Animation Options,Proteus 默认只启用了显示引脚的逻辑状态动画,进入该项设置页后,我们可以把:

  • Show Voltage & Current on Probes 在探针上显示电压和电流
  • Show Logic State of Pins 在引脚上显示逻辑状态 (默认开启)
  • Show Wire Voltage by Color 用颜色标注导线的电压
  • Show Wire Current With Arrows 在导线上标注电流的方向

这些设置项全部开启,这些动画信息可以有效地帮助我们进行调试。

问题排查思路

如果使用了上诉调试技巧还是找不到问题所在,但是日志中显示 proteus simulation is not running in real time due to excessive cpu load 警告,提示你 由于 CPU 超负荷了 Proteus 的仿真不能实时进行。这个警告⚠️很有可能是导致仿真失败、无法复现在真实硬件上的效果的真正原因,建议解决方案如下:

  • 调低 CMU 的晶振频率,如 STM32F103RCT6 的实际晶振频率为 72M,你可以根据你电脑性能的实际情况调整至 比如 1M。双击 CMU 后在 Crystal Frequency 输入框中填写 1M 即可
  • Proteus 进入 System -> Set Simulation Options 调低仿真精度,但是作用其实有限
  • 打开 任务管理器,选中 Proteus 进程右键 转置详细信息,选中进程 PDS.EXE 右键设置设置优先级 实时。这样可以让该进程可以分到更多的 CPU 计算资源,但是作用有限
  • 换一台高性能的设备 /狗头