<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>dtysky|一个行者的轨迹</title>
<link>http://dtysky.moe</link><description>不灭的理想，永远的真诚，坚定的信念，执着的希望</description><atom:link href="http://dtysky.moe/feeds/Create.rss.xml" rel="self"></atom:link>
<lastBuildDate>Monday, 23 Feb 2026 01:39:47 -00:00</lastBuildDate>

<item>
<title>我的简历</title>
<link>http://dtysky.moe/article/Create-MyResume</link>
<description>&lt;div id=&amp;quot;resume-header&amp;quot;&gt;
    &lt;div class=&amp;quot;resume-title&amp;quot;&gt;
        &lt;h2&gt;瞬光寂暗&lt;/h2&gt;
        &lt;p&gt;邮箱 : dtysky@outlook.com&lt;/p&gt;
        &lt;p&gt;Github : &lt;a href=&amp;quot;https://github.com/dtysky&amp;quot;&gt;dtysky&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;Bilibili : &lt;a href=&amp;quot;https://space.bilibili.com/58698&amp;quot;&gt;瞬光寂暗N&lt;/a&gt;&lt;/p&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;hr /&gt;
&lt;h2&gt;学历和工作&lt;/h2&gt;
&lt;p&gt;2025.03 - now    , 腾讯，互娱，游戏引擎技术组长&lt;br /&gt;
2020.08 - 2025.02，腾讯，微信，图形/大模型&lt;br /&gt;
2019.07 - 2020.07，支付宝，前端技术专家&lt;br /&gt;
2018.03 - 2019.07，支付宝，高级前端开发工程师&lt;br /&gt;
2016.08 - 2018.03，哔哩哔哩，前端工程师&lt;br /&gt;
2015.10 - 2016.07，上海禾赛光电，软件工程师&lt;br /&gt;
2015.07 - 2015.09，华为，逻辑（FPGA）工程师&lt;br /&gt;
2014.10 - 2015.06，Xilinx，研发实习生&lt;br /&gt;
2011.08 - 2015.06，东南大学，测控技术与仪器，工学士  &lt;/p&gt;
&lt;h2&gt;发展方向&lt;/h2&gt;
&lt;p&gt;游戏/WEB/AI。&lt;/p&gt;
&lt;h2&gt;技能&lt;/h2&gt;
&lt;h3&gt;工具&lt;/h3&gt;
&lt;p&gt;工作语言：TypeScript, C#, C++, CSS...需要什么就写什么呗，哪有那么多讲究。&lt;br /&gt;
领域技术：WebGL，WebGPU，FPGA，图形学，游戏开发...需要什么去学呗，哪有这么多讲究。&lt;br /&gt;
业余语言：Rust, Python, Scheme, VHDL, Verilog...更没讲究了，写着玩。&lt;br /&gt;
兴趣爱好：微单，无人机，PS，LR，LRT，Davinci，剪映。&lt;/p&gt;
&lt;h3&gt;专业领域&lt;/h3&gt;
&lt;p&gt;工作：WEB开发, 游戏引擎开发, 图形学&lt;br /&gt;
业余：数字图像处理, FPGA&lt;br /&gt;
爱好：文学创作，专业风光/人文摄影&lt;/p&gt;
&lt;h2&gt;个人介绍&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;生命是积累痛苦的巡礼，但这断然不是虚无与毁灭的故事，在前方一定还存在着救赎和希望。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;非典型码农，INFP，ADHD，宿命论者，混乱善良。自洽，坦然，从容。&lt;br /&gt;
厌恶无聊，追求有趣，言出必遂，尽力而为。但为了避免过早毁灭，也知道节制。&lt;br /&gt;
最欣赏的特质是清醒和勇气，最讨厌的特质是怯懦和贪婪。&lt;br /&gt;
看过不少书，经历过不少事，但仍旧保持着初心。&lt;br /&gt;
从小漂泊过半个中国，没有故乡。&lt;br /&gt;
毕业后换过四个城市五家公司，在B站用爱发电过，在阿里经历过斗争，现在在鹅厂搬砖，工作内容和兴趣一致。&lt;br /&gt;
事业比大多同龄人快，见识过很多光鲜外表下的不堪，看透了许多事，觉得任何交往中最重要的是真诚。&lt;br /&gt;
人格已达「完全之形」，拥有不随环境变化的强硬核心，承受逆境的能力很强。&lt;br /&gt;
对世俗物质本质上没有需求，但仍然在不影响创作的前提下努力工作赚钱，为了获得更多的体验作为输入。&lt;br /&gt;
严格来说我有很多种世俗上更好的选择，不过都放弃了。&lt;br /&gt;
因为从个人审美来看，现在选择的这种生存方式可能会支付更多代价，但却是最美丽动人的。  &lt;/p&gt;
&lt;p&gt;欣赏过一些严肃文学，文艺片，舞台剧，摇滚，独立游戏。&lt;br /&gt;
持续探索适合自己的创作方式，想创作出令自己满意的作品，并产生社会价值，让他人得到慰藉或清醒。&lt;br /&gt;
写过剧本小说，系统学过画画。每周末都会出去拍VLOG，假期会出去旅游。&lt;br /&gt;
目前是专业风光摄影师，设备A7R5/A7R3/AIR3。&lt;/p&gt;
&lt;p&gt;年少时蔑视命运，坚信“我命由我不由天”、“人定胜天”，做事没有节制和分寸，以对自己和周围的损害换取高人一等，现在想来只是不知道恐惧、也不知伤害为何物。&lt;br /&gt;
再后来总是被命运戏弄，了解到了人的局限性，知道了人不一定胜天，就多了些怯懦和贪婪，想苟活于世，尽可能向这个世界索取更多，达到一种“盈余”性质的无憾。&lt;br /&gt;
而现在终于理解了命运的本质：“命”为宿命，在人生的起点便已经达成了某种意义上的结束，是无法改变的一个范围；而“运”则为运势，是可以尽力去改变和拨弄的，而这种“拨弄”，就是在既定结局前的选择。&lt;br /&gt;
向世界索取一些，然后再向世界归还等价的一些。选择与代价的一体两面，成长所意味的背负，以负担换取生命的厚度。知晓恐惧后却仍要向前走，观测之后更重要的是付诸行动。&lt;br /&gt;
以前不理解为什么异域镇魂曲的主角在历经一切、得知真相的最后，选择放弃永生随意捡起一把武器冲向战场，现在我终于彻底理解了——&lt;/p&gt;
&lt;p&gt;“意义并不在于‘包罗万象’的观测，而存在于任一行动的回响之中。”&lt;/p&gt;
&lt;p&gt;这大概就是所谓“命运”在审美层次的真正内涵吧。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/resume/0.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;主要项目（工作I）&lt;/h2&gt;
&lt;h3&gt;Supercell游戏迁移&lt;/h3&gt;
&lt;p&gt;说明: 将Supercell的自研引擎,以及所有游戏迁移到小游戏平台.&lt;br /&gt;
规模: 超大&lt;br /&gt;
性质: 公司项目&lt;br /&gt;
职位: 技术组长&lt;br /&gt;
进度: 第一个游戏预备上线  &lt;/p&gt;
&lt;h3&gt;XR-FRAME 2.0&lt;/h3&gt;
&lt;p&gt;说明： 在微信小程序中，实现一套官方的混合渲染、高性能、易用、强扩展性、渐进式、遵循小程序开发标准的XR渲染方案。&lt;br /&gt;
规模： 大&lt;br /&gt;
性质： 公司项目&lt;br /&gt;
职位： 主要负责人，整体架构设计、客户端和前端实现。&lt;br /&gt;
进度： 第二版开发完毕，待上线。
使用技能：&lt;br /&gt;
图形学, 客户端开发，前端架构, 游戏引擎架构,WebGPU，Metal，XR, Typescript, C++，OC，JNI......&lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;基于1.0的前端架构，重构了后端实现。  &lt;/li&gt;
&lt;li&gt;负责了WebGPU的工具用渲染后端实现。&lt;/li&gt;
&lt;li&gt;负责了基于Google/Dawn项目的wgpu客户端渲染后端实现。  &lt;/li&gt;
&lt;li&gt;实现了XR系统，让项目可以在VR眼睛上运行。  &lt;/li&gt;
&lt;li&gt;实现过一个纯Metal的渲染后端，后来由于维护成本放弃。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;XR-FRAME&lt;/h3&gt;
&lt;p&gt;说明： 在微信小程序中，实现一套官方的混合渲染、高性能、易用、强扩展性、渐进式、遵循小程序开发标准的XR渲染方案。&lt;br /&gt;
规模： 大&lt;br /&gt;
性质： 公司项目&lt;br /&gt;
职位： 主要负责人，整体架构设计、渲染系统、资源系统、XR系统。&lt;br /&gt;
进度： 第一版已发布，第二版开发中&lt;br /&gt;
使用技能：&lt;br /&gt;
图形学, 前端架构, 游戏引擎架构, XR, Typescript, C++......&lt;br /&gt;
详见官网：&lt;a href=&amp;quot;https://developers.weixin.qq.com/miniprogram/dev/framework/xr-frame/index.html&amp;quot;&gt;微信小程序 XR-FRAME&lt;/a&gt;&lt;br /&gt;
惯例彩蛋：&lt;a href=&amp;quot;https://www.bilibili.com/video/BV12g411J76s&amp;quot;&gt;小程序示例官方AR彩蛋，开启传送门探寻世界真相&lt;/a&gt;&lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;负责了整体架构，在顶层实现了一个小程序的渲染后端，提供了给用户高度的组件定制、资源定制能力。  &lt;/li&gt;
&lt;li&gt;基于跨平台的渲染层（微信内是客户端实现），负责了主体渲染前端和WebGL后端的实现。&lt;/li&gt;
&lt;li&gt;其他重要系统，例如资源系统、AR系统、后处理系统等等的实现。  &lt;/li&gt;
&lt;li&gt;富有个人特色的文档，全面的数据上报监控。  &lt;/li&gt;
&lt;li&gt;和多个团队以及外部服务商对接合作，进行业务落地的推进。  &lt;/li&gt;
&lt;li&gt;第二版将实现3D和2D标签的混合，在GL中统一渲染，便于开发者实现更加酷炫的UI。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;微信小游戏高性能解决方案&lt;/h3&gt;
&lt;p&gt;说明： 在微信小游戏环境内提供逼近原生的性能。&lt;br /&gt;
规模： 超大&lt;br /&gt;
性质： 公司项目&lt;br /&gt;
职位： 主要负责渲染部分。&lt;br /&gt;
进度： 全新版本已发布&lt;br /&gt;
使用技能：&lt;br /&gt;
图形学, 游戏引擎架构, Typescript, C++......&lt;br /&gt;
详见官网：&lt;a href=&amp;quot;https://developers.weixin.qq.com/minigame/dev/guide/&amp;quot;&gt;微信小游戏框架&lt;/a&gt;&lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;参与渲染引擎设计，与同事一同决定了渲染部分的方案。  &lt;/li&gt;
&lt;li&gt;负责了整个渲染引擎前端部分的实现。  &lt;/li&gt;
&lt;li&gt;负责了整个引擎的Web跨端方案实现。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;项目已经支持不少项目，比如&lt;strong&gt;新轩辕传奇&lt;/strong&gt;、&lt;strong&gt;天龙八部荣耀版&lt;/strong&gt;，可在微信小程序内直接搜索游玩。&lt;/p&gt;
&lt;h3&gt;支付宝2020新春五福活动&lt;/h3&gt;
&lt;p&gt;说明： 负责&lt;strong&gt;首页3D展示&lt;/strong&gt;和&lt;strong&gt;福满全球&lt;/strong&gt;：&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/115989449&amp;quot;&gt;亿级前端项目中的3D技术-支付宝2020年新春活动的背后&lt;/a&gt;。&lt;br /&gt;
规模： 大&lt;br /&gt;
性质： 公司项目&lt;br /&gt;
职位： 项目前端负责人。&lt;br /&gt;
进度： 已上线&lt;br /&gt;
使用技能：SEIN.JS&lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;主会场3D模型，5个场景，极致优化，只占20M内存。&lt;/li&gt;
&lt;li&gt;福满全球，几乎所有元素都在3D场景内，所有材质均为定制，极致优化了内存。&lt;/li&gt;
&lt;li&gt;可靠的降级方案。&lt;/li&gt;
&lt;li&gt;覆盖面极广（10亿用户级），极低的闪退率。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;SEIN.JS&lt;/h3&gt;
&lt;p&gt;说明： Web3D游戏引擎。&lt;br /&gt;
规模： 超大&lt;br /&gt;
性质： 公司项目&lt;br /&gt;
职位： 项目负责人，负责内核开发，体系设计，跨BU合作项目管理，部分工具开发&lt;br /&gt;
进度： 已开源，持续迭代中&lt;br /&gt;
使用技能：&lt;br /&gt;
图形学, 游戏引擎架构, Typescript, Unity, Unreal4, C#, Webpack, WebAudio......&lt;br /&gt;
项目源：一系列项目，比如&lt;a href=&amp;quot;https://github.com/hiloteam/Sein.js&amp;quot;&gt;Sein.js&lt;/a&gt;、&lt;a href=&amp;quot;https://github.com/hiloteam/SeinJSUnityToolkit&amp;quot;&gt;SeinJSUnityToolkit&lt;/a&gt;和&lt;a href=&amp;quot;https://github.com/hiloteam/seinjs-gltf-loader&amp;quot;&gt;seinjs-gltf-loader&lt;/a&gt;等等...&lt;br /&gt;
惯例彩蛋：&lt;a href=&amp;quot;https://seinjs.com/cn/song&amp;quot;&gt;荒诞，艺术，存在&lt;/a&gt;&lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;游戏引擎本体&lt;/li&gt;
&lt;li&gt;空间音频系统、相机控制器、GUI等扩展系统(合作)和组件。&lt;/li&gt;
&lt;li&gt;Unity扩展&lt;/li&gt;
&lt;li&gt;CLI&lt;/li&gt;
&lt;li&gt;一系列Webpack扩展Loader以及Plugin&lt;/li&gt;
&lt;li&gt;VSCode扩展&lt;/li&gt;
&lt;li&gt;Inspector&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;项目已经支持内部不少业务。&lt;/p&gt;
&lt;h3&gt;Paradise平台&lt;/h3&gt;
&lt;p&gt;说明： 支付宝内部互动图形探索平台。&lt;br /&gt;
规模： 大&lt;br /&gt;
性质： 公司项目&lt;br /&gt;
职位： 项目前后端负责人&lt;br /&gt;
进度： 完成&lt;br /&gt;
使用技能：&lt;br /&gt;
Typescript，NodeJS，React, Mysql......&lt;br /&gt;
项目源：闭源&lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;前端以及服务端，拥有各种复杂的逻辑，包括但不限于创建作品、自动创建仓库、绑定HOOK自动构建、审核、测试、CLI、Lottie和3D模型预览发布等等。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Bilibili圣诞音游《Jingle Beats》&lt;/h3&gt;
&lt;p&gt;说明： &lt;a href=&amp;quot;https://www.bilibili.com/blackboard/activity-xmas2017.html&amp;quot;&gt;Bilibili圣诞音游《Jingle Beats》&lt;/a&gt;。&lt;br /&gt;
规模： 大&lt;br /&gt;
性质： 公司项目&lt;br /&gt;
职位： 项目前端负责人、开发者、原案&lt;br /&gt;
进度： 完成&lt;br /&gt;
使用技能：&lt;br /&gt;
PIXI.js, Typescript, WebGL, Tween，MUG、Touch Events......&lt;br /&gt;
项目源：&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/32298391&amp;quot;&gt;Bilibili圣诞游戏剖析-用pixi.js实现鬼畜音游&lt;/a&gt;&lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;一个HTML5的偏硬核音游，基于2D渲染引擎PIXI.js。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Bilibili七夕游戏《Double;7》&lt;/h3&gt;
&lt;p&gt;说明： &lt;a href=&amp;quot;http://www.bilibili.com/blackboard/double7.html&amp;quot;&gt;Bilibili七夕游戏《Double;7》&lt;/a&gt;。&lt;br /&gt;
规模： 大&lt;br /&gt;
性质： 公司项目&lt;br /&gt;
职位： 项目前端负责人、开发者、原案、制作进行&lt;br /&gt;
进度： 完成&lt;br /&gt;
使用技能：&lt;br /&gt;
Egret, Typescript, WebGL, Tween......&lt;br /&gt;
项目源：&lt;a href=&amp;quot;https://github.com/dtysky/egret-galgame&amp;quot;&gt;egret-galgame&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/28763290&amp;quot;&gt;Bilibili《七夕之约 - Double;7》技术剖析&lt;/a&gt;&lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;一个HTML5的Galgame，基于游戏引擎Egret，使用了陀螺仪、Websocket等技术，用技术为阿宅带来人文关怀。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;BML2017主视觉&lt;/h3&gt;
&lt;p&gt;说明： &lt;a href=&amp;quot;http://bml.bilibili.com/2017/index.html&amp;quot;&gt;BML2017主视觉&lt;/a&gt;。&lt;br /&gt;
规模： 大&lt;br /&gt;
性质： 公司项目&lt;br /&gt;
职位： 项目前端负责人和开发者&lt;br /&gt;
进度： 完成&lt;br /&gt;
使用技能：  &lt;br /&gt;
Node.js, React.js, Typescript, Less, Webpack......&lt;br /&gt;
项目源：&lt;a href=&amp;quot;http://dtysky.moe/article/Skill-2017_06_12_a&amp;quot;&gt;BML2017主视觉技术剖析&lt;/a&gt;  &lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;一个酷炫的主视觉，用到大量DOM动画和H5视频，PC和移动端双端适配，包含一个复杂的贴吧性质的讨论区。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Bilibili活动管理后台&lt;/h3&gt;
&lt;p&gt;说明： 一个超大型复杂管理后台。&lt;br /&gt;
规模： 超大&lt;br /&gt;
性质： 公司项目&lt;br /&gt;
职位： 核心开发者和架构者之一&lt;br /&gt;
进度： 完成&lt;br /&gt;
使用技能：  &lt;br /&gt;
Node.js, React.js, Redux, Express.js, ES6, Scss, Webpack......&lt;br /&gt;
项目源：闭源。&lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;大型活动管理后台，包括一个复杂CMS，数据源聚合管理，权限系统和发布系统。&lt;br /&gt;
全栈开发，前端技术栈为React + Redux + React-router + Immutable，后端技术栈为Node + Express。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;业余项目&lt;/h2&gt;
&lt;h3&gt;Project 31&lt;/h3&gt;
&lt;p&gt;说明： 三十一岁庆生短片。&lt;br /&gt;
规模： 中&lt;br /&gt;
性质： 严肃创作&lt;br /&gt;
职位： 负责人，剧本，演出，后期&lt;br /&gt;
进度： 完成&lt;br /&gt;
使用技能：写作，摄影，后期&lt;br /&gt;
完成内容：&lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://www.bilibili.com/video/BV16qpUepEGL/&amp;quot;&gt;我是「 」，祝我三十一岁生日快乐，我的故事仍在继续&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Project Destiny&lt;/h3&gt;
&lt;p&gt;说明： 三十岁庆生微电影。&lt;br /&gt;
规模： 中&lt;br /&gt;
性质： 严肃创作&lt;br /&gt;
职位： 负责人，剧本，演出，后期&lt;br /&gt;
进度： 完成&lt;br /&gt;
使用技能：写作，摄影，后期&lt;br /&gt;
完成内容：&lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://www.bilibili.com/video/BV1zr4y1d7o6/&amp;quot;&gt;三十岁生日，我给自己拍了部微电影&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Project Tomorrow&lt;/h3&gt;
&lt;p&gt;说明： 独立游戏。&lt;br /&gt;
规模： 大&lt;br /&gt;
性质： 梦想&lt;br /&gt;
职位： 负责人，剧本，程序，演出，出钱&lt;br /&gt;
进度： 剧本创作&lt;br /&gt;
使用技能：写作，游戏开发，花钱&lt;br /&gt;
完成内容：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;第二版剧本已完成，MVP版本创作中。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Awaken&lt;/h3&gt;
&lt;p&gt;说明： 基于Hybrid方案和WebDAV的全平台开源阅读软件。&lt;br /&gt;
规模： 中&lt;br /&gt;
性质： 产品输出&lt;br /&gt;
职位： 独立完成&lt;br /&gt;
项目源：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/Awaken&amp;quot;&gt;Awaken&lt;/a&gt;&lt;br /&gt;
使用技能：Hybrid,WebDAV,Android,iOS,Tauri,React&lt;br /&gt;
完成内容：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;功能介绍：&lt;a href=&amp;quot;https://www.bilibili.com/video/BV1uD4y1j79i&amp;quot;&gt;Awaken-开源跨平台多端同步阅读软件&lt;/a&gt;。 &lt;br /&gt;
技术教程：&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/594798266&amp;quot;&gt;Awaken-基于Hybrid方案和WebDAV的全平台开源阅读软件&lt;/a&gt;。 &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Project Love&lt;/h3&gt;
&lt;p&gt;说明： 29岁前交友企划，以及某种意义上的人生回顾。&lt;br /&gt;
规模： 中&lt;br /&gt;
性质： 记录&lt;br /&gt;
职位： 创作&lt;br /&gt;
进度： 完成&lt;br /&gt;
使用技能：写作，剪辑&lt;br /&gt;
完成内容：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;公众号发文：&lt;a href=&amp;quot;https://mp.weixin.qq.com/s/NNijyysyJwl9rQxNuA9BCA&amp;quot;&gt;Project Love&lt;/a&gt;&lt;br /&gt;
VLOG：&lt;a href=&amp;quot;https://www.bilibili.com/video/BV1dF411N7x6&amp;quot;&gt;某「非刻板印象INFP码农」的日常一天&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Project Self&lt;/h3&gt;
&lt;p&gt;说明： 28岁自我记录企划。&lt;br /&gt;
规模： 中&lt;br /&gt;
性质： 记录&lt;br /&gt;
职位： 需求定制，创作，演讲&lt;br /&gt;
进度： 完成&lt;br /&gt;
使用技能：写作，花钱&lt;br /&gt;
完成内容：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;写真集+文章：&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/507391278&amp;quot;&gt;Project Self&lt;/a&gt;&lt;br /&gt;
个人访谈：&lt;a href=&amp;quot;https://www.bilibili.com/video/BV1kY4y187my&amp;quot;&gt;访谈&lt;/a&gt;，&lt;a href=&amp;quot;https://mp.weixin.qq.com/s/M-x5wLvFEhRXAyZOmUlTUg&amp;quot;&gt;公众号发文&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;WebGPU Renderer &amp;amp; Path Tracer&lt;/h3&gt;
&lt;p&gt;说明： 一个基于WebGPU的渲染引擎，以及基于其实现的路径追踪渲染器，并产出了系列教程。&lt;br /&gt;
规模： 中&lt;br /&gt;
性质： 技术学习&lt;br /&gt;
职位： 独立完成&lt;br /&gt;
进度： 完成&lt;br /&gt;
项目源：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/webgpu-renderer&amp;quot;&gt;webgpu-renderer&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;https://dtysky.github.io/webgpu-renderer/&amp;quot;&gt;Live Demo&lt;/a&gt;&lt;br /&gt;
使用技能：  WebGPU, Typescript, 图形学&lt;br /&gt;
完成内容：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;使用WebGPU实现了一个渲染器，包含绝大部分3D渲染功能。 &lt;br /&gt;
利用渲染器，使用光栅化+计算着色器混合方式，实现了一个路径追踪渲染器，其中包括场景合并、BVH构建、射线求交、蒙特卡洛方法、BRDF、BSDF、降噪等。&lt;br /&gt;
产出了多篇系列教程：&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/407191699&amp;quot;&gt;WebGPU实时光追美少女系列&lt;/a&gt;。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Double;14&lt;/h3&gt;
&lt;p&gt;说明： 庆生用原创游戏。&lt;br /&gt;
规模： 中&lt;br /&gt;
性质： 生日纪念&lt;br /&gt;
职位： 剧本，程序&lt;br /&gt;
进度： 已完成&lt;br /&gt;
使用技能：&lt;br /&gt;
写作，编剧，Unity&lt;br /&gt;
项目源：  闭源 &lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;游戏录屏：  &lt;a href=&amp;quot;https://www.bilibili.com/video/BV1VL4y1v7Xj/&amp;quot;&gt;《Double;14》个人独立游戏&lt;/a&gt;
小说版本：  &lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/401016548&amp;quot;&gt;Double;14&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;gl-matrix-wasm&lt;/h3&gt;
&lt;p&gt;说明： 将&lt;a href=&amp;quot;https://github.com/toji/gl-matrix&amp;quot;&gt;gl-matrix&lt;/a&gt;移植到WebAssembly。&lt;br /&gt;
规模： 中&lt;br /&gt;
性质： 技术输出&lt;br /&gt;
职位： 独立完成&lt;br /&gt;
进度： 完成&lt;br /&gt;
项目源：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/gl-matrix-wasm&amp;quot;&gt;gl-matrix-wasm&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;http://gl-matrix-wasm.dtysky.moe/&amp;quot;&gt;Live Demo&lt;/a&gt;&lt;br /&gt;
使用技能：  Rust，WebAssembly&lt;br /&gt;
完成内容：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;使用Rust + wasm-bindgen + wasm-pack技术栈。&lt;br /&gt;
提供分离、单JS两种形式。&lt;br /&gt;
包含完全的单元测试，以及完整的Benchmark。   &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;D2-2017前端论坛嘉宾&lt;/h3&gt;
&lt;p&gt;说明： 代表B站，在D2-2017前端论坛进行了分享，中心主题和PPT在这里：&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/32104703&amp;quot;&gt;D2-现代前端-对视觉和交互的探索&lt;/a&gt;。&lt;br /&gt;
规模： 中小&lt;br /&gt;
性质： 分享&lt;br /&gt;
职位： 独立完成&lt;br /&gt;
进度： 完成&lt;br /&gt;
使用技能：  PPT，演讲......  &lt;/p&gt;
&lt;h3&gt;kanata&lt;/h3&gt;
&lt;p&gt;说明： 一个纯前端实现、无依赖的图像处理库，纯TypeScript编写，并在计划WebAssembly的版本。&lt;br /&gt;
规模： 中&lt;br /&gt;
性质： 技术输出&lt;br /&gt;
职位： 独立完成&lt;br /&gt;
进度： 开发中&lt;br /&gt;
项目源：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/kanata&amp;quot;&gt;kanata&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;http://kanata.dtysky.moe/&amp;quot;&gt;Live Demo&lt;/a&gt;&lt;br /&gt;
使用技能：  TypeScript，React.js，数字图像处理......&lt;br /&gt;
完成内容：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;主要包含一个ImageCore和若干点操作、几何变换、直方图操作和局部滤波器。&lt;br /&gt;
本身支持单操作引用，设计上支持流式处理，为敏感的性能做了一些优化，优于市面上很多库。&lt;br /&gt;
目前已然实现了许多操作并搭好了测试框架、写了一部分测试，剩下的操作正在实现中。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;hana-ui&lt;/h3&gt;
&lt;p&gt;说明：和两位队友一起实现的一个React UIKit，作为本项目的负责人，我设计项目框架，完成了项目开发、文档和DEMO的整体结构。除了设计，我也承担了相当的组件开发，在这个过程中我对React、工程化和样式的理解都大有进步。&lt;br /&gt;
规模： 中&lt;br /&gt;
性质： 技术输出&lt;br /&gt;
职位： 独立完成&lt;br /&gt;
进度： 已完成并在内部系统可用、部分外部系统可用，开源准备中。&lt;br /&gt;
项目源：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/hana-group/hana-ui&amp;quot;&gt;hana-ui&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;https://hana-ui.moe/&amp;quot;&gt;Homepage&lt;/a&gt;&lt;br /&gt;
惯例彩蛋：&lt;a href=&amp;quot;https://hana-ui.moe/cn/hana-song&amp;quot;&gt;hana song&lt;/a&gt;&lt;br /&gt;
使用技能：  React.js, ES6, Scss......&lt;/p&gt;
&lt;h3&gt;BlogReworkPro&lt;/h3&gt;
&lt;p&gt;说明： 重构&lt;a href=&amp;quot;http://dtysky.moe/article/Create-2016_03_12_a&amp;quot;&gt;BlogRework&lt;/a&gt;，这是此Blog迎来的第四次重构了，和上一次的间隔比预期要早一些，不过这种事早点没啥坏处。这次重构主要是重写了前端、修了一些后端的BUG，跟进ES6，用Eslint和Flow约束代码规范，上了React最佳实践全家桶并且实现了完美的服务端渲染，加了Memory Cache，样式换成了less，DOM语义化也做了，构建工具也换成了gulp，也就是说，上一次遗留的Feature基本都搞定了。&lt;br /&gt;
规模： 中小&lt;br /&gt;
性质： 新技术学习实践&lt;br /&gt;
职位： 独立完成&lt;br /&gt;
进度： 完成&lt;br /&gt;
使用技能：  &lt;br /&gt;
Python, Node.js, Flask, React.js, Redux, MongoDB, Express.js......&lt;br /&gt;
项目源：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/BlogReworkPro&amp;quot;&gt;BlogRewrokPro&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;http://dtysky.moe/article/Create-2016_10_14_a&amp;quot;&gt;【React/Flask】BlogReworkPro-Rework the BlogRework&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;http://dtysky.moe/article/Skill-2016_10_09_a&amp;quot;&gt;【React/Redux/Router/Immutable】React最佳实践的正确食用姿势&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;http://dtysky.moe/article/Skill-2016_10_11_a&amp;quot;&gt;【React/Redux】深入理解React服务端渲染&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;http://dtysky.moe/article/Skill-2016_10_13_a&amp;quot;&gt;【Flask/React】此博客服务端的缓存实现&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;http://dtysky.moe/article/Skill-2016_10_14_a&amp;quot;&gt;【Less】实现可选参数以及各种autoprefixer&lt;/a&gt;&lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;服务端渲染。  &lt;/li&gt;
&lt;li&gt;内存缓存。  &lt;/li&gt;
&lt;li&gt;资源优化。  &lt;/li&gt;
&lt;li&gt;SEO。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;MoeNotes&lt;/h3&gt;
&lt;p&gt;说明： &lt;strong&gt;正在使用Redux + RXJS重构。&lt;/strong&gt;MoeNotes是一个简单的日记写作软件（当然，我也用其写一些不是特别复杂的小说和文章），不同于印象笔记、Onenote、Leanote等，它所要解决的问题仅仅是“本地展示”，也就是在本地管理你的日志文件，并提供一个类似于Onenote的分类体验。本软件使用Markdown作为笔记编写语言，每一篇日记都会以.md文件的形式保存到本地而不是数据库中，用户可以自行选择如何同步这些文章以及将它们同步到多少地方，完全不依赖于平台，这也是我编写本软件而不使用现成日记软件的一个主要的原因。当然，对文章和分类进行拖动排序也是被支持的。除此之外，本软件还支持“即写即看”，“专注写作”和“专注阅读”三种模式，也可以进行主题的切换和自定义，由于是基于web进行得开发，所以扩展起来也十分方便和简单。&lt;br /&gt;
规模： 中&lt;br /&gt;
性质： 造一个满足自己需求的轮子&lt;br /&gt;
职位： 独立完成&lt;br /&gt;
进度： 完成&lt;br /&gt;
使用技能：  &lt;br /&gt;
Javacript, Node.js, React.js, Electron, HTML, CSS, Grunt, Webpack, Jasmine......&lt;br /&gt;
项目源：&lt;br /&gt;
&lt;a href=&amp;quot;http://moe-notes.dtysky.moe&amp;quot;&gt;MoteNotes&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/MoeNotes&amp;quot;&gt;Github&lt;/a&gt;&lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;本地文件树管理。&lt;/li&gt;
&lt;li&gt;OneNote的分类体验，增强版Markdown支持。&lt;/li&gt;
&lt;li&gt;即使预览、专注写作和专注阅读模式。&lt;/li&gt;
&lt;li&gt;可定制主题。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;BlogRework&lt;/h3&gt;
&lt;p&gt;说明： 重构Blog为SPA。&lt;br /&gt;
规模： 小&lt;br /&gt;
性质： 新技术学习实践&lt;br /&gt;
职位： 独立完成&lt;br /&gt;
进度： 完成&lt;br /&gt;
使用技能：  &lt;br /&gt;
Python, Node.js, Flask, React.js, MongoDB, Express.js, Jade, CSS......&lt;br /&gt;
项目源：&lt;br /&gt;
&lt;a href=&amp;quot;http://dtysky.moe/article/Create-2016_03_12_a&amp;quot;&gt;[Flask/React/MongoDB]BlogReworkIII-如何搭建一个动态Blog&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/BlogRework&amp;quot;&gt;BlogRewrok&lt;/a&gt;&lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;扩展Markdown文章解析以及数据库管理。&lt;/li&gt;
&lt;li&gt;后端Web服务器。&lt;/li&gt;
&lt;li&gt;前端界面。&lt;/li&gt;
&lt;li&gt;前端WEB服务器。&lt;/li&gt;
&lt;li&gt;SEO。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2&gt;主要项目（工作II）&lt;/h2&gt;
&lt;h3&gt;激光雷达上位机&lt;/h3&gt;
&lt;p&gt;说明： 用于展示激光雷达的3D数据，包括实时显示、数据录制、数据回放。&lt;br /&gt;
职位： 负责人&lt;br /&gt;
进度： 基本功能完成&lt;br /&gt;
使用技能：&lt;br /&gt;
JavaScript, C++, Python, Electron, Node.js, React, Three.js, HTML, CSS, Webpack, Grunt
项目源：  闭源 &lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;整体功能分析，实现了UI的设计和所有功能的开发。  &lt;/li&gt;
&lt;li&gt;用Electron作为框架，使用web的方式开发了桌面应用。  &lt;/li&gt;
&lt;li&gt;编写了C++的Node.js扩展，提高了点云坐标系转换效率。   &lt;/li&gt;
&lt;li&gt;用Three.js完成了点云的绘制。  &lt;/li&gt;
&lt;li&gt;实现了数据、显示、控制的隔离，自己实现了事件驱动的设计，保证可维护性。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;无人机甲烷监控数据可视化平台&lt;/h3&gt;
&lt;p&gt;说明： 将后台数据库进行表格形式的显示，兼容WEB、iOS和安卓三个平台。&lt;br /&gt;
职位： 负责人&lt;br /&gt;
进度： 基本完成&lt;br /&gt;
使用技能：&lt;br /&gt;
JavaScript, Python, Flask, MySQL, HTML, CSS, PyJade, Node.js, React native, Object-C&lt;br /&gt;
项目源：  闭源 &lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;开发基于Flask的后端以及使用PyJade模板进行网页显示  &lt;/li&gt;
&lt;li&gt;开发了一版Object-C编写的APP，用于熟练iOS底层。   &lt;/li&gt;
&lt;li&gt;用React native开发了iOS和安卓的APP。  &lt;/li&gt;
&lt;li&gt;设计了美观的UI。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;PM25传感器数据监测平台&lt;/h3&gt;
&lt;p&gt;说明： 接手项目，开发一个数据监测平台，收集不同地方的传感器传回的数据，进行记录和分析&lt;br /&gt;
职位： 负责人之一&lt;br /&gt;
进度： 正在进行&lt;br /&gt;
使用技能：&lt;br /&gt;
JavaScript，Node， React， MongoDB， Grunt， Python， HTML， CSS&lt;br /&gt;
平台：&lt;br /&gt;
OSX（开发），Ubuntu（部署）&lt;br /&gt;
项目源：  闭源 &lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;开发基于Node.js的后端  &lt;/li&gt;
&lt;li&gt;开发基于React.js的前端，用Grunt管理。   &lt;/li&gt;
&lt;li&gt;包含数据库管理，数据下载，数据呈现等功能。  &lt;/li&gt;
&lt;li&gt;做了设计，美化了UI。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;无人机定位&lt;/h3&gt;
&lt;p&gt;说明： 寻找并实现无人机的一些定位方式&lt;br /&gt;
职位： 负责人&lt;br /&gt;
进度： 基本完成&lt;br /&gt;
使用技能：&lt;br /&gt;
Python&lt;br /&gt;
平台：&lt;br /&gt;
Windows（PC），Ubuntu（Ordroid）&lt;br /&gt;
项目源：  闭源 &lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;差分GPS的研究和应用。  &lt;/li&gt;
&lt;li&gt;激光定位的研究和应用。  &lt;/li&gt;
&lt;li&gt;UWB定位的研究和应用。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2&gt;主要项目（大学）&lt;/h2&gt;
&lt;h3&gt;梦见星空之诗 - Aria der Freiheit und des Seins：&lt;/h3&gt;
&lt;p&gt;说明： 原创游戏。&lt;br /&gt;
规模： 超大&lt;br /&gt;
性质： 梦想&lt;br /&gt;
职位： 企划，监督，设定，剧本，程序&lt;br /&gt;
进度： 长期规划，剧本重构中&lt;br /&gt;
使用技能（已经）：&lt;br /&gt;
哲学（半吊子），设定，写作，编剧，Python，Ren&amp;apos;py&lt;br /&gt;
使用技能（预定）：&lt;br /&gt;
Unity3d，编译器设计，FPGA设计，商学&lt;br /&gt;
项目源：  闭源 &lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;游戏剧本解析器。  &lt;/li&gt;
&lt;li&gt;70万字剧本原稿。  &lt;/li&gt;
&lt;li&gt;游戏系统和界面（暂定）。 &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;体三维显示器：&lt;/h3&gt;
&lt;p&gt;说明： 一个分辨率较高的、由二维LED点阵旋转的三维显示器。&lt;br /&gt;
规模： 大&lt;br /&gt;
性质： 省级SRTP&lt;br /&gt;
职位： 独立完成&lt;br /&gt;
进度： 基本完成&lt;br /&gt;
使用技能：&lt;br /&gt;
PCB，FPGA，C，C#，Autocad&lt;br /&gt;
项目源：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/3D_Displayer_Controller&amp;quot;&gt;控制&lt;/a&gt;，&lt;a href=&amp;quot;https://github.com/dtysky/Led_Array&amp;quot;&gt;PCB&lt;/a&gt;，&lt;a href=&amp;quot;https://github.com/dtysky/3D_Displayer_Machine&amp;quot;&gt;机械&lt;/a&gt;&lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;学习Cadence套件，设计了一个由0603LED，PMOS，NMOS构成的120*114，密度为15.2d、cm^2的LED阵列；同时，完成了由触发器、锁存器、电源部分构成的此LED阵列的控制板，控制板为四层，走线密度较高。  &lt;/li&gt;
&lt;li&gt;学习使用FPGA，利用Altera的CycloneIV系列的器件，使用VHDL语言完成了：&lt;br /&gt;
1).DDR2控制器，完全由自己实现，并自己编写Testbench，利用Modelsim进行了批量数据验证，但缺少PHY的支持。&lt;br /&gt;
2).LED控制器，完全由自己实现，使用ROM进行了板上验证。&lt;br /&gt;
3).学习使用高速USB芯片cy68013，熟悉其slave-fifo模式，完成了它的FPGA控制部分，进行了板上验证。  &lt;/li&gt;
&lt;li&gt;学习使用C#语言，使用cy68013芯片的提供的.  net下的API，完成了其上位机。   &lt;/li&gt;
&lt;li&gt;学习C语言，完成了cy68013的固件部分设计。  &lt;/li&gt;
&lt;li&gt;机械部分的设计，设计了底座、联轴器，以及LED阵列的装载板；使用Autocad设计。  &lt;/li&gt;
&lt;li&gt;基于Matlab的三维切片。  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;游戏剧本解析器：&lt;/h3&gt;
&lt;p&gt;说明： 一个分离剧本和代码的解析器，用于将自己设计的DSL解析成Ren&amp;apos;py的脚本，提供扩展和输入插件。&lt;br /&gt;
规模： 中&lt;br /&gt;
性质： 个人项目&lt;br /&gt;
职位： 独立完成&lt;br /&gt;
进度： 基本完成&lt;br /&gt;
使用技能：  Python，Ren&amp;apos;py&lt;br /&gt;
项目源：  &lt;a href=&amp;quot;https://github.com/dtysky/Gal2Renpy&amp;quot;&gt;Gal2Renpy&lt;/a&gt;&lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;学习使用游戏引擎ren’py和Python，了解了各自特性。  &lt;/li&gt;
&lt;li&gt;DSL的形式继承自xml语言，自己完成了标记语言的解析，以及与ren’py脚本的映射,同时提供给用户自行扩展的接口，具有良好的可扩展性。  &lt;/li&gt;
&lt;li&gt;Sublime的插件，方便输入。  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;FPGA Image Library：&lt;/h3&gt;
&lt;p&gt;说明： 一个开源的FPGA图像处理库，将一些图像处理操作在FPGA上实现，每一个操作都会封装成一个模块，并拥有各自的软件测试与功能仿真，测试图片可以自由选取,并且设计为统一接口，流水化处理。&lt;br /&gt;
规模： 大&lt;br /&gt;
性质： 独立项目&lt;br /&gt;
职位： 独立完成&lt;br /&gt;
进度： &lt;a href=&amp;quot;http://fil.dtysky.moe&amp;quot;&gt;1.0版本发布&lt;/a&gt;，由于工作原因暂时中断= =&lt;br /&gt;
使用技能：  FPGA设计（Verilog, SystemVerilog），Python，图像处理 &lt;br /&gt;
项目源：  &lt;a href=&amp;quot;https://github.com/dtysky/FPGA-Imaging-Library&amp;quot;&gt;FPGA-Imaging-Library&lt;/a&gt; &lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;灰度化，二值化，亮度和对比度变换。  &lt;/li&gt;
&lt;li&gt;彩色字符输出（任意字体）（暂停）。  &lt;/li&gt;
&lt;li&gt;帧控制器，行缓存生成器，窗口生成器。  &lt;/li&gt;
&lt;li&gt;窗口均值滤波，二值腐蚀膨胀，二值模板匹配，灰度腐蚀膨胀，排序滤波器。  &lt;/li&gt;
&lt;li&gt;Harris角点检测。  &lt;/li&gt;
&lt;li&gt;平移，缩放，镜像，裁剪，旋转，仿射变换。  &lt;/li&gt;
&lt;li&gt;预计加入直方图操作。  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;我的主页：&lt;/h3&gt;
&lt;p&gt;说明： 一个用于记录自己学习经验的博客。&lt;br /&gt;
规模： 小&lt;br /&gt;
性质： 个人项目&lt;br /&gt;
职位： 独立完成&lt;br /&gt;
进度： 完成&lt;br /&gt;
使用技能 HTML，CSS，JS&lt;br /&gt;
项目源：  不开源
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;学习使用pelican(基于jinjia2)，学习css、html、js，在VPS上搭建。  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;一个面向教学的单周期MIPS CPU：&lt;/h3&gt;
&lt;p&gt;说明： 一个MIPS CPU，单周期32bits，vivado搭建，模块化设计，设立了tcl文件简化预览。&lt;br /&gt;
规模： 小&lt;br /&gt;
性质： 实习项目&lt;br /&gt;
职位： 独立完成&lt;br /&gt;
进度： 完成&lt;br /&gt;
使用技能：FPGA设计, Verilog, Systemverilog, Python &lt;br /&gt;
项目源：  &lt;a href=&amp;quot;https://github.com/dtysky/SIMPLE_MIPS_CPU&amp;quot;&gt;SIMPLE_MIPS_CPU&lt;/a&gt;&lt;br /&gt;
完成内容：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;完成了ALU, REGFILE, CONTRLO_UNIT, DATAPATH, INST_MEM, DATA_MEM模块的设计与随机测试，并将它们拼接，完成了用于功能仿真的CPU。  &lt;/li&gt;
&lt;li&gt;完成了KEY2INST, SHOW_ON_LED模块的设计与测试，将它们拼接于以上模块中，设计了一台按键编码，用于CPU的板上测试。  &lt;/li&gt;
&lt;li&gt;tcl文件的建立，用户可以在vivado界面写source tcl文件来快速建立工程。  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;其他&lt;/h2&gt;
&lt;p&gt;欲戴皇冠，必承其重。&lt;/p&gt;
&lt;p&gt;一个人不可能被所有人理解，这几年遭受的攻击、嘲讽、中伤即从中来。  &lt;/p&gt;
&lt;p&gt;人要为其言论负责，表达的代价也即为此。&lt;/p&gt;
&lt;p&gt;如此坚持的结果应有两种可能——倘若无悔，那将是无上的荣耀；倘若后悔，也将是深刻的戏谑。&lt;/p&gt;
&lt;p&gt;但无论如何，这种种经历都会成为我一生的注脚。  &lt;/p&gt;
&lt;p&gt;当拥有了永恒的视角后，你可以选择入戏当演员去起舞，也可以选择出离来当个观众，但只有一点：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;作为演员的时候，我们不可忘却愤怒。&lt;br /&gt;
作为观众的时候，我们不可忘却叹息。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我将倾尽所能去体验、去观测、去记录、去表达。&lt;br /&gt;
不能迷惘、不许后悔、不准遗憾、不计代价、不可回头。&lt;br /&gt;
直至肉身灭亡的一刻到来。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Friday, 14 Mar 2025 00:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2025.03.14 00:00:article/Create-MyResume</guid>
<category>简历</category>
</item>

<item>
<title>Awaken-基于Hybrid方案和WebDAV的全平台开源阅读软件</title>
<link>http://dtysky.moe/article/Create-2022_12_27_a</link>
<description>&lt;p&gt;在本篇文章中，我将从技术选型开始，分享我在开发这个阅读软件过程中的一些经验和心得，也算是对个人技术道路的又一个路标。&lt;/p&gt;
&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;今年六月，亚马逊宣布Kindle将在次年退出中国市场，听到这个新闻的我开始寻找它的替代品，同时由于在思考后彻底失去了对内容平台的信任，我对这个替代品定了以下要求：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;存储不依赖于平台。&lt;/li&gt;
&lt;li&gt;能够跨平台和设备共享，要求支持桌面/安卓/iOS全端。&lt;/li&gt;
&lt;li&gt;允许笔记和进度同步。&lt;/li&gt;
&lt;li&gt;笔记协议开放，能够从Kindle导入。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;带着这些要求寻觅了许久后，我并未找到完全满足的软件。作为一个合格的程序员，我便自然有了自己去实现它的想法，这同时也可以作为我的最终项目《Project Journey》预热。但由于事情太多同时离Kindle完全停止运营为时尚早，我并未立即开始这个项目，而再次启动它的契机，则是九月份的另一个项目。&lt;/p&gt;
&lt;p&gt;在延续创作完去年的独立游戏《Project Tomorrow》的第二版剧本后，我陷入了美术的困境，为了避免一开始就把事情搞砸，我启动了一个另一个比较小的博客改版项目《Project Totem》来磨合和美术的相性，但很遗憾最终失败了（题外话也因此我打消了相当的找个美术对象、或者说合作伙伴对象的想法）。但无论现状如何，项目总要继续推进，于是我最终决定提高自己的艺术修养，让自己继程序、剧本后也成为美术总监把控全局，便于之后找外包。而这个设想前提则离不开读书，所以正好借这个机会完成这个读书软件。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我知道以上思维路径看起来很离谱，但我确实是这么想的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;从九月底开始，我便启动了这个命名为《Awaken》的项目，花了近两个月的业余时间，我终于将其完成。&lt;/p&gt;
&lt;p&gt;项目开源在此：&lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://github.com/dtysky/Awaken&amp;quot;&gt;https://github.com/dtysky/Awaken&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;功能介绍在此：&lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://www.bilibili.com/video/BV1uD4y1j79i/&amp;quot;&gt;Awaken-开源跨平台多端同步阅读软件&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;技术选型&lt;/h2&gt;
&lt;p&gt;经过这么多年的开发，我逐渐明白了技术选型的重要性。看起来轻松的方案最后可能会踩更多的坑，而看起来麻烦的方案可能却是弯路最少的，反而会达到稳定和效率的平衡。技术选型的前提是需求，而由前言中提到的需求可得最影响决策的两个需求是——“同步”和“全平台”。&lt;/p&gt;
&lt;p&gt;“同步”决定了C/S架构，继而需要考虑服务端存储和多端同步方案；而“全平台”则决定了客户端需要一种跨端方案。&lt;/p&gt;
&lt;h3&gt;服务端&lt;/h3&gt;
&lt;p&gt;一般来讲，服务端有两种选择：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;像常见平台那样，服务端除了存储还接管同步逻辑，书库一开始就存在于服务端，用户只是去获取并将其拉取到客户端，而笔记、进度等也都由服务端处理同步后下发。&lt;/li&gt;
&lt;li&gt;服务端只做存储，所有的同步逻辑交由客户端，书籍由客户端添加上传，笔记、进度也都在客户端运算。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;第一种方案的优势是逻辑中心化，清晰简单，要处理的状况少，但代价就是需要一个独立的服务端，而且书库的维护可能要另外写逻辑。比较适合大平台，但不适合个人维护，尤其在国内这种动辄要备案的环境更是巨麻烦。&lt;/p&gt;
&lt;p&gt;第二种方案的优势就是只需要一个远端存储方案，而代价就是逻辑是分布式的，状况比较多且复杂。这种方案也比较适合个人用户。&lt;/p&gt;
&lt;p&gt;综合权衡后我选择了第二种方案，那么接下来的问题只有一个了——如何选择存储方案。这个没什么说的，我选了&lt;strong&gt;WebDAV&lt;/strong&gt;：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;WebDAV （Web-based Distributed Authoring and Versioning） 一种基于 HTTP 1.1协议的通信协议。它扩展了HTTP 1.1，在GET、POST、HEAD等几个HTTP标准方法以外添加了一些新的方法，使应用程序可对Web Server直接读写，并支持写文件锁定(Locking)及解锁(Unlock)，还可以支持文件的版本控制。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;目前国外支持WebDAV的网盘有不少，国内的话就坚果云吧，我也一直用的这个。&lt;/p&gt;
&lt;h3&gt;客户端&lt;/h3&gt;
&lt;p&gt;客户端的选型要比服务端复杂一些，不同于服务端只负责存储，客户端要负责整个软件所有的逻辑。倘若只是单个平台还没啥，但一旦涉及到跨端就麻烦了，主要考虑最终效果和开发成本两点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;不同平台分别实现，优点是最终性能肯定好上限高，代价是承受不起的开发成本。&lt;/li&gt;
&lt;li&gt;Flutter，移动端效果应该不错，桌面端残废，开发效率中等吧。&lt;/li&gt;
&lt;li&gt;ReactNative，emmm...不想再踩一次坑，桌面端也差不多残废。&lt;/li&gt;
&lt;li&gt;Hybrid分层方案，在Webview跑的JS前端 + 客户端通过JSB/XHR拦截实现的后端，优点是能充分利用JS生态，代价是效果受到Webview约束，仍然要实现不同客户端的JSB拦截。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;其实不用再多分析大家也能看出来了，综合效果和开发成本，只有第四种方案是可行的：Hybrid本身就是一种业界早已成熟的方案，没有太多坑，不会出现原理上难以解决的工程问题，而且能充分利用JS生态也省去了很多功夫，我是没兴趣为了这么个次要项目重新造一些轮子。&lt;/p&gt;
&lt;h2&gt;开发流程&lt;/h2&gt;
&lt;p&gt;在具体的实现前，需要先确定整个开发测试和构建的流程。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2022_12_27a/1.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;如图，这里我按照一个过时前端老人的习惯，选择了&lt;code&gt;typescript&lt;/code&gt;作为主要开发语言，&lt;code&gt;webpack&lt;/code&gt;作为构建工具，使用&lt;code&gt;dev-server&lt;/code&gt;做为开发服务器，发布时稍微改动做下打包就行。同时为了在开发阶段测试&lt;code&gt;webdav&lt;/code&gt;协议，我写了&lt;code&gt;webdav.server.js&lt;/code&gt;在本地开了个服务。&lt;/p&gt;
&lt;p&gt;代码本体在&lt;code&gt;src&lt;/code&gt;下，&lt;code&gt;interfaces&lt;/code&gt;中包括后端接口协议和书籍同步接口协议的定义，&lt;code&gt;backend&lt;/code&gt;中是不同平台下后端的具体实现，&lt;code&gt;frontend&lt;/code&gt;最后则是具体的前端逻辑。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;platforms&lt;/code&gt;是不同平台下的项目工程。在开发阶段，我用&lt;code&gt;dev-server&lt;/code&gt;开个支持hot-load的本地服务器，以不同平台的Webview打开本地Url来方便调试；在发布阶段，我将产物构建到三个平台工程的指定目录下，再以后续会提到的手段加载。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;test&lt;/code&gt;中是提供测试的一些书籍和Kindle导出的笔记。&lt;/p&gt;
&lt;h2&gt;后端实现&lt;/h2&gt;
&lt;p&gt;客户端的后端部分主要负责通过一致的接口协议，将Native基础能力暴露给Webview前端使用。  &lt;/p&gt;
&lt;p&gt;在代码接口上，我在&lt;code&gt;src/interfaces/IWorker&lt;/code&gt;中定义了接口协议，并在&lt;code&gt;src/backend&lt;/code&gt;中在各端具体实现，同时在``&lt;/p&gt;
&lt;h3&gt;接口协议&lt;/h3&gt;
&lt;p&gt;分层设计最重要的是接口协议，我这里依照项目需求，设计了以下接口：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;TBaseDir&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;Books&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;Settings&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;None&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;TToastType&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;info&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;warning&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;error&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;interface&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IFileSystem&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;readFile&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;filePath&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;encoding&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;utf8&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;binary&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;baseDir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TBaseDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Promise&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;ArrayBuffer&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;writeFile&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;filePath&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;content&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;ArrayBuffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;baseDir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TBaseDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Promise&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;void&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;removeFile&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;filePath&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;baseDir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TBaseDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Promise&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;void&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;readDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;dirPath&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;baseDir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TBaseDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Promise&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;path&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;isDir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;boolean&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}[]&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;createDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;dirPath&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;baseDir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TBaseDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Promise&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;void&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;removeDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;dirPath&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;baseDir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TBaseDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Promise&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;void&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;exists&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;filePath&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;baseDir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TBaseDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Promise&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;boolean&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;interface&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IWorker&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;fs&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IFileSystem&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;loadSettings&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Promise&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ISystemSettings&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;saveSettings&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;settings&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;ISystemSettings&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Promise&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;void&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;selectFolder&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Promise&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;selectBook&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Promise&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[]&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;selectNote&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Promise&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[]&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;showMessage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;msg&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TToastType&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;title?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Promise&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;void&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;setBackground&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;r&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;g&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;b&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Promise&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;void&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;onAppHide&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;callback&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;void&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;void&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;getCoverUrl&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBook&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Promise&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;接口的方法名都很明显了，不做过多解释。接下来要做的就是在各端实现这个&lt;code&gt;IWorker&lt;/code&gt;接口。&lt;/p&gt;
&lt;h3&gt;桌面端&lt;/h3&gt;
&lt;p&gt;在桌面端，基于浏览器的方案有不少，比如最广为人知的&lt;code&gt;Electron&lt;/code&gt;，还有类似的&lt;code&gt;CEF&lt;/code&gt;等，其基本都是打包了一个&lt;code&gt;Chromium&lt;/code&gt;进去，在开发简单、一致性强、兼容性强等优点下，也有安装包大小和内存开销等为人诟病的代价。&lt;/p&gt;
&lt;p&gt;一开始我是准备直接用&lt;code&gt;Electron&lt;/code&gt;的，但由于其只支持桌面，想偷懒的我便不由自主得想：“都2022年了，这么热衷造轮子的前端业界，不会还没有能直接跨所有端的方案吧？”虽然答案仍然确实是没有，但却意外发现了一个框架——&lt;code&gt;Tauri&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Tauri&lt;/code&gt;是基于&lt;code&gt;Rust&lt;/code&gt;和&lt;code&gt;Webview&lt;/code&gt;的混合应用开发框架，其目前支持全桌面平台，并计划支持客户端（当然遥遥无期）。其优势是利用系统原生的Webview（不错桌面系统也有Webview），包体积很小并且内存开销会小一些，但相对代价就是很难利用&lt;code&gt;Node&lt;/code&gt;生态，并且可能存在平台一致性问题，在某些低版本操作系统不支持。&lt;/p&gt;
&lt;p&gt;经过权衡，最终我选择了&lt;code&gt;Tauri&lt;/code&gt;，因为这个应用并不需要什么扩展逻辑，只需要基本的文件系统、提示、桌面选择器等等基本能力，而这些它都有官方支持。接下来，就让我们看看怎么在桌面端实现这个接口。&lt;/p&gt;
&lt;h4&gt;FileSystem&lt;/h4&gt;
&lt;p&gt;首先是接口中的文件系统，看到方法名便可以知道它们本质上就是对本地文件的存取。这个在&lt;code&gt;Tauri&lt;/code&gt;中很简单，其官方提供的库&lt;code&gt;@tauri-apps/api&lt;/code&gt;中就有相关能力，只需要将其引入并在&lt;code&gt;tauri.conf.json&lt;/code&gt;中的&lt;code&gt;tauri.allowlist&lt;/code&gt;中配置好&lt;code&gt;fs&lt;/code&gt;的参数即可使用，比如：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;import&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;from&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;@tauri-apps/api&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;nx&amp;quot;&gt;async&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;readFile&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;filePath&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;encoding&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;utf8&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;binary&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;baseDir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TBaseDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;base&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;processPath&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;filePath&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;baseDir&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;encoding&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;utf8&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;fs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;readTextFile&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;base&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;dir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;base&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;})&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Promise&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;await&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;fs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;readBinaryFile&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;base&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;dir&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;base&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;})).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;buffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;注意到&lt;code&gt;baseDir&lt;/code&gt;这个参数，他指定了当前操作路径相对的目录，这里我用&lt;code&gt;TBaseDir&lt;/code&gt;指定，&lt;code&gt;Books&lt;/code&gt;表示用户指定的书籍目录，&lt;code&gt;Settings&lt;/code&gt;
则是用户个人配置目录，&lt;code&gt;None&lt;/code&gt;代表传入绝对路径。当然这些目录在不同平台下的表现不一致，在桌面端由于能够允许用户自己指定文件夹，所以&lt;code&gt;Books&lt;/code&gt;是用于配置的，&lt;code&gt;Settings&lt;/code&gt;则是&lt;code&gt;appDir&lt;/code&gt;。&lt;/p&gt;
&lt;h4&gt;其他接口&lt;/h4&gt;
&lt;p&gt;文件系统之外就是其他接口了，其中：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;loadSettings&lt;/code&gt;和&lt;code&gt;saveSettings&lt;/code&gt;只是存取&lt;code&gt;Settings/settings.json&lt;/code&gt;文件。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;selectFolder&lt;/code&gt;、&lt;code&gt;selectBook&lt;/code&gt;、&lt;code&gt;selectNote&lt;/code&gt;和&lt;code&gt;showMessage&lt;/code&gt;都可以用&lt;code&gt;@tauri-apps/api&lt;/code&gt;中的&lt;code&gt;dialog&lt;/code&gt;模块解决，前三个是&lt;code&gt;dialog.open&lt;/code&gt;，最后一个是&lt;code&gt;dialog.message&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;setBackground&lt;/code&gt;和&lt;code&gt;onAppHide&lt;/code&gt;在桌面端是不必要的。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getCoverUrl&lt;/code&gt;本质上是一种优化，这个会在前端部分说明。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;安卓端&lt;/h3&gt;
&lt;p&gt;不同于桌面端&lt;code&gt;Tauri&lt;/code&gt;帮我们搞定了大部分事情，移动端就要麻烦不少，安卓和iOS要去分别手动实现JS到客户端的绑定，不过好在如开头所说这个选型是比较稳健的，踩了点坑还算顺利。&lt;/p&gt;
&lt;p&gt;安卓的我用的是&lt;code&gt;Kotlin&lt;/code&gt;，写起来没&lt;code&gt;Java&lt;/code&gt;那么啰嗦，它的&lt;code&gt;Webview&lt;/code&gt;用起来是比较简单的，&lt;code&gt;JSBridge&lt;/code&gt;通过&lt;code&gt;addJavascriptInterface&lt;/code&gt;方法配合&lt;code&gt;@JavascriptInterface&lt;/code&gt;注解即可添加：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 定义JSB&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;class&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;AwakenJSB&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kd&amp;quot;&gt;@JavascriptInterface&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;fun&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;setBackground&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;r&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Double&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;g&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Double&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;b&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Double&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;// 注册JSB&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;webView&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;addJavascriptInterface&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;jsb&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!!&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;Awaken&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;很简单是吧？那么看起来我们只要通过这个JSB实现下文件之类的接口，也没多麻烦的样子？一开始我也是这么想的，但做起来后却发现没这么简单。&lt;/p&gt;
&lt;h4&gt;文件系统&lt;/h4&gt;
&lt;p&gt;JSB有个很大的问题是它只能传输&lt;strong&gt;基本类型&lt;/strong&gt;，也就是数字、字符串之类的，而不能传输二进制数据。但对于这个软件来说，电子书和封面都是二进制数据，如果全部都走JSB的话，只有一招——在一端把二进制数据转&lt;code&gt;base64&lt;/code&gt;，到了另一端再转回来。&lt;/p&gt;
&lt;p&gt;虽然理论上这没啥问题，对于绝大部分书籍而言（1M以内）转换的开销对于客户端或者V8 JIT加持下的JS绰绰有余，但每次都这么转一下对于我而言是难以接受的——即使到现在，我还是有那么一些完美主义倾向。&lt;/p&gt;
&lt;p&gt;那么是否存在一种方式，能够在双端不经转换地传输二进制数据呢？仔细想想，“由前端和其他端互相传输二进制数据”，这不就是&lt;code&gt;XHR&lt;/code&gt;或者说&lt;code&gt;fetch&lt;/code&gt;吗？至此，我的思路便从“如何用JSB传输二进制数据”变成了“如何拦截&lt;code&gt;XHR&lt;/code&gt;”。&lt;/p&gt;
&lt;p&gt;在稍许调研后，我便找到了安卓Webview提供的官方拦截方法：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;webView&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;webViewClient&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;object&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;WebViewClient&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;override&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;fun&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;shouldInterceptRequest&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;view&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;WebView?&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;request&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;WebResourceRequest?&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;WebResourceResponse&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;我们可以从&lt;code&gt;request&lt;/code&gt;最后拿到请求的&lt;code&gt;url&lt;/code&gt;，然后拆分出&lt;code&gt;host&lt;/code&gt;、&lt;code&gt;path&lt;/code&gt;和&lt;code&gt;query&lt;/code&gt;，进入下一步操作：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;host&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;equals&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;awaken.api&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;method&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;String&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;path&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;toString&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;().&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;substring&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;MutableMap&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;mutableMapOf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
        &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;method&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;to&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;request&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;method&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;queryParameterNames&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;forEach&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;it&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getQueryParameter&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;it&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;toString&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;jsb&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!!&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;callMethod&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;method&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;requestHeaders&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这里我以&lt;code&gt;awaken.api&lt;/code&gt;作为此类特殊接口的&lt;code&gt;host&lt;/code&gt;标识，以&lt;code&gt;path&lt;/code&gt;作为请求的方法名，以&lt;code&gt;query&lt;/code&gt;传递参数，一次接口调用转换为请求如下：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fetch&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;http&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;//awaken.api/${method}?${query}`, {&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;method&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;data&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;POST&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;body&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;data&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;识别到此类请求后，客户端会调用&lt;code&gt;jsb&lt;/code&gt;示例（直接复用了上面提到的JSB类）对应的方法，进行处理：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fun&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;callMethod&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;method&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;params&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Map&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;origHeaders&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Map&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;WebResourceResponse&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;val&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;headers&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;HashMap&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;headers&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;Access-Control-Allow-Origin&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;*&amp;quot;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;headers&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;Access-Control-Allow-Methods&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;*&amp;quot;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;headers&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;Access-Control-Expose-Headers&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;X-Error-Message, Content-Type, WWW-Authenticate&amp;quot;&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;try&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;err&amp;quot;&gt;处理实际逻辑&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;WebResourceResponse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Charsets&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;UTF_8&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;toString&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(),&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;200&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;OK&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;headers&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;stream&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;catch&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;error&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Exception&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;headers&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;X-Error-Message&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;error&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;message&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;toString&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
      &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;WebResourceResponse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;application/json&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;200&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;Error&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;headers&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;ByteArrayInputStream&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ByteArray&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)))&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这里要注意我在返回的&lt;code&gt;headers&lt;/code&gt;中都写入了允许跨域，这实际上也是为了解决后面章节的问题，而其中的&lt;code&gt;X-Error-Message&lt;/code&gt;这个字段是为了返回错误，至于为什么么...因为&lt;code&gt;iOS&lt;/code&gt;中的拦截无法返回自定义状态信息，安卓也只能跟着搞了。&lt;/p&gt;
&lt;p&gt;至于实际上的文件存取逻辑没什么好说的，查一下API就完了，唯一值得一提的点是我将用户文件都存在了&lt;code&gt;context.getExternalFilesDir(null)!!.toPath()&lt;/code&gt;取得的&lt;strong&gt;扩展外部存储&lt;/strong&gt;中。&lt;/p&gt;
&lt;p&gt;按理说到这了，文件系统应该OK了吧？既满足了需求，又能够避免&lt;code&gt;base64&lt;/code&gt;转换，简直完美！那我只能说太天真了。在实现过程中我很快就遇到了麻烦——我&lt;strong&gt;无法获取到&lt;code&gt;POST&lt;/code&gt;请求的&lt;code&gt;body&lt;/code&gt;&lt;/strong&gt;。并且在深度搜索的最后，也只找到了一个社区给谷歌在19年提的Issue，他们说在考虑支持然后就...没有然后了。&lt;/p&gt;
&lt;p&gt;所以虽然很不甘心，我最终也只能做了个特殊处理——如果是安卓平台并且是&lt;code&gt;writeFile&lt;/code&gt;接口，还是走JSBridge，也就是说在写入的情况下，二进制数据还是要经历 encodeBase64 -&amp;gt; 传到客户端 -&amp;gt; decodeBase64 的过程，具体的逻辑就不多说了也很简单。&lt;/p&gt;
&lt;p&gt;不过最终综合来看，从前端向客户端写入二进制数据的状况只有在添加书籍时，这&lt;strong&gt;在移动端是个非常低频的操作，远低于读取&lt;/strong&gt;，而读取的优化是不受这个影响的，整体仍然很赚。&lt;/p&gt;
&lt;h4&gt;其他接口&lt;/h4&gt;
&lt;p&gt;其他接口的实现就是完全通过JSBridge了，其实也没什么好说的，简单提一下吧。&lt;/p&gt;
&lt;p&gt;首先是&lt;code&gt;showMessage&lt;/code&gt;这个接口，实际上就是利用了客户端的&lt;code&gt;AlertDialog&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 定义&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;mAlertDialog&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;AlertDialog&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Builder&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;mContext&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;mAlertDialog&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setPositiveButton&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;OK&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;DialogInterface&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;OnClickListener&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;dialog&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;which&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;dialog&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cancel&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;mAlertDialog&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setNegativeButton&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;Close&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;DialogInterface&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;OnClickListener&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;dialog&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;which&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;dialog&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cancel&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;// 调用&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;mAlertDialog&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setTitle&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;title&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;mAlertDialog&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setMessage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;message&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;mAlertDialog&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;show&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;而&lt;code&gt;select&lt;/code&gt;系列就稍微有点麻烦了，实际上是实现了一个通用的&lt;code&gt;selectFiles&lt;/code&gt;接口：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;@JavascriptInterface&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;fun&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;selectFiles&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;title&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;mimeTypes&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Array&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;arrayOf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;mContext&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;selectFiles&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;mimeTypes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;it&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;mContext&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;mainWebView&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;evaluateJavascript&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
            &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;Awaken_SelectFilesHandler(${JSONArray(res)})&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
            &lt;span class=&amp;quot;nx&amp;quot;&gt;ValueCallback&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;首先注意这里的&lt;code&gt;evaluateJavascript&lt;/code&gt;，这是因为JSBridge都是同步调用，但这里面实际上执行了一个异步操作，所以为了通知前端，我执行了一个全局的JS方法&lt;code&gt;Awaken_SelectFilesHandler&lt;/code&gt;，而对应于前端则会在调用JSB方法前设置这个全局方法的值。&lt;/p&gt;
&lt;p&gt;而这里之所以会调用主&lt;code&gt;activity&lt;/code&gt;的接口然后回调，主要是因为安卓机制上的限制——唤起文件选择菜单实际上是启动另一个&lt;code&gt;activity&lt;/code&gt;，而我们需要重写主&lt;code&gt;activity&lt;/code&gt;的&lt;code&gt;onActivityResult&lt;/code&gt;方法，来获取结果：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fun&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;selectFiles&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;mimeTypes&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;callback&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;files&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Array&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Unit&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;selectFilesCallback&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;callback&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;val&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;intent&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Intent&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Intent&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ACTION_OPEN_DOCUMENT&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;apply&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;addCategory&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Intent&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;CATEGORY_OPENABLE&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;mimeTypes&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;putExtra&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Intent&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;EXTRA_ALLOW_MULTIPLE&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;c1&amp;quot;&gt;// 唤起菜单，指定状态码为4&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;startActivityForResult&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;intent&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;// &lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;override&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;fun&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;onActivityResult&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;requestCode&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Int&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;resultCode&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Int&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;resultData&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Intent?&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;requestCode&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;c1&amp;quot;&gt;// 处理状态码为4的结果&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;kr&amp;quot;&gt;super&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;onActivityResult&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;requestCode&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;resultCode&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;resultData&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;剩下的就是&lt;code&gt;onAppHide&lt;/code&gt;方法可以监听主&lt;code&gt;activity&lt;/code&gt;的&lt;code&gt;onPause&lt;/code&gt;周期，然后用&lt;code&gt;evaluateJavascript&lt;/code&gt;执行一个全局JS方法即可；&lt;code&gt;setBackground&lt;/code&gt;方法在安卓端并不需要，取而代之的是隐藏安卓的虚拟按键，这个本质上就是隐藏&lt;code&gt;ActionBar&lt;/code&gt;并进入全屏模式，代码自己看吧没啥特别要注意的。&lt;/p&gt;
&lt;h3&gt;iOS端&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;iOS&lt;/code&gt;的接口思路和安卓是完全一致的，不过由于平台差异，实现起来还是有些不同。由于实在不想写&lt;code&gt;OC&lt;/code&gt;了，这里我选择的是&lt;code&gt;Swift&lt;/code&gt;顺便带上&lt;code&gt;swiftui&lt;/code&gt;，同时为了JIT的性能用的是&lt;code&gt;WKWebView&lt;/code&gt;。和安卓类似，我同样需要注册&lt;code&gt;JSBridge&lt;/code&gt;和进行&lt;code&gt;XHR&lt;/code&gt;拦截，这里先说JSBridge，&lt;code&gt;XHR&lt;/code&gt;拦截就完全交给文件系统一节吧。&lt;/p&gt;
&lt;p&gt;WKWebView的JSBridge注册也很简单，首先我们要定义一个实现了&lt;code&gt;WKScriptMessageHandler&lt;/code&gt;协议的类&lt;code&gt;AwakenJSB&lt;/code&gt;，然后将其实例注册即可：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 实现JSB&lt;/span&gt;
&lt;span class=&amp;quot;kd&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;class&lt;/span&gt; &lt;span class=&amp;quot;nc&amp;quot;&gt;AwakenJSB&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;NSObject&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;WKScriptMessageHandler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;WKUIDelegate&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kd&amp;quot;&gt;init&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;onChangeBg&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;escaping&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kc&amp;quot;&gt;_&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;CGColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;())&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;


&lt;span class=&amp;quot;c1&amp;quot;&gt;// 以下代码在`swiftui`对应实现`UIViewRepresentable`协议的`WebView`类中&lt;/span&gt;
&lt;span class=&amp;quot;c1&amp;quot;&gt;// 实例化JSB&lt;/span&gt;
&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;jsb&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;AwakenJSB&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;onChangeBg&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;onChangeBg&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;// 初始化JS脚本&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;initJS&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;window.Awaken = {&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;    getPlatform() {&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;        return &amp;#39;IOS&amp;#39;;&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;    },&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;    showMessage(message, type, title) {&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;        window.webkit.messageHandlers.showMessage.postMessage({title: title, message: message, type: type || &amp;#39;&amp;#39;});&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;    },&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;    selectFiles(title, types) {&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;        window.Awaken.showMessage(&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;iOS&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;设备不支持导入本地书籍，请使用其他平台操作&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;error&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;, &amp;quot;&amp;quot;);&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;        window.Awaken_SelectFilesHandler([]);&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;    },&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;    setBackground(r, g, b) {&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;        window.webkit.messageHandlers.setBackground.postMessage({r: r, g: g, b: b});&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;    }&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;// 初始化WebView&lt;/span&gt;
&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;config&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;WKWebViewConfiguration&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;...&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;一些初始化逻辑&lt;/span&gt;
&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;wkWebView&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;WKWebView&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;frame&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;zero&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;configuration&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;// 注入初始化脚本&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;controller&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;addUserScript&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;bp&amp;quot;&gt;WKUserScript&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;source&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;initJS&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;injectionTime&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;atDocumentStart&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;forMainFrameOnly&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt;
&lt;span class=&amp;quot;c1&amp;quot;&gt;// 注册JSB&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;controller&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;add&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kc&amp;quot;&gt;self&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;showMessage&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;controller&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;add&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kc&amp;quot;&gt;self&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;setBackground&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;code&gt;onChangeBg&lt;/code&gt;回调这里暂时无需在意，其他除了流程和安卓大差不差，唯一有显著区别的就是&lt;code&gt;initJS&lt;/code&gt;了，这是一段会在WKWebView加载完&lt;code&gt;html&lt;/code&gt;、执行用户&lt;code&gt;js&lt;/code&gt;前注入的一段&lt;code&gt;js&lt;/code&gt;代码。可以看到其实际上给全局挂载了一些方法，而这些方法在安卓中是直接用&lt;code&gt;@JavascriptInterface&lt;/code&gt;注解实现在JSB类中的。再看每个方法的实现，除了&lt;code&gt;iOS&lt;/code&gt;无法实现文件选择外导致无效的&lt;code&gt;selectFiles&lt;/code&gt;外，都有个调用&lt;code&gt;window.webkit.messageHandlers.xxxx.postMessage&lt;/code&gt;，这是因为WKWebView只支持前端和客户端的&lt;strong&gt;异步通信&lt;/strong&gt;，只能这么搞，好在这几个接口基本都不需要返回值，随便搞搞就行了。在&lt;code&gt;js&lt;/code&gt;中&lt;code&gt;postMessage&lt;/code&gt;后，我们还需要在客户端稍微处理下：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;func&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;userContentController&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kc&amp;quot;&gt;_&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;userContentController&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;WKUserContentController&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;didReceive&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;message&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;WKScriptMessage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;message&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;name&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;==&lt;/span&gt; &lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;showMessage&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;params&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;message&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;body&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;as&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;!&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;showMessage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;message&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;message&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;title&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;message&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;name&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;==&lt;/span&gt; &lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;setBackground&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;params&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;message&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;body&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;as&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;!&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;Double&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;setBackground&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;r&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;g&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;g&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;b&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Swift是我写过的第二啰嗦的语言，苹果的API设计不敢恭维，XCode就是依托答辩。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;文件系统&lt;/h4&gt;
&lt;p&gt;iOS端的&lt;code&gt;XHR&lt;/code&gt;拦截的方式和安卓大同小异，不过这API搞起来虽然蛋疼，但人家却支持了获取&lt;code&gt;request&lt;/code&gt;的&lt;code&gt;body&lt;/code&gt;...要我说你两就不能合计合计整个完全体吗？&lt;/p&gt;
&lt;p&gt;不吐槽了...来看看怎么搞吧，iOS提供了&lt;code&gt;WKURLSchemeHandler&lt;/code&gt;协议来为WKWebView提供&lt;code&gt;XHR&lt;/code&gt;拦截：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 实现协议的类&lt;/span&gt;
&lt;span class=&amp;quot;kd&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;class&lt;/span&gt; &lt;span class=&amp;quot;nc&amp;quot;&gt;AwakenXHRHandler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;NSObject&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;WKURLSchemeHandler&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;c1&amp;quot;&gt;// 请求开始&lt;/span&gt;
    &lt;span class=&amp;quot;kd&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;func&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;webView&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kc&amp;quot;&gt;_&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;webView&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;WKWebView&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;start&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;urlSchemeTask&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;WKURLSchemeTask&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;c1&amp;quot;&gt;// 请求结束&lt;/span&gt;
    &lt;span class=&amp;quot;kd&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;func&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;webView&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kc&amp;quot;&gt;_&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;webView&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;WKWebView&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;stop&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;urlSchemeTask&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;WKURLSchemeTask&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;// WKWebView的那个`config`，注册拦截器，传入`jsb`只是为了和安卓一样复用JSB类实现逻辑&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;setURLSchemeHandler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;AwakenXHRHandler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;jsb&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;jsb&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;forURLScheme&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;awaken&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这里特别要注意的、和安卓不同的是&lt;code&gt;forURLScheme&lt;/code&gt;这个参数，它指定了一个请求的&lt;code&gt;schema&lt;/code&gt;，因为理论上iOS不允许开发者拦截WKWebView的标准协议，类似&lt;code&gt;http&lt;/code&gt;/&lt;code&gt;https&lt;/code&gt;等等，所以这里我必须指定一个自定义的&lt;code&gt;awaken&lt;/code&gt;，实际请求时为：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fetch&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;awaken&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;//awaken.api/${method}?${query}`, {&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;method&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;data&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;POST&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;body&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;data&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;不过在安卓上用自定义&lt;code&gt;schema&lt;/code&gt;请求会报错，也是够麻烦的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;由于WKWebView允许获取拦截到的请求的&lt;code&gt;body&lt;/code&gt;，所以也不用像安卓那样麻烦地去搞什么base64转换了：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;request&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;urlSchemeTask&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;request&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;guard&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;requestUrl&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;request&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;url&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;method&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;requestUrl&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;path&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;method&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;method&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;==&lt;/span&gt; &lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;method&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;method&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;method&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;index&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;method&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;startIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;offsetBy&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)...])&lt;/span&gt;
&lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[:]&lt;/span&gt;
&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;components&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;URLComponents&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;requestUrl&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;resolvingAgainstBaseURL&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;queryItems&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;components&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;?.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;queryItems&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;queryItems&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;nil&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;params&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;queryItems&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;!.&lt;/span&gt;&lt;span class=&amp;quot;bp&amp;quot;&gt;reduce&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;into&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]())&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;result&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;item&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;in&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;result&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;item&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;item&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;value&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;body&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;request&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;httpBody&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;取得了&lt;code&gt;method&lt;/code&gt;和&lt;code&gt;params&lt;/code&gt;后，就可以正常处理请求并返回了，这个并没有什么麻烦的，感兴趣可以直接去项目看代码，唯一值得说道的下面几点：&lt;/p&gt;
&lt;p&gt;其一，如何获取用户目录：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;paths&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;NSSearchPathForDirectoriesInDomains&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;documentDirectory&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;userDomainMask&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;mBaseDir&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;URL&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;fileURLWithPath&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;paths&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;bp&amp;quot;&gt;last&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;!,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;isDirectory&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;其二，如何返回&lt;code&gt;response&lt;/code&gt;。一般的教程中都会说返回&lt;code&gt;URLResponse&lt;/code&gt;，但这个无法自定义状态码和&lt;code&gt;headers&lt;/code&gt;，无法满足需求，在调研后我最终找到了&lt;code&gt;HTTPURLResponse&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;response&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;HTTPURLResponse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;requestUrl&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;statusCode&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;200&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;httpVersion&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;nil&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;headerFields&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;headers&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h4&gt;其他接口&lt;/h4&gt;
&lt;p&gt;和安卓相同，其它接口都是通过JSBridge实现的。&lt;/p&gt;
&lt;p&gt;首先是&lt;code&gt;showMessage&lt;/code&gt;这个接口，得益于iOS的天才API设计和每升一个版本就相当于另一门语言的&lt;code&gt;swift&lt;/code&gt;，恕我懒得搞清楚它背后做了什么，直接用吧：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;func&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;showMessage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;message&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;title&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;alert&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;UIAlertController&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;title&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;title&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;message&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;message&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;preferredStyle&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;UIAlertController&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Style&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;alert&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;alert&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;addAction&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;bp&amp;quot;&gt;UIAlertAction&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;title&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;确定&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;style&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;UIAlertAction&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Style&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;default&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;handler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;nil&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt;

    &lt;span class=&amp;quot;bp&amp;quot;&gt;UIApplication&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;shared&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;connectedScenes&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;flatMap&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;$0&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;as&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;UIWindowScene&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)?.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;windows&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;??&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[]&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;bp&amp;quot;&gt;first&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;$0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;isKeyWindow&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;}?&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;rootViewController&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;?&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;present&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;alert&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;animated&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;completion&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;nil&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;然后是&lt;code&gt;onAppHide&lt;/code&gt;，这个也很简单，我们首先要让&lt;code&gt;AwakenJSB&lt;/code&gt;持有WKWebView实例，然后配合&lt;code&gt;NotificationCenter&lt;/code&gt;实现：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;public&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;func&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;setWebview&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;webview&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;WKWebView&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;kc&amp;quot;&gt;self&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;webview&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;webview&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;NotificationCenter&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;default&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;addObserver&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kc&amp;quot;&gt;self&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;selector&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;#&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;selector&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;didEnterBackground&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;name&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;UIScene&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;didEnterBackgroundNotification&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;object&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;nil&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;kr&amp;quot;&gt;@objc&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;func&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;didEnterBackground&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;webview&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;?.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;evaluateJavaScript&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;window.Awaken_AppHideCB &amp;amp;&amp;amp; window.Awaken_AppHideCB()&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;最后就是&lt;code&gt;setBackground&lt;/code&gt;了，这个在安卓中无用，但对于大部分都是刘海异形屏的iPhone，还是很有必要的——我们需要将WKWebView放在&lt;strong&gt;Safe Area&lt;/strong&gt;，顶部和底部保证和WebView的背景色一致，而这一点我最终的做法是利用&lt;code&gt;swiftui&lt;/code&gt;的特性：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;struct&lt;/span&gt; &lt;span class=&amp;quot;nc&amp;quot;&gt;ContentView&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;View&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;@&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;State&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;bgColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;CGColor&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;CGColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;srgbRed&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;green&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;blue&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;alpha&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;

    &lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;body&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;some&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;View&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;ZStack&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
            &lt;span class=&amp;quot;n&amp;quot;&gt;Color&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;bgColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
                &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;frame&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;minWidth&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;maxWidth&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;infinity&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;minHeight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;maxHeight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;infinity&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
                &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;background&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Color&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;red&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
                &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;edgesIgnoringSafeArea&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;all&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
            &lt;span class=&amp;quot;n&amp;quot;&gt;WebView&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
                &lt;span class=&amp;quot;n&amp;quot;&gt;onChangeBg&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;changeBgColor&lt;/span&gt;
            &lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;kd&amp;quot;&gt;func&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;changeBgColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;bp&amp;quot;&gt;CGColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;bgColor&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;color&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;在UI的根节点，我在底层放了一个全屏的&lt;code&gt;Color&lt;/code&gt;控件来铺满背景，然后在上面放上自定义的&lt;code&gt;WebView&lt;/code&gt;控件，并利用&lt;code&gt;onChangeBg&lt;/code&gt;这个回调来修改控件的&lt;code&gt;bgColor&lt;/code&gt;属性，最终影响到&lt;code&gt;Color&lt;/code&gt;的颜色。而进一步，我们再将其传入一开始定义给&lt;code&gt;AwakenJSB&lt;/code&gt;传入的那个&lt;code&gt;onChangeBg&lt;/code&gt;回调，最终在JSB类中实现功能&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;func&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;setBackground&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;Double&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;g&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;Double&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;Double&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;mOnChangeBg&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;bp&amp;quot;&gt;CGColor&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;srgbRed&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;r&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;green&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;g&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;blue&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;alpha&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;webdav跨域&lt;/h3&gt;
&lt;p&gt;至此，三端的后端接口都实现完毕，理论上为前端实现扫平了障碍，但在实际的开发中却还是遇到了因Web方案带来的麻烦，其中最大的一个就是&lt;strong&gt;跨域&lt;/strong&gt;问题。&lt;/p&gt;
&lt;p&gt;众所周知浏览器对于跨域资源是有&lt;code&gt;CORS&lt;/code&gt;来限制的，这本质上是为了内容安全，无可厚非。但对于&lt;code&gt;webdav&lt;/code&gt;这种协议，尤其是用户购买的私有服务，跨域本就不应该是障碍，而实际上很多厂商包括坚果云确实也没做这个限制。一开始我在用本地&lt;code&gt;webdav-server&lt;/code&gt;和&lt;code&gt;webdav-client&lt;/code&gt;这两个开源库调试的时候，也遇到了跨域问题，但在大量搜索后认为是&lt;code&gt;server&lt;/code&gt;的实现不标准，对其进行了魔改后凑合开发，但最终实际环境测试时仍然绕不过这个问题。为此我还专门联系了坚果云的开发人员，而他们的答复也很简单：“我们实现的是标准的WebDAV服务端协议”。&lt;/p&gt;
&lt;p&gt;好了扯了这么久，困扰我的这个问题究竟是什么呢？其实很简单：&lt;strong&gt;对于非简单的跨域请求，浏览器在发送真正的请求前会先发送一个名为&lt;code&gt;preflight&lt;/code&gt;的&lt;code&gt;OPTIONS&lt;/code&gt;请求，来保护不知晓&lt;code&gt;CORS&lt;/code&gt;的老服务器，只有这个请求成功后才允许发出真正的请求&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;听起来很合理对吧？对于大多请求时没毛病，但问题在于&lt;code&gt;webdav&lt;/code&gt;服务是有账号密码验证的，而&lt;code&gt;preflight&lt;/code&gt;请求是不会携带验证信息的，而大多“按照标准实现”的WebDAV服务器在校验前并不会区分你是不是&lt;code&gt;preflight&lt;/code&gt;请求...这TM就死锁了。&lt;/p&gt;
&lt;p&gt;那怎么办呢？没啥办法，既然浏览器的限制绕不过去，就只能借由客户端了，毕竟客户端并没有&lt;code&gt;CORS&lt;/code&gt;。如此一来，就不得不给我们上面提供的&lt;code&gt;XHR&lt;/code&gt;拦截多加个方法了。不过这块处理起来，比上面那些接口要更复杂一些。&lt;/p&gt;
&lt;h4&gt;前端拦截&lt;/h4&gt;
&lt;p&gt;首先因为是用的开源库（我这么懒显然不想去拉一份下来自己改），所以只能看有没有&lt;code&gt;hook&lt;/code&gt;的方案，而这时候选择Hybrid方案的优点就体现出来了——轮子多。我使用了&lt;code&gt;ajax-hook&lt;/code&gt;库来在前端拦截&lt;code&gt;XHR&lt;/code&gt;，将所有&lt;code&gt;webdav&lt;/code&gt;请求都加上了一个&lt;code&gt;prefix&lt;/code&gt;来协助拦截：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;DAV_PREFIX&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;http://AwakenWebDav:&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;nx&amp;quot;&gt;proxy&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;onRequest&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;handler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;startsWith&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;DAV_PREFIX&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;handler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;next&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;url&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;replace&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;DAV_PREFIX&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;...&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;接下来的代理操作&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;code&gt;hook&lt;/code&gt;住&lt;code&gt;XHR&lt;/code&gt;后，接下来就是在不同平台实现请求代理了。&lt;/p&gt;
&lt;h4&gt;桌面后端&lt;/h4&gt;
&lt;p&gt;首先是桌面端，在上面接口的实现中，桌面对文件系统并不依赖与XHR拦截，所以这里要额外想怎么实现。好在&lt;code&gt;Tauri&lt;/code&gt;官方已经给我们准备好了由&lt;code&gt;rust&lt;/code&gt;实现并绑定好的&lt;code&gt;http&lt;/code&gt;模块。&lt;/p&gt;
&lt;p&gt;首先在&lt;code&gt;tauri.conf.json&lt;/code&gt;的&lt;code&gt;tauri.allowlist&lt;/code&gt;中配置：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;http&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;all&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;request&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;scope&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;
    &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;https://**&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;http://**&amp;quot;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;随后在代码中简单实现即可：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 引入&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;import&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;http&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;from&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;@tauri-apps/api&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;// 实现代理&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;http&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fetch&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;method&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;config.method&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;any&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;body&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;config.body&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;typeof&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;body&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;string&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;http&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Body&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;text&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;body&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;http&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Body&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;bytes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;body&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;undefined&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;headers&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;config.headers&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;responseType&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;sr&amp;quot;&gt;/(png|epub)$/&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;http.ResponseType.Binary&lt;/span&gt; : &lt;span class=&amp;quot;kt&amp;quot;&gt;http.ResponseType.Text&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;then&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;handler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;resolve&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;config&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;status&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;res.status&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;headers&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{},&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;response&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;res.data&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}).&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;catch&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;error&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;handler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;reject&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;error&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h4&gt;安卓后端&lt;/h4&gt;
&lt;p&gt;移动两端做法基本一致，为前面的&lt;code&gt;XHR&lt;/code&gt;拦截协议新增方法&lt;code&gt;webdav&lt;/code&gt;，然后将真正请求的&lt;code&gt;url&lt;/code&gt;作为请求的参数传入客户端即可：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;body&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;platform&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;ANDROID&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;isBase64&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;typeof&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;body&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!==&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;string&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;data&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;isBase64&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;atob&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;body&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;ArrayBuffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;body&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;jsb&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setWebdavRequestBody&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;method&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;data&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;isBase64&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;nx&amp;quot;&gt;fetch&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;API_PREFIX&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;webdav&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;encodeURIComponent&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)}&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;method&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;config.method&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;body&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;config.body&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;headers&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;config.headers&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;then&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;errorMessage&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;headers&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;get&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;X-Error-Message&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;errorMessage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;throw&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;Error&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;errorMessage&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;webdav&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;})&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;sr&amp;quot;&gt;/(png|epub)$/&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;test&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;arrayBuffer&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;text&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;then&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;data&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;handler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;resolve&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;config&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;config&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;status&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;res.status&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;statusText&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;res.statusText&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;headers&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;res.headers&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;response&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;data&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}).&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;catch&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;error&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;console&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;error&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;error&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;handler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;reject&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;error&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;但在最后的处理中双端还是有一些差异：&lt;/p&gt;
&lt;p&gt;首先安卓端由于前面说过的原因，需要将二进制&lt;code&gt;body&lt;/code&gt;转换为&lt;code&gt;base64&lt;/code&gt;，所以在客户端需要实现一个JSB接口&lt;code&gt;setWebdavRequestBody&lt;/code&gt;，在请求前将转好的&lt;code&gt;base64&lt;/code&gt;发给客户端，这个在上面的代码也有所体现。接下来在客户端只需要将请求的&lt;code&gt;url&lt;/code&gt;作为&lt;code&gt;key&lt;/code&gt;，把&lt;code&gt;base64&lt;/code&gt;存到一张&lt;code&gt;Map&lt;/code&gt;中，后续接收到请求取出处理即可，无序赘述。&lt;/p&gt;
&lt;p&gt;其次就是请求代理的本质是将从Web拦截下的请求由客户端发出，再将结果返回Web。而客户端发出请求时，应当&lt;strong&gt;带上原先请求的&lt;code&gt;headers&lt;/code&gt;和&lt;code&gt;body&lt;/code&gt;&lt;/strong&gt;，很遗憾我并未在安卓的官方API找到能满足需求的接口，所以最终我使用了&lt;code&gt;okhttp3&lt;/code&gt;这个库：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fun&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;webdav&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;url&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;method&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;headers&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Map&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;okhttp3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Response&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;val&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;request&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;okhttp3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Request&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Builder&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;().&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;val&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;cache&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;mWebdavRequestCache&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;method&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cache&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;null&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;request&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;method&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;method&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;null&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cache&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;second&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
            &lt;span class=&amp;quot;nx&amp;quot;&gt;request&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;method&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;method&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Base64&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;decode&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cache&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;first&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Base64&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;DEFAULT&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;toRequestBody&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;())&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
            &lt;span class=&amp;quot;nx&amp;quot;&gt;request&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;method&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;method&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;cache&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;first&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;toRequestBody&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;())&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

        &lt;span class=&amp;quot;nx&amp;quot;&gt;mWebdavRequestCache&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;remove&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;method&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;nx&amp;quot;&gt;headers&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;forEach&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;request&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;addHeader&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;it&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;key&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;it&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;value&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;client&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;newCall&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;request&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;build&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;execute&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;吐槽一下&lt;code&gt;kotlin&lt;/code&gt;、&lt;code&gt;gradle&lt;/code&gt;这些工具和依赖他们的库之间的各种依赖冲突真是蛋疼。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;iOS后端&lt;/h4&gt;
&lt;p&gt;iOS这边比起安卓要更简单一些，其原生的&lt;code&gt;URLRequest&lt;/code&gt;就可以满足需求了，在上面的&lt;code&gt;XHR&lt;/code&gt;拦截入口前加上一个分支即可：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;method&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;==&lt;/span&gt; &lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;webdav&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;url&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;URL&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;params&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;url&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;
    &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;session&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;URLSession&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;shared&lt;/span&gt;
    &lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;req&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;URLRequest&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;req&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;httpMethod&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;request&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;httpMethod&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;req&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;httpBody&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;body&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;req&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;allHTTPHeaderFields&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;request&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;allHTTPHeaderFields&lt;/span&gt;
    &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;task&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;session&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dataTask&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;with&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;req&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;completionHandler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{[&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;weak&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;self&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;data&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;response&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;error&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;in&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;guard&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;strongSelf&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;self&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;error&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;nil&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
            &lt;span class=&amp;quot;n&amp;quot;&gt;strongSelf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;postFailed&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;to&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;urlSchemeTask&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;error&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;error&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;!)&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;data&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;nil&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
            &lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;res&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;response&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;as&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;!&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;HTTPURLResponse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
            &lt;span class=&amp;quot;kd&amp;quot;&gt;var&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;headers&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;
                &lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;Access-Control-Allow-Origin&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;*&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
                &lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;Access-Control-Allow-Methods&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;*&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
                &lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;Access-Control-Expose-Headers&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;X-Error-Message, Content-Type, WWW-Authenticate&amp;quot;&lt;/span&gt;
            &lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;
            &lt;span class=&amp;quot;n&amp;quot;&gt;strongSelf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;postResponse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;to&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;urlSchemeTask&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;response&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;HTTPURLResponse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
                &lt;span class=&amp;quot;n&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;requestUrl&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;statusCode&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;statusCode&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
                &lt;span class=&amp;quot;n&amp;quot;&gt;httpVersion&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;nil&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;headerFields&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;headers&lt;/span&gt;
            &lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
            &lt;span class=&amp;quot;n&amp;quot;&gt;strongSelf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;postResponse&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;to&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;urlSchemeTask&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;data&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;data&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;!)&lt;/span&gt;
            &lt;span class=&amp;quot;n&amp;quot;&gt;strongSelf&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;postFinished&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;to&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;urlSchemeTask&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;})&lt;/span&gt;

    &lt;span class=&amp;quot;n&amp;quot;&gt;task&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;resume&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;前端实现&lt;/h2&gt;
&lt;p&gt;前端逻辑主要是书籍和笔记的管理与同步，堆业务逻辑嘛懂得都懂，无非都是说难也不难，说简单却也坑多有些麻烦。所以这里我不会说太多细节，只会捡重点说一些心得。&lt;/p&gt;
&lt;p&gt;为了加快开发效率（其实是懒），这里我选择了以前用惯了的&lt;code&gt;React&lt;/code&gt;作为前端框架，样式用&lt;code&gt;SCSS&lt;/code&gt;配合&lt;code&gt;css-modules&lt;/code&gt;，并配合17年在B站用爱发电期间和同事一起开源的&lt;a href=&amp;quot;https://hana-ui.moe/&amp;quot;&gt;hana-ui&lt;/a&gt;，效果还行吧。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;已经两年多没写正儿八经的前端代码了，有些手生，第一次用&lt;code&gt;hooks&lt;/code&gt;玩不太转，而且也懒得用什么状态管理库了硬莽，不得不服老啊...嘛...又不是不能用。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;书籍管理&lt;/h3&gt;
&lt;p&gt;首先是书籍管理，也就是维护书籍列表，这也是软件刚进去的首页。&lt;/p&gt;
&lt;h4&gt;协议&lt;/h4&gt;
&lt;p&gt;要维护书籍，首先就需要定义好书籍的数据结构：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;TBookType&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;EPUB&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;interface&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IBook&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;hash&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;TBookType&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;author&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;ts&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;removed?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;boolean&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;cover?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这里面最重要的是&lt;code&gt;hash&lt;/code&gt;，它是电子书本身的&lt;code&gt;md5&lt;/code&gt;，保证了书籍的唯一性，之所以特意算一遍&lt;code&gt;hash&lt;/code&gt;是因为书籍本身可能重名而且也不一定都有&lt;code&gt;ids&lt;/code&gt;元数据。在实际的存储中，我会给每本书籍创建一个以&lt;code&gt;hash&lt;/code&gt;命名的目录，将具体内容存于其中。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;type&lt;/code&gt;是书籍类型，之所以有这个的理由你应该猜到了...不错，一开始我想支持&lt;code&gt;EPUB&lt;/code&gt;/&lt;code&gt;PDF&lt;/code&gt;/&lt;code&gt;MOBI&lt;/code&gt;等多种书籍格式，但最后发现太麻烦不值得花这么多精力就只剩&lt;code&gt;EPUB&lt;/code&gt;了（没事毕竟我们还有针对PDF的&lt;code&gt;OCR&lt;/code&gt;和转换神器&lt;code&gt;calibre&lt;/code&gt;嘛...又不是不能用。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;name&lt;/code&gt;和&lt;code&gt;author&lt;/code&gt;是从电子书提取的书名和作者，没啥好说的。&lt;code&gt;ts&lt;/code&gt;记录的是书籍被修改的（添加/删除）的时间戳，配合&lt;code&gt;removed&lt;/code&gt;在同步时使用，毕竟咱没有中心服务器处理逻辑，为了避免同步错乱没啥办法。&lt;/p&gt;
&lt;p&gt;最后一个&lt;code&gt;cover&lt;/code&gt;是从书籍中提取的封面，方便首页展示。我在添加书籍时，会将封面的二进制数据提取出来存到书籍目录下，之后在每次软件启动拿到书籍信息后，会根据当前平台自动生成一个&lt;code&gt;cover&lt;/code&gt;地址。&lt;/p&gt;
&lt;p&gt;在桌面端，我们需要在&lt;code&gt;tauri.conf.json&lt;/code&gt;中的&lt;code&gt;tauri.protocol&lt;/code&gt;字段中进行配置，允许软件访问用户本地文件：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;protocol&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;asset&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nt&amp;quot;&gt;&amp;quot;assetScope&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;
    &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;**&amp;quot;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;之后利用接口转换地址即可：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;import&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;tauri&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;from&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;@tauri-apps/api&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;nx&amp;quot;&gt;getCoverUrl&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBook&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;tauri&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;convertFileSrc&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;BOOKS_FOLDER&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;hash&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cover&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;png&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;而在移动端就简单了，复用之前实现过的文件接口协议即可：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getCoverUrl&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBook&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;API_PREFIX&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;readBinaryFile&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;filePath&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;hash&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cover&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;png&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;base&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Books&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h4&gt;同步&lt;/h4&gt;
&lt;p&gt;书籍列表的同步从原理上其实挺简单的，但要处理的边界情况稍微会有点麻烦，其本质上可以规约为：&lt;/p&gt;
&lt;p&gt;拉取远端列表 -&amp;gt; 和本地列表对比 -&amp;gt; 拉取远端新增书籍 -&amp;gt; 上传本地新增书籍 -&amp;gt; 同步列表目录&lt;/p&gt;
&lt;p&gt;具体的逻辑就不写了，有兴趣的可以自己去看代码。这里值得特别说明的有几点：&lt;/p&gt;
&lt;p&gt;首先是书籍冲突，其原因很简单，因为本方案没有一个中心的逻辑服务器，逻辑是分布在各个设备单独处理的，同时添加本地书籍并不需要联网，这就可能导致各端分别添加了同样的书籍，然后一并同步到远端的状况，这又会带来两个可能的问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;本地尚未上传的书籍已然存在于远端。这个问题从设计层面被解决了：因为书籍的唯一性由&lt;code&gt;hash&lt;/code&gt;决定，一般来讲不会碰撞，所以这里不涉及到状态。&lt;/li&gt;
&lt;li&gt;两个设备同时将本地书籍同步到远端。这个问题是切实存在的，理论上来讲可以通过&lt;code&gt;webdav&lt;/code&gt;协议的&lt;code&gt;lock&lt;/code&gt;方法解决，但考虑到实际会这么操作的可能性很低（喜欢在日常中COS测试工程师的用户不在其中，我也没义务考虑），所以就这么办吧，不处理。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;其次是书籍的删除，同样是由于没有中心服务器的缘由，书籍的删除变得非常麻烦。我不能直接把条目移除覆盖到远端，这样会导致有两个设备都存在统一条目的情况下永远删不掉。所以我退而求其次，即便是删除了也保留条目，然后用书籍协议中的&lt;code&gt;removed&lt;/code&gt;和&lt;code&gt;ts&lt;/code&gt;字段，选择时间戳比较近的，然后判定是否被删除：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;syncToLocalBooks&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBook&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[];&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;remoteBooks&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;forEach&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;localBook&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;localTable&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;hash&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;localBook&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ts&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;localBook&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ts&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;||&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;localBook&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;removed&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;syncToLocalBooks&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;

&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;syncToRemoteBooks&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBook&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[];&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;books&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;forEach&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;remoteBook&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;remoteTable&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;hash&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;remoteBook&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ts&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;remoteBook&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ts&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;||&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;remoteBook&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;removed&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;syncToRemoteBooks&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;最后是错误处理，由于我的设计中支持批量添加书籍，那么在中途任一环节被异常中断都是可能的。这个为了避免状况复杂化，我的选择是：直接退出，下次同步时直接重新处理，对远端直接覆盖上传。严格来说这样确实不是最优解，但本身就是低频操作，代价也还行吧。&lt;/p&gt;
&lt;h3&gt;EPUB解析阅读&lt;/h3&gt;
&lt;p&gt;在书籍列表点击任一封面后，进入的就是阅读界面了。这里我使用了&lt;code&gt;epub.js&lt;/code&gt;这个开源库，虽然文档一般坑不少，但却是能解决绝大多数的问题，至少不用从头去再造轮子了。&lt;/p&gt;
&lt;p&gt;这一部分吧...虽然算起来是前端最复杂的一部分，也搞了挺久，但其实也没有太多好说的，大部分看&lt;code&gt;epub.js&lt;/code&gt;的文档就OK，值得重点提的几个地方：&lt;/p&gt;
&lt;p&gt;首先初始化，对书籍的初始化大致逻辑如下：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;ePub&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;props&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;content&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;rendition&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;renderTo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;epub-viewer&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;100%&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;100%&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;stylesheet&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;props.bookStyle&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;allowScriptedContent&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;allowPopups&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;true&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这其中要注意的是&lt;code&gt;allowScriptedContent&lt;/code&gt;和&lt;code&gt;allowPopups&lt;/code&gt;的设置，由于&lt;code&gt;epub.js&lt;/code&gt;使用&lt;code&gt;iframe&lt;/code&gt;的&lt;code&gt;sandbox&lt;/code&gt;模式渲染，所以要启用所有功能必须开启。而&lt;code&gt;stylesheet&lt;/code&gt;会在下面的&lt;strong&gt;主题切换&lt;/strong&gt;一节说到。&lt;/p&gt;
&lt;p&gt;其次是分页，众所周知&lt;strong&gt;页码&lt;/strong&gt;是用来分割传统书籍的内容的，而&lt;strong&gt;EPUB&lt;/strong&gt;这种电子媒介中并没有这个东西。对于Kindle而言，页码其实是一种额外信息，由亚马逊特别处理还原传统书籍照顾读者习惯或者方便引用。而我显然是拿不到这种信息的，所以只能按照业界一般的估计，以&lt;strong&gt;600字&lt;/strong&gt;每页来分割书籍，这自然是一种不严谨的做法，但也凑合吧：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;pages&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;await&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;locations&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;generate&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;600&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这样看起来似乎OK了，但如果只是这样，你会发现每次进入阅读都会很慢，因为生成页码是个非常耗时的操作。好在&lt;code&gt;epub.js&lt;/code&gt;提供了一个口子，我们只需要生成一次&lt;code&gt;pages&lt;/code&gt;，然后将其存下来，之后每次进入时读取即可：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 存储分页数据&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;async&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;savePages&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBook&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;pages&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[])&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;await&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;bk&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;worker&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;writeFile&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;hash&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;pages&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;json&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;JSON&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;stringify&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;pages&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;Books&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;// 加载已存在的分页数据&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;locations&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;load&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;pages&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;有了分页，自然就要考虑页面跳转，但这个其实没有这么简单。如果用户已经体验过阅读模式，可以知道页面的跳转有以下几种：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;上一页/下一页：通过键盘左右，或者点击左右空白处。&lt;/li&gt;
&lt;li&gt;进度条：拖动或者点击进度条，快速跳转。&lt;/li&gt;
&lt;li&gt;目录跳转：在目录界面，点击章节标题跳转。&lt;/li&gt;
&lt;li&gt;笔记/书签跳转：和目录类似，不过是点击笔记或书签列表。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;而这些跳转，都是通过一个函数实现的：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;jump&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;action&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;EJumpAction&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;cfiOrPageOrIndex?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;|&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IBookIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;action&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!==&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;EJumpAction&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Page&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;rendition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;on&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;relocated&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;updateProgress&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;action&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;EJumpAction&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Pre&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;rendition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;prev&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;action&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;EJumpAction&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Next&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;rendition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;next&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;action&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;EJumpAction&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;CFI&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;c1&amp;quot;&gt;// first, jump to chapter&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;rendition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;display&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfiOrPageOrIndex&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;then&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;c1&amp;quot;&gt;// then, jump to note&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;rendition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;display&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfiOrPageOrIndex&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;action&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;EJumpAction&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Index&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;rendition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;display&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;idToHref&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfiOrPageOrIndex&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IBookIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;id&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]);&lt;/span&gt;  
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;action&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;EJumpAction&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Page&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;rendition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;display&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rendition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;book&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;locations&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfiFromLocation&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfiOrPageOrIndex&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;函数开头的&lt;code&gt;relocated&lt;/code&gt;方法监听，是为了在进度跳转后，向上一级同步页数。下面就是根据不同状况做的区分处理了：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Pre&lt;/code&gt;和&lt;code&gt;Next&lt;/code&gt;：针对普通切页操作，直接使用&lt;code&gt;prev()&lt;/code&gt;和&lt;code&gt;next()&lt;/code&gt;方法切换页面，这里不能用生成的页码是因为&lt;strong&gt;针对不同设备和画布，一屏显示的内容并不对应一页&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Index&lt;/code&gt;：针对目录索引，&lt;code&gt;idToHref&lt;/code&gt;可以通过&lt;code&gt;book.loaded.navigation&lt;/code&gt;处理得到。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Page&lt;/code&gt;：针对通过进度条修改的页码，可见是转换到&lt;code&gt;CFI&lt;/code&gt;处理。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CFI&lt;/code&gt;：这个比较特别，将在下一节详细论述。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;笔记、书签和进度&lt;/h3&gt;
&lt;p&gt;这一部分的逻辑算是阅读部分最为复杂的，由于需要同步，所以和上面的书籍列表同步有相似的问题，但由于需要注意顺序，状况更多更麻烦一些。&lt;/p&gt;
&lt;h4&gt;协议&lt;/h4&gt;
&lt;p&gt;首先还是要定协议，看下面两个接口：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;interface&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IBookNote&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;cfi&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;start&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;end&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;page&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;text?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;annotation?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;c1&amp;quot;&gt;// timestamp&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;modified&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;removed?&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;interface&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IBookConfig&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;ts&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;lastProgress&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;progress&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;bookmarks&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBookNote&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[];&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;notes&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBookNote&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[];&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;removedTs&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;?:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfi&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;其中&lt;code&gt;IBookConfig&lt;/code&gt;是每本书的配置文件，存于目录的&lt;code&gt;config.json&lt;/code&gt;中：&lt;code&gt;ts&lt;/code&gt;是更新时间戳，&lt;code&gt;progress&lt;/code&gt;是本地进度，&lt;code&gt;lastProgress&lt;/code&gt;是远端最新进度，这三个配合起来可以做进度同步。&lt;code&gt;bookmarks&lt;/code&gt;和&lt;code&gt;notes&lt;/code&gt;分别是书签和笔记列表，&lt;code&gt;removedTs&lt;/code&gt;则是为了解决笔记的删除问题，无奈下特意的一个优化用对象。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;IBookNote&lt;/code&gt;则是笔记或书签的数据结构：&lt;code&gt;cfi&lt;/code&gt;/&lt;code&gt;start&lt;/code&gt;/&lt;code&gt;end&lt;/code&gt;这三个都是&lt;code&gt;CFI&lt;/code&gt;下面会解释，&lt;code&gt;page&lt;/code&gt;是根据&lt;code&gt;cfi&lt;/code&gt;算出的页码，&lt;code&gt;text&lt;/code&gt;和&lt;code&gt;annotation&lt;/code&gt;是笔记专有的标注的文本和用户输入的注解，而&lt;code&gt;modified&lt;/code&gt;/&lt;code&gt;removed&lt;/code&gt;是时间戳，用于记录当前设备删除/修改一条笔记的时间，用于同步。&lt;/p&gt;
&lt;h4&gt;CFI&lt;/h4&gt;
&lt;p&gt;上面多次提到了&lt;code&gt;CFI&lt;/code&gt;，那么这到底是是个什么东西呢？让我们看看官方解释：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This specification, EPUB Canonical Fragment Identifier (epubcfi), defines a standardized method for referencing arbitrary content within an EPUB® Publication through the use of fragment identifiers.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;简单来说，&lt;code&gt;CFI&lt;/code&gt;或者说&lt;code&gt;epubcfi&lt;/code&gt;，就是用于表达对&lt;code&gt;EPUB&lt;/code&gt;电子书中某一段内容的引用。我们可以可以利用它完成对电子书中任一片段的定位索引，其形如&lt;code&gt;epubcfi(/6/12!/4[3Q280-46130e5d9d644673954c13edca4fc20f]/4,/1:325,/1:340)&lt;/code&gt;。具体的定义我不再赘述，有兴趣可以直接看&lt;a href=&amp;quot;https://idpf.org/epub/linking/cfi/&amp;quot;&gt;Specification&lt;/a&gt;，这里只讲我如何利用其完成的笔记功能。&lt;/p&gt;
&lt;p&gt;首先是书签，在上面我们提到过&lt;code&gt;relocated&lt;/code&gt;事件会更新进度，这个事件会返回一个&lt;code&gt;location&lt;/code&gt;，通过其我们可以得到需要的信息：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;props&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;onBookmarkInfo&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;({&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;start&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;location.start.cfi&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;end&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;location.end.cfi&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;cfi&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;mergeCFI&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;location&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;start&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfi&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;location&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;end&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfi&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;page&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;loc&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;modified&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Date.now&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;code&gt;location.start&lt;/code&gt;和&lt;code&gt;location.end&lt;/code&gt;分别是当前显示内容的起始和结束&lt;code&gt;CFI&lt;/code&gt;，我自己写了个方法&lt;code&gt;mergeCFI&lt;/code&gt;将它们合并起来备用。而在&lt;code&gt;onBookmarkInfo&lt;/code&gt;的处理中，我并没有直接将其作为待处理的信息直接交由书签标记逻辑，而是先用另一个方法进行了处理：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;parser&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;EpubCFI&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;function&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;checkNoteMark&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;notes&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBookNote&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[],&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;start&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;end&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;INoteMarkStatus&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;notes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;length&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;exist&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;false&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;notes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;length&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;start&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;s&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;end&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;e&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;removed&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;notes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;cse&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;parser&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;compare&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;start&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;e&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;ces&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;parser&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;compare&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;end&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;s&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;ces&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;exist&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;false&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cse&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;exist&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;removed&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;false&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;exist&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;false&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;index&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;notes.length&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这个方法传入一个书签或者笔记，返回其是否存在，以及在当前列表中的位置。为什么要做这个处理呢？因为前面说过——书签和笔记都是&lt;strong&gt;有序&lt;/strong&gt;的，所以我们不能随便插入了事，要先知道插到哪个位置。而有了位置信息，接下来的逻辑也就水到渠成了。&lt;/p&gt;
&lt;p&gt;比起书签，笔记的处理要更麻烦一些，因为其不是一个定点而是片段，同时还需要让用户自己去选中这个片段。UI层面的交互我就不多说了，无非就是堆点逻辑，其中比较重点的是&lt;strong&gt;如何获取选中的文字片段&lt;/strong&gt;。如果你去查文档，它会告诉你在&lt;code&gt;rendition&lt;/code&gt;中有个&lt;code&gt;selected&lt;/code&gt;事件可以解决，但事实上不行，我在阅读了源码后最终只能得到一个Hack的方案——在&lt;code&gt;locationChanged&lt;/code&gt;事件后，获取到当前的&lt;code&gt;content&lt;/code&gt;，在其上注册事件：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rendition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;on&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;locationChanged&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;content&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;off&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;selected&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;selectNote&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;c&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;rendition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getContents&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;c&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;on&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;selected&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;selectNote&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;c&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!==&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;content&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;setContent&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;c&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;});&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;而这个事件处理器&lt;code&gt;selectNote&lt;/code&gt;的逻辑也很简单，就是将其传入笔记标注工具组件。在这个组件中，我对每个传入的&lt;code&gt;CFI&lt;/code&gt;判断是否为新，如果是的话使用&lt;code&gt;checkNoteMark&lt;/code&gt;计算出其在当前笔记列表中的状态和位置，然后再计算出工具栏在页面上的位置：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;range&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Range&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;content&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;range&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfi&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;range&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getBoundingClientRect&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;cw&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;document&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getElementById&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;epub-viewer&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;clientWidth&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

&lt;span class=&amp;quot;nx&amp;quot;&gt;setX&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;x&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;cw&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;setY&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;y&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;然后就可以按照工具栏上的功能写逻辑了，比如删除笔记、修改注解等等。添加/删除笔记同时也伴随着高亮的标注，这个倒是比较简单：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;c1&amp;quot;&gt;// 添加&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;rendition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;annotations&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;add&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;highlight&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;cfi&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;undefined&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;undefined&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;awaken-highlight&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;

&lt;span class=&amp;quot;c1&amp;quot;&gt;// 删除&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;props&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rendition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;annotations&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;remove&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;note&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfi&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;highlight&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;其中&lt;code&gt;awaken-highlight&lt;/code&gt;是主题的一部分，后面会说。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;功能到这差不多完备了，但还有点体验上的细节：&lt;/p&gt;
&lt;p&gt;其一，一般来讲，对于一段已经标注好的笔记，我们往往希望点击它就可以弹出笔记工具栏，而不是需要选中。此时&lt;code&gt;rendition&lt;/code&gt;的&lt;code&gt;markClicked&lt;/code&gt;事件就可以帮助我们。&lt;/p&gt;
&lt;p&gt;其二，如果你看了&lt;code&gt;epub.js&lt;/code&gt;关于文本选择的源码，可以发现其处理非常粗暴：以选择开始为起点，在&lt;code&gt;150ms&lt;/code&gt;后判定结束返回事件，而这做法显然不合理，所以我做了点优化（当然仍然不想改工程，凑合改了）：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;EVENT_NAME&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;bk&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;supportChangeFolder&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;mouseup&amp;#39;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;touchend&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;Contents&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;any&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;).&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;prototype&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;onSelectionChange&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;function&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;e&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;Event&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;t&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;this&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;any&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;doingSelection&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;handler&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;function&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;()&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;window&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;removeEventListener&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;EVENT_NAME&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;handler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;selection&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;window&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getSelection&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;();&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;triggerSelectedEvent&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;selection&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;doingSelection&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;false&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;

  &lt;span class=&amp;quot;nx&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;window&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;EVENT_NAME&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;handler&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;t&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;doingSelection&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;true&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;修改很简单，将选择结束的条件改为&lt;code&gt;mouseup&lt;/code&gt;或者&lt;code&gt;touchend&lt;/code&gt;就行了。&lt;/p&gt;
&lt;h4&gt;同步&lt;/h4&gt;
&lt;p&gt;书签和笔记的同步在内容上比书籍简单，因为只需要合并两个数组，但由于其有序并且存在同一条目的更新（修改注解），而且量可能较大还要考虑效率，所以更加麻烦。不过好在这也不是特别复杂的算法，凑合写了个大家看看就懂：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;private&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;_mergeNotes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;localNotes&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBookNote&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[],&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;remoteNotes&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBookNote&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[],&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;removedTs&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfi&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;})&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IBookNote&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[]&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBookNote&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;[];&lt;/span&gt;
  &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;localIndex&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;remoteIndex&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;pre&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBookNote&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;less&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBookNote&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;preRemoved&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IBookNote&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;while&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;localIndex&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;localNotes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;length&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;||&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;remoteIndex&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;remoteNotes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;length&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;local&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;localNotes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;localIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;
    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;remote&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;remoteNotes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;remoteIndex&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;

    &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;comp&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;local&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;remote&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;1&lt;/span&gt; : &lt;span class=&amp;quot;kt&amp;quot;&gt;parser.compare&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;local&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;start&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;remote&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;start&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;comp&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;local&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;modified&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;remote&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;modified&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;less&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;remote&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;remoteIndex&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;  
      &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;less&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;local&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;nx&amp;quot;&gt;localIndex&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;  
      &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;comp&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;less&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;remote&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;remoteIndex&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;less&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;local&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;localIndex&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;c1&amp;quot;&gt;// local&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;less&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;removed&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;removedTs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;less&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfi&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;Math&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;less&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;removed&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;removedTs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;less&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfi&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;||&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;preRemoved&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;less&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
      &lt;span class=&amp;quot;k&amp;quot;&gt;continue&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;c1&amp;quot;&gt;// remote&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;preRemoved&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfi&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;less&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfi&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;removedTs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;less&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfi&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;||&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;less&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;modified&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;continue&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
      &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;c1&amp;quot;&gt;// remote&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;pre&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfi&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;===&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;less&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfi&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;nx&amp;quot;&gt;pre&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;modified&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;Math&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;pre&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;modified&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;less&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;modified&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
      &lt;span class=&amp;quot;k&amp;quot;&gt;continue&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;((&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;removedTs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;less&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;cfi&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;||&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;less&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;modified&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
      &lt;span class=&amp;quot;k&amp;quot;&gt;continue&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;push&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;less&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;nx&amp;quot;&gt;pre&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;less&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;其中每个&lt;code&gt;note&lt;/code&gt;的&lt;code&gt;removed&lt;/code&gt;属性仅存在于本地条目，而到了远端则变成&lt;code&gt;removedTs&lt;/code&gt;中一部分。这么做首先是由于和书籍同步同样的原因，我必须要在远端存下来某个条目是否被删除了；其次不像书籍一样直接同步&lt;code&gt;removed&lt;/code&gt;字段是由于一个笔记占用开销，同时按照上面这种优化实现逻辑上会出问题。&lt;/p&gt;
&lt;p&gt;如何合并完远端和本地的笔记与书签后，本地存一份然后立即同步到远端，搞定。&lt;/p&gt;
&lt;h4&gt;Webview默认行为&lt;/h4&gt;
&lt;h3&gt;Kindle笔记迁移&lt;/h3&gt;
&lt;p&gt;有了笔记功能，别忘了我最初是为了什么搞这个软件的——从Kindle迁移。所以终于到这一步了，就是如何将Kindle的书迁移过来，笔记也迁移过来。&lt;/p&gt;
&lt;p&gt;Kindle的书籍迁移已经有很成熟的教程了，可以参考：&lt;a href=&amp;quot;https://www.iplaysoft.com/kindle-download-dedrm.html&amp;quot;&gt;一键批量下载 Kindle 全部电子书工具 + 移除 DRM 解密插件 + 格式转换教程 (开源免费)&lt;/a&gt;，最后用&lt;code&gt;calibre&lt;/code&gt;转换的时候选择&lt;code&gt;EPUB&lt;/code&gt;即可。&lt;/p&gt;
&lt;p&gt;接下来就是笔记迁移了，亚马逊提供了几种笔记导出方案，本来想都支持的，后来由于时间不够懒得搞了就只支持&lt;strong&gt;从桌面端软件（PC/Mac）导出&lt;/strong&gt;的笔记，详细教程搜一下就有，最后导出的应当是名为&lt;strong&gt;XXXXX-笔记.html&lt;/strong&gt;这样的文件。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这里吐槽一下亚马逊的同行们，你们连个&lt;code&gt;html&lt;/code&gt;文件的输出都拼不对我真是服了...是因为&lt;code&gt;html&lt;/code&gt;本身容错率太高导致看了下能渲染就没检查了是吧？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;导出的笔记文件格式分析我就略过了，经过我的解析处理后，大致可以得到如下信息：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;笔记的标注内容文本，去掉了空格填充。&lt;/li&gt;
&lt;li&gt;如果有注解，注解文本。&lt;/li&gt;
&lt;li&gt;笔记的“位置”。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;看到这，读者可能觉得最直接的方法就是将笔记的“位置”转换为&lt;code&gt;EPUB&lt;/code&gt;的&lt;code&gt;CFI&lt;/code&gt;就行，特别简单对吧？哪有这么好的事...在经过查询后，我大概了解了Kindle是怎么计算“位置”的：每&lt;strong&gt;128个字节&lt;/strong&gt;算一个“位置”，这也就解释了为什么你复制中文文本甚至导出的笔记里都会插入这么多空格（我猜的），所以这路子行不通，那可咋整呢？&lt;/p&gt;
&lt;p&gt;想来想去，最后还是只能用原始暴力的方法——文本搜索匹配。我直接把笔记拿出来全文搜索出那个片段总行了吧？正好&lt;code&gt;epub.js&lt;/code&gt;也提供了对章节的&lt;code&gt;find&lt;/code&gt;接口。但在满怀期待试了以后，发现...问题更复杂了：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;脚注：很多书中都会有“xxx[1]yyy”这样的脚注，而由于&lt;code&gt;EPUB&lt;/code&gt;本身是&lt;code&gt;xml&lt;/code&gt;结构的，脚注会被单独渲染为一个结点，但搜索是基于结点的，直接去搜索根本搜不到。&lt;/li&gt;
&lt;li&gt;段落：和脚注差不多的原因，搜索基于&lt;code&gt;xml&lt;/code&gt;结点，跨段落直接跨结点了，搜不了。&lt;/li&gt;
&lt;li&gt;重复：同一段文字会多次出现，对于笔记的“金句”而言不多见，但还是可能在译者评论中出现。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;为了解决这些问题呢，我搞了个挺恶心的算法，具体太长就不贴了有兴趣自己去看吧，大概说下思路：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;用&lt;code&gt;query&lt;/code&gt;的前N个字获取&lt;code&gt;rangeStart&lt;/code&gt;，后N个字获取&lt;code&gt;rangeEnd&lt;/code&gt;，最后合并。&lt;/li&gt;
&lt;li&gt;N看实际情况来取，而&lt;code&gt;textEnd&lt;/code&gt;需要在&lt;code&gt;textStart&lt;/code&gt;（无脚注link）/&lt;code&gt;linkEnd&lt;/code&gt;（有脚注link）的若干结点之内。 &lt;/li&gt;
&lt;li&gt;先判断是否有[\d+]的link，没有的话：&lt;ol&gt;
&lt;li&gt;小于六个字的，直接全文搜索。&lt;/li&gt;
&lt;li&gt;大于六个字的，拆成前六后六两部分，分别搜索后合并。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;有的话&lt;ol&gt;
&lt;li&gt;先查找到第一个link，然后反向搜索link前的文本的前（小于等于六个字），没有文本则直接以link为起点。&lt;/li&gt;
&lt;li&gt;然后找到最后一个link，正向搜索link后的文本的最后（小于等于六个字），没有文本则以link为终点。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;但即便如此，仍然无法覆盖所有情况，不过大部分情况已经可以覆盖了（只要你处理的是&lt;strong&gt;中文书籍&lt;/strong&gt;），对于边界情况，我会收集起来在最后弹窗提示用户，并复制到用户的剪贴板。&lt;/p&gt;
&lt;h3&gt;主题切换&lt;/h3&gt;
&lt;p&gt;至此，主要的功能逻辑都搞定了，但由于个人的习惯，搞了这么久不加点私货是不可能的，所以主题切换功能就此诞生，其属于阅读设定的一部分：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;interface&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;ITheme&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;name&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;color&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;background&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;highlight&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;interface&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;IReadSettings&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;extends&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;ITheme&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;theme&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;fontSize&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;letterSpace&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;lineSpace&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;number&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;由数据结构便可以看到阅读设定允许的内容：&lt;code&gt;fontSize&lt;/code&gt;是文本大小，&lt;code&gt;letterSpace&lt;/code&gt;是字间距，&lt;code&gt;lineSpace&lt;/code&gt;是行间距，这几个单位都是&lt;code&gt;rem&lt;/code&gt;。而&lt;code&gt;theme&lt;/code&gt;就是最重要的主题了，&lt;code&gt;name&lt;/code&gt;是主题名，&lt;code&gt;color&lt;/code&gt;是文本颜色，&lt;code&gt;background&lt;/code&gt;是背景色，&lt;code&gt;highlight&lt;/code&gt;是标注高亮、注解、外部链接颜色。&lt;/p&gt;
&lt;p&gt;如何修改这些字段的UI逻辑没必要赘述，这里比较重要的是如何让这些参数产生效果。还记得前面说过的后端的&lt;code&gt;setBackground&lt;/code&gt;接口，以及初始化电子书时的&lt;code&gt;stylesheet&lt;/code&gt;字段吗？&lt;code&gt;setBackground&lt;/code&gt;配合阅读界面的背景颜色，来让异形屏客户端的非安全区统一颜色，这个没啥好说的。&lt;code&gt;stylesheet&lt;/code&gt;则可以给渲染&lt;code&gt;EPUB&lt;/code&gt;的&lt;code&gt;iframe&lt;/code&gt;添加一个&lt;code&gt;css&lt;/code&gt;文件，而这个文件是我动态生成的：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;export&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;function&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;buildStyleUrl&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;settings&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IReadSettings&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;kt&amp;quot;&gt;string&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;style&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;body&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;color&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;settings&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;font&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;size&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;settings&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fontSize&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rem&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;line&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;height&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;settings&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fontSize&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;settings&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;lineSpace&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rem&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;letter&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;spacing&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;settings&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;letterSpace&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;||&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rem&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;touch&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;action&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;none&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;webkit&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;touch&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;callout&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;none&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;word&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;break&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;break&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;all&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;nx&amp;quot;&gt;img&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;width&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;100&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;%&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

&lt;span class=&amp;quot;nx&amp;quot;&gt;a&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;color&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;settings&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;color&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;text&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;decoration&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;none&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;border&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;bottom&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;2px&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;solid&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;settings&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;highlight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;};&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
  &lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;

  &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;URL&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;createObjectURL&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;new&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;Blob&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;([&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;style&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;type&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;text/css&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}));&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;当用户每次修改完样式确认后，我还需要去修改重新设置样式：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;applyReadSettings&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;async&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rSettings&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;IReadSettings&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;background&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;highlight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;rSettings&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;await&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;bk&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;worker&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;setBackground&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;
    &lt;span class=&amp;quot;nb&amp;quot;&gt;parseInt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;background&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;substring&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;16&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;255&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;nb&amp;quot;&gt;parseInt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;background&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;substring&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;5&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;16&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;255&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; 
    &lt;span class=&amp;quot;nb&amp;quot;&gt;parseInt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;background&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;substring&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;5&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;7&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;16&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;/&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;255&lt;/span&gt;
  &lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
  &lt;span class=&amp;quot;kr&amp;quot;&gt;const&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;sheet&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;document&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;getElementById&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;global-style&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;kr&amp;quot;&gt;as&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;HTMLStyleElement&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;sheet&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;textContent&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;g&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;awaken&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;highlight&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fill&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;$&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;highlight&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;important&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fill&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;opacity&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;0.5&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;}&lt;/span&gt;&lt;span class=&amp;quot;err&amp;quot;&gt;`&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;setBookStyle&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;buildStyleUrl&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rSettings&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;
  &lt;span class=&amp;quot;nx&amp;quot;&gt;setReadSettings&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rSettings&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;其中&lt;code&gt;global-style&lt;/code&gt;的修改是为了笔记高亮的样式（&lt;code&gt;epub.js&lt;/code&gt;是用SVG实现的），而&lt;code&gt;setBookStyle&lt;/code&gt;后执行的则是：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;rendition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;themes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;register&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;awaken-style&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;props&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;bookStyle&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;nx&amp;quot;&gt;rendition&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;themes&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;select&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;awaken-style&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;卸载之前的样式重新加载即可。&lt;/p&gt;
&lt;h2&gt;构建发布&lt;/h2&gt;
&lt;p&gt;开发完成后就是最终的构建发布了，这个在三端也有不同的做法。当然无论如何，第一步都是构建出生产环境的代码，我将最终代码构建到了&lt;code&gt;dist&lt;/code&gt;目录，以备后续处理。&lt;/p&gt;
&lt;h3&gt;桌面端&lt;/h3&gt;
&lt;p&gt;桌面端的构建很简单，&lt;code&gt;Tauri&lt;/code&gt;都帮我们想好了，我在&lt;code&gt;tauri.conf.json&lt;/code&gt;中指定了资源目录&lt;code&gt;build.distDir&lt;/code&gt;为&lt;code&gt;./assets&lt;/code&gt;，并用脚本将上一步构建后的产物复制到其内：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;platforms/desktop/assets &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; rm -rf &lt;span class=&amp;quot;nv&amp;quot;&gt;$d&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; mkdir &lt;span class=&amp;quot;nv&amp;quot;&gt;$d&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; cp dist/* &lt;span class=&amp;quot;nv&amp;quot;&gt;$d&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;最后执行切换到桌面工程目录下执行构建代码即可：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;cd&lt;/span&gt; ./platforms/desktop &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; tauri build
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;最终的产物在&lt;code&gt;./platforms/desktop/target/release/bundle&lt;/code&gt;内。&lt;/p&gt;
&lt;h3&gt;安卓端&lt;/h3&gt;
&lt;p&gt;移动端相较于桌面端，需要自己区分开发和生产环境，而且对资源的处理也要更复杂一些。&lt;/p&gt;
&lt;p&gt;在安卓端，首先区分开发和发布环境是通过菜单的&lt;code&gt;Build&lt;/code&gt; -&amp;gt; &lt;code&gt;Select Build Variants&lt;/code&gt;窗口设置的，默认有&lt;code&gt;debug&lt;/code&gt;和&lt;code&gt;release&lt;/code&gt;两种模式，在不同模式切换后会有个全局单例中的变量&lt;code&gt;BuildConfig.DEBUG&lt;/code&gt;能判断处于什么环境：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;BuildConfig&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;DEBUG&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;接下来在发布时，我们现将构建好的前端代码复制到指定目录：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;platforms/android/app/src/main/assets &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; rm -rf &lt;span class=&amp;quot;nv&amp;quot;&gt;$d&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; mkdir &lt;span class=&amp;quot;nv&amp;quot;&gt;$d&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; cp dist/* &lt;span class=&amp;quot;nv&amp;quot;&gt;$d&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;至于如何返回这些包内静态资源呢？也很简单，在前面我已经在客户端实现了Webview中&lt;code&gt;XHR&lt;/code&gt;的拦截，现在只要在里面添加一些逻辑即可。&lt;/p&gt;
&lt;p&gt;首先，在发布环境下，我需要将Webview加载的&lt;code&gt;url&lt;/code&gt;从调试的&lt;code&gt;dev-server&lt;/code&gt;的地址，换到我们拦截的&lt;code&gt;schema&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;mainWebView&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;?&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;loadUrl&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;BuildConfig&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;DEBUG&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;host&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;http://awaken.api&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;})&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;然后对于所有&lt;strong&gt;在之前接口协议之外的&lt;code&gt;method&lt;/code&gt;&lt;/strong&gt;，全部认为是对包内静态资源的请求，然后进行拦截处理：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;fun&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;loadAsset&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;url&lt;/span&gt;: &lt;span class=&amp;quot;kt&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;InputStream&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;mContext&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;assets&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;open&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nx&amp;quot;&gt;url&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;index.html&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt; &lt;span class=&amp;quot;nx&amp;quot;&gt;url&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;})&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;可见在安卓上对包内资源的请求是很简单的。在完成这些后，我们还需要给应用提供一个签名，用&lt;code&gt;Build&lt;/code&gt; -&amp;gt; &lt;code&gt;Generate Signed Build or APK&lt;/code&gt;即可。&lt;/p&gt;
&lt;p&gt;最终构建产物在&lt;code&gt;platforms/android/app/release&lt;/code&gt;中。&lt;/p&gt;
&lt;h3&gt;iOS端&lt;/h3&gt;
&lt;p&gt;iOS的工程配置是完全由XCode管理的，单纯从构建区分来讲并不复杂。&lt;/p&gt;
&lt;p&gt;首先是区分开发和发布环境，这个只需要在工程配置文件的&lt;code&gt;Build Settings&lt;/code&gt; -&amp;gt; &lt;code&gt;swift compiler - Custom Flags&lt;/code&gt; -&amp;gt; &lt;code&gt;Other Swift Flags&lt;/code&gt;中，给&lt;code&gt;Debug&lt;/code&gt;配置加上&lt;code&gt;-DDEBUG&lt;/code&gt;，给&lt;code&gt;Release&lt;/code&gt;配置加上&lt;code&gt;-DRELEASE&lt;/code&gt;，然后创建两个构建的&lt;code&gt;Schema&lt;/code&gt;，在其中分别使用哪个环境，最后就可以在代码中使用这些编译选项了：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;cp&amp;quot;&gt;#if&lt;/span&gt; &lt;span class=&amp;quot;cp&amp;quot;&gt;RELEASE&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
&lt;span class=&amp;quot;cp&amp;quot;&gt;#else&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;......&lt;/span&gt;
&lt;span class=&amp;quot;cp&amp;quot;&gt;#endif&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;接下来在发布时，我们现将构建好的前端代码复制到指定目录：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;nv&amp;quot;&gt;d&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt;platforms/ios/Awaken/assets &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; rm -rf &lt;span class=&amp;quot;nv&amp;quot;&gt;$d&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; mkdir &lt;span class=&amp;quot;nv&amp;quot;&gt;$d&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; cp dist/* &lt;span class=&amp;quot;nv&amp;quot;&gt;$d&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;随后在XCode项目配置的&lt;code&gt;Build Phase&lt;/code&gt; -&amp;gt; &lt;code&gt;Copy Bundle Resource&lt;/code&gt;中，将&lt;code&gt;assets&lt;/code&gt;目录添加进去，让其能打入构建包。随后便可以在代码中的发布分支中编写具体逻辑了：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;rp&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;Bundle&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;main&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;path&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;forResource&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;index&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;ofType&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;html&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;inDirectory&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;assets&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;do&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;str&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;try&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;contentsOfFile&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;rp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;encoding&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;utf8&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;wkWebView&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;loadHTMLString&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;str&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;baseURL&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;URL&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;awaken://awaken.api&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;catch&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;wkWebView&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;load&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;URLRequest&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;URL&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;string&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;host&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;!&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;可见这里比安卓要复杂不少，我先读取了本地Bundle内的&lt;code&gt;index.html&lt;/code&gt;，然后指定了我们实现了&lt;code&gt;XHR&lt;/code&gt;拦截的地址&lt;code&gt;awaken://awaken.api&lt;/code&gt;作为&lt;code&gt;baseURL&lt;/code&gt;。接下来WKWebView就会以这个地址为基础去请求JS、CSS等静态资源了。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;之所以要提前读取&lt;code&gt;html&lt;/code&gt;的内容，是因为通过&lt;code&gt;XHR&lt;/code&gt;拦截返回&lt;code&gt;index.html&lt;/code&gt;会出现问题。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;静态资源的处理和安卓基本一致，对于不满足既定接口方法的请求直接尝试加载本地资源即可：&lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;kd&amp;quot;&gt;private&lt;/span&gt; &lt;span class=&amp;quot;kd&amp;quot;&gt;func&lt;/span&gt; &lt;span class=&amp;quot;nf&amp;quot;&gt;loadAsset&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;String&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;throws&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;FileHandle&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;fp&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;url&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;==&lt;/span&gt; &lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;?&lt;/span&gt; &lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;index.html&amp;quot;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;url&lt;/span&gt;
    &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;nameExt&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;fp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;components&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;separatedBy&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;.&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;rp&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;Bundle&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;main&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;.&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;path&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;forResource&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;nameExt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;ofType&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;nameExt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;],&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;inDirectory&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;assets&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;kd&amp;quot;&gt;let&lt;/span&gt; &lt;span class=&amp;quot;nv&amp;quot;&gt;file&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;FileHandle&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;forReadingAtPath&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;rp&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;!)&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;file&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;==&lt;/span&gt; &lt;span class=&amp;quot;kc&amp;quot;&gt;nil&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;throw&lt;/span&gt; &lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;File load error: &lt;/span&gt;&lt;span class=&amp;quot;si&amp;quot;&gt;\(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;url&lt;/span&gt;&lt;span class=&amp;quot;si&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;quot;&lt;/span&gt;
    &lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;

    &lt;span class=&amp;quot;k&amp;quot;&gt;return&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;file&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;!&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;结语&lt;/h2&gt;
&lt;p&gt;搞之前没想到还挺麻烦的，要是之前有人搞了我也不会花着精力，毕竟这甚至不属于我既定项目的一部分。不过总体来讲，做完还挺有成就感的，未来有别的项目也可以作为模板打个样。&lt;/p&gt;
&lt;p&gt;无论如何，我过去从开源社区已经索取了这么多，那么尽可能做出力所能及的回报也是必要的。无论做点啥总都比躺着伸手祈求强，毕竟——&lt;/p&gt;
&lt;p&gt;意义并不在于‘包罗万象’的观测，而存在于任一行动的回响之中。&lt;/p&gt;
&lt;p&gt;接下来就是去读书然后完成既定的主线项目了。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Tuesday, 27 Dec 2022 23:33:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2022.12.27 23:33:article/Create-2022_12_27_a</guid>
<category>阅读软件</category>
<category>Hybrid</category>
<category>WebDAV</category>
<category>Android</category>
<category>iOS</category>
<category>Tauri</category>
<category>React</category>
</item>

<item>
<title>Project Self</title>
<link>http://dtysky.moe/article/Create-project-self</link>
<description>&lt;p&gt;褐色的枝干攀附于浅黄的墙壁，粉白的花簇拥着吊在其上，油绿的树叶反而成了点缀。风不大，偶有几片花叶被吹落到地上，笔直的水泥路灰里透着青，一直向远处的湛蓝天空延伸。我站在墙下抬头望去，阳光逆着我的视线透过树叶，照在了我带着蓝色美瞳的眼睛上。&lt;/p&gt;
&lt;p&gt;“对对对，就是这个姿势，保持一下！老天赏脸，这光真不错。”身后的摄影师@夏昊（以下简称X）背着他的富士GFX100S，语气轻松：“你今天状态感觉还好吧？”&lt;/p&gt;
&lt;p&gt;X和我同龄，几年前毕业于北影，据说刚毕业时吃过一阵子肉，但前些年电影行业开始不景气就离开了这行当。好在他家境不错，这两年便自由在家学习感兴趣的东西，不时出来给有缘人拍些写真赚点钱。&lt;/p&gt;
&lt;p&gt;“还好，虽然感冒还是有点。”我顶着些许的身体不适，努力保持着这个姿势。保持，意味着静止，而完全的静止，对我而言是不可能的。虽然肉体可以尽量不动，但无法停止的思绪却开始了游离。&lt;/p&gt;
&lt;p&gt;这段时间我似乎一直在感冒，说是感冒，其实就是持续性的头晕加嗓子不舒服。症状一般始于起床，午饭后逐渐缓解，晚上到家又开始。前几天我似乎开始适应了这种不适，但今天特别需要一个良好状态，所以出门前饭后我磕了个对乙酰氨基酚片。和年少时不同，现在的我不再会硬抗肉体的不适来获取某种悲壮的情绪。数次实践下选出的最适合自己的药，让我现在的症状相比早上拍和猫的合照时缓解了许多。&lt;/p&gt;
&lt;p&gt;想到了猫，便想到了早晨，对早晨的回想接替了感冒的主题。而按照惯例，这思绪又进一步跳跃，回到了我和X首次沟通的那天。&lt;/p&gt;
&lt;h2&gt;沟通-起点&lt;/h2&gt;
&lt;p&gt;沟通，对于我而言可以很简单，也可以很困难，这取决于具体的内容。当然，每个人都有自己擅长或不擅长沟通的领域，但像我这么极端区分的，应该是不多。以前我会将这些领域内容区分为很多种类，比如技术、工作、二刺螈、文学、哲学、情感问题等等，但现在想来，我大概仅会将其分为两类：取悦他人，或是取悦自己。&lt;/p&gt;
&lt;p&gt;我擅长取悦自己，即便是往常那对自我的折磨，很多也可以说是一种变相的取悦。但取悦他人，的确是我极不擅长的，所以我一般会尽力避开那些取悦他人的场合，来让自己得到最大的安宁。倘若所有的事情都能被如此干脆得划分，倒也不失为一种有益的秩序。但现实的本质既是混乱，又怎可能事事顺遂？现在摆在眼前的，便是一个特例。&lt;/p&gt;
&lt;p&gt;写真作为一个特例，包含了取悦他人和取悦自己的双重性质。这个取悦的他人倒不是说摄影师，而是源于我拍摄的初心：一个妹子对我社交账号照片的建议。她说“从女方视角，很多男的都很卷，各种花里胡哨，你也应该搞点让大家喜欢的照片。”我觉得蛮有道理，便认真考虑起了这个建议。“考虑”这件事本来并不是什么大事，我每天本就在考虑许多无关紧要的屁事，但前面加上“认真”二字就不同了。&lt;/p&gt;
&lt;p&gt;认真，在我的词典里优先级是极高的。任何事一旦加上了这个前缀，便会直接跃升为一个大工程，我会尽自己的所有可能将其做到最好。写真这事说起来，一开始不过是为了搞几张取悦他人的照片，按理说花个几百块拍套简单的、温柔的、沉稳的艺术照即可。但既然认真了，就需要考虑将其维度延展开。这一认真，一延展，一加工，我便又给自己挖了一个坑。和我之前定下的诸多类似《Project Heart》、《Project Tomorrow》、《Project Fake》等等企划一般，我脑海里突然浮现了一个企划。&lt;/p&gt;
&lt;p&gt;这个企划的名字便是《&lt;strong&gt;Project Self&lt;/strong&gt;》，而其核心便是三个人，或者说是三个人格，亦或说是三个投影比较合适：少年，少女，青年。&lt;/p&gt;
&lt;p&gt;不错，这三个投影，正是在我过去写给自己的私小说中，那数次出现的少年H、少女H和青年H。如此一来，这写真就成了一种记录和表达，那么它和写作也就相去无几。而我的写作，无论目的如何，取悦自己的比例一直远高于取悦他人。考虑到这，写真的初心就变了个味，从取悦他人变成了取悦自己。&lt;/p&gt;
&lt;p&gt;拍写真取悦自己，意味着需要高度的定制，定制又意味着预算和增加和详尽的沟通。对于现在的我，相比于预算这种小事，沟通显得额外艰难。作为一个INFP，我是比较擅长自我剖析和暴露，也逐渐不在乎他人的不理解，但此次却不能如此。如果将其也视为一种创作，那么和往常我一人构思和执笔不同，这一次我是将笔交予了对方。它不但要求摄影师完全理解了我的诉求，还要求对方能够认同，这样才能由衷给出那种表达。&lt;/p&gt;
&lt;p&gt;我写了一篇需求来描述我的意图，将其发给了几个朋友介绍的本地摄影大佬，但都被拒接了。其中有一位给出了“这个只能让一个比较熟悉你的摄影师”来的建议。循着这建议，我找到了这个深圳的北影出身的知乎KOL摄影师朋友。将需求发给他想让他介绍个广州的圈内朋友给我后，他便饶有兴致得以“我就喜欢创意型摄影”自己接下了这个单。&lt;/p&gt;
&lt;p&gt;摄影的报价是我给出的，出于对朋友的信任和避免某些无效的沟通，我咨询了圈内朋友后，给出了圈内的一般最高价格——三四千，对方表示可以：如果结果不满意三千，满意四千，而且排期自由。我觉得如果能达到要求，也算合理，便定了下来，具体时间则是在清明。&lt;/p&gt;
&lt;p&gt;我本来认为做好了这些前置准备，便只需要为了取悦自己而沟通，如此一来效果的达成也就是时间问题。但事后想来，取悦他人还是自己不过是结果论罢了，对于这样的一种非常主观的活动，本质上是双方需求的对抗与中和。对抗和中和，也就是沟通的另一种说法，但在这个侧面而言，沟通的实质其实是“要求”。&lt;/p&gt;
&lt;p&gt;极其不擅于提出“要求”的我，让拍摄饶了一些弯路。&lt;/p&gt;
&lt;h2&gt;第一日&lt;/h2&gt;
&lt;p&gt;拍摄的第一日，正是清明的第一天。由于感冒症状以及昨晚摄影设备和电脑的调试，我大约九点醒来，却挣扎到了十点多才起床。不过打开窗户后，和煦的阳光让我心情好了不少，天公赏脸，今日并未像预报一般是阴天。换了衣服走出卧室后，惯例的低血糖让我有些头晕，便当即决定吃了饭再出门。饭后磕了药，身体状况并未瞬间好了起来，但时间已然不早，我也不想拖延更久，便和X说先带着猫在小区内拍几张日常，之后再正式开始今天的外景。&lt;/p&gt;
&lt;p&gt;在这所有的拍摄之前，X先问了我一句：“看你的需求，到底是想要美化的，还是比较真实的？”&lt;/p&gt;
&lt;p&gt;我不假思索得回到：“我不想做一个伪物，所以当然是真实。”&lt;/p&gt;
&lt;p&gt;于是&lt;strong&gt;真实&lt;/strong&gt;便成了这系列拍摄的主基调，在这个基调下，我们带着滚滚和糖糖下楼了。&lt;/p&gt;
&lt;h3&gt;猫&lt;/h3&gt;
&lt;p&gt;小区坐落在海珠和番禺两个区的交界处，位置可以说是偏僻到鸟不拉屎，但其另一面也确实有足够大的面积、很低的容积率与不错的绿化。我们站在单元门口一眼望去，是一条被错落有致的树木包裹着的林荫小道。此时万里无云，明媚的阳光正透过树叶，向被砖红色和青涩投下粒粒光斑，非常适合取景。我们向前走了几步，X观察了周围，很快就确定了个地点。我走了过去，将猫抱了出来，开始了拍摄。&lt;/p&gt;
&lt;p&gt;“就这是吧？”城市来讲，如果可以列一个我不擅长的事情列表，“拍照”应该是能排在很前列的。所以在拍摄时，我第一想的便是专业的事交给专业的人，尽量遵从对方的指导。&lt;/p&gt;
&lt;p&gt;“再前面一点，对，就这几个光斑下面。”他指着地上的几个光斑，我带着猫走了过去。&lt;/p&gt;
&lt;p&gt;“糖糖！别瞎跑。”生性好动谨慎的糖糖刚下地就想着躲起来，她体型很小又白，匍匐在地，东张西望，见缝就钻。好在虽然我身体仍然不适，但在三年多的相处下早就学会了对付她。我将比较傻不拉几的滚滚放在一边，迅速拎起了糖糖的脖子，将她放回原地。如此重复三四次后，她可能暂时得习得性无助了，便不再反抗。&lt;/p&gt;
&lt;p&gt;拍照在搞定了猫后顺利了起来，这组写真的主角是猫而不是我，所以我个人在镜头前的特质并没有表现出来。同时在和猫的互动中，我的表情相对自然并且还有些许笑意，这掩盖了一些本应出现的麻烦，让我们认为接下来的拍摄都会如此简单。&lt;/p&gt;
&lt;p&gt;第一组照片，就这样完成了。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/1.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;h3&gt;少年&lt;/h3&gt;
&lt;p&gt;“你过来看下效果吧。”&lt;/p&gt;
&lt;p&gt;“...好的。”&lt;/p&gt;
&lt;p&gt;X的声音将我从回想中拉回，我走了过去，看了看他相机中的照片。情绪有点复杂，但还是回了句：“感觉不错，挺有日系清新的那个味。”&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/2.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;情绪复杂，不是在于对方拍摄技巧不好，也并非由于本应出现的那个麻烦：它还尚未出现。问题是在于，这和我本身设想的感觉确实不太一样。在我的设想中，摄影的第一个阶段是“少年”，而我的少年时光，与大众意义、也可能是X所理解的少年感，还是有不小差距的。&lt;/p&gt;
&lt;p&gt;我对少年的标签，是&lt;strong&gt;桀骜不驯，唯我，以及脆弱&lt;/strong&gt;，其模板如下：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/3.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;而X拍摄出的，是包容、阳光和温暖。&lt;/p&gt;
&lt;p&gt;此刻我本应提出一些要求，但虽然时常在文章或是其他场合不掩饰自我的特质，但在这种一对一的、我所不擅于应对的场合中，我将主导权完全交由了对方。“要求”此刻对我而言成为了非常困难的事情，便出于尽可能减少“沟通”的想法，选择了沉默。&lt;/p&gt;
&lt;p&gt;我们就这样相对沉默，顺着笔直的道路向前走着，寻觅着下一个拍摄的场所。走了不一会，我们便停在了一片露营地旁。他指了指不远的一处草丛，说：“这不错，你去坐到这个地方。”&lt;/p&gt;
&lt;p&gt;我便走了过去，很配合得坐了下来，之后又按照X的提示摆好了姿势。这里和方才不太一样，人多了不少，大多还都是妹子。虽然表面上力保那种体面和从容，我的心中还是有些不是滋味。即便是在社会上找到了许多自信的我，还是很不适应那种非常社会化的审视目光，这并非出于怯懦，而是出于一种由羞耻心和厌恶感交织而成的复杂情绪。&lt;/p&gt;
&lt;p&gt;抱有这样情绪的我，望着对面的他，感觉他手中的相机就像是一挺枪，将周围的那种审视浓缩成了对我特攻威力巨大的子弹，要将其射入我的胸口，铭刻我此时的惨状。&lt;/p&gt;
&lt;p&gt;“你能笑一笑吗？”他一边端着相机对着我，一边向我示意应当摆出的表情。&lt;/p&gt;
&lt;p&gt;“我尽力...”我当然可以理解他说的含义，那也是世俗“少年感”的一部分，所以我尽可能尝试去这么做了。而从此刻开始，那个源于我特质的麻烦也就出现了。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/4.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;不一会，我们在这一块换了几个地点，拍了几张照片。整个过程中我们的气氛也逐渐缓和，不再那么沉默，随便聊了聊周边环境，广州的生活气息，甚至还有照片中那个可乐售卖机引发出的一系列关于时代感标语的探讨。然而拍完这一组后，我们的话题还是最终落在了那个麻烦的地方。&lt;/p&gt;
&lt;p&gt;“你是不太放的开的那种性格是吧？”X语气轻快，开玩笑般向我问道。&lt;/p&gt;
&lt;p&gt;“嗯，我是比较严肃。”&lt;/p&gt;
&lt;p&gt;“确实，你这个嘴角一直都是这样”他说着比了个︵的手势：“的，总有种生人勿近的感觉。”&lt;/p&gt;
&lt;p&gt;“哎，那也没啥办法，尤其是在相机前。”我无奈的口吻发自真心：“虽然以前恋爱开心的时候确实也︶过不短时间。”&lt;/p&gt;
&lt;p&gt;“那即便是单身，你也可以把这种状态延续下去吧，从你的一个目的来讲，这样会有更多人喜欢你，阻力也小一些。”&lt;/p&gt;
&lt;p&gt;“可能吧，不过既然你也创作过，应该可以理解︵的时候比较容易出好东西，而︶的时候一般是创作不能的。所以...”&lt;/p&gt;
&lt;p&gt;“OK，我理解了，那就按照这种感觉去拍也好。”虽然他这么说，但我明白他应该是理解了他心目中的那种少年感应该是无法达成了。&lt;/p&gt;
&lt;p&gt;下一组照片决定了在江边拍摄。我们过了马路，靠着石质的栏杆，眼下便是缓缓流淌的珠江。在江的对岸是数座林立的高楼，那便是广州的CBD珠江新城，也是这个城市房价最贵的地方。而此时不知是巧合还是命运，我的旁边，正是一个被放在栏杆墩子上的路灯。路灯虽然很小，但看到了它的我还是会心一笑。我不知道X是否也有这样的想法，但他最后敲定的地点就是这里。&lt;/p&gt;
&lt;p&gt;“好就这个姿势，眼睛看向十点钟方向。”&lt;/p&gt;
&lt;p&gt;我坐到了栏杆上，将双腿交叉，双手自然搭在两侧，按X所说望着十点钟的方向，静静等待着他的拍摄。十点钟的方向，是天空，天很蓝，云很少。群青的天空，CROSSING，群青学院广播社，孤独，这样一来确实有些我认为的少年氛围吧。所以至少在这一刻，我确信我应当展露出了少年的情绪，而当最终拍摄结束时，我看到样片时也终于确定了：我的少年时代，大概确实如此。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/5.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;拍完这组后，X提议这个形态室外差不多了，可以换个内景试试。离这最近的比较适合内景的，自然是广东博物馆。我们便回到了方才的那片露营地，绕着它走向博物馆，而在这个途中，一串泡泡在我们眼前飘过，X便有了想法：“你觉不觉得这个很有意思？”我虽然没有完全会意到，但觉得也算有趣，便和他一起走去，完全没有意识到前方的艰难：密度更高的人群，和他人的配合，更多注视着我的人，还多了小孩和他们的家长。不过最后的结果还挺好，虽然我仍然没能笑出来。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/6.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;h3&gt;过度&lt;/h3&gt;
&lt;p&gt;到博物馆时已经快四点了，我们被管理人员告知今日预约已满，便只好考虑接下来的行程。我原定的规划是去TIT，回工位换衣服后直接进入青年状态，在园区内取一些景物后入夜再到海心桥上拍夜景。但X的想法却有些不同，他建议中间加一个“过渡态”，来体现少年到青年的这种转变。&lt;/p&gt;
&lt;p&gt;我想了一下，感觉这个提议确实有道理。一来我确实带了三身衣服备用，二来如果说白日对应少年，黑夜对应青年，那么傍晚黄昏作为过度，也确实是一个绝妙的象征。于是我换上了第二身衣服，银白的休闲西裤，米黄色的衬衫，蓝灰色的休闲西装外套，加上黑框眼镜，恰好对应了白到黑的一个渐变，也体现了少年/青年特质的部分过度——&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;尚未完全颓废的桀骜不驯，尚未演变成疏离的脆弱，尚未完全伪装起来的自我&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;拍摄始于一个比较文艺的咖啡馆，我们的座位侧面是一个挺帅的、打扮讲究的小哥，后面则是看起来在暧昧的男女。X提议为我在这里拍两张照片，他拿起相机拍我的时候，我感觉其他几个人又审视起了我，这让我不太舒服，效果也就不太好。所以在些许休息后，我们终于走出了这里去了外景，我便松了一口气，恢复了状态。&lt;/p&gt;
&lt;p&gt;第一批正式的照片是在不远处拍的，TIT整个园区的绿化都做得很好，找一个枝叶繁盛的背景并不困难。我按照指导站在了一簇簇树木和花草之前，背对着它们后方的双层Loft：微信的访客中心。此刻已经接近傍晚，透过枝叶的阳光没有了之前的凌厉毒辣，变得温暖且柔和。我在X示意下，大概构想了一下应当摆出的姿态，结果仍然是理所当然的克制和拘谨。而后接着这个情绪，我们找个了比较僻静的角落，顺着台阶一路往上，又拍了几张表现出日常和带着颓废的照片。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/7.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/8.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;但这几张更多是体现了那种和解和隐忍，而忽视了反抗，不过即便意识到了问题，我也并未直接提出诉求，而是在接下来的过程中找机会从我这边调整。幸运的是接下来的两个场景非常恰恰到好处，虽然比起达到“被人喜欢”，它们更多的应当是引起大多人的厌恶，但这种审视和倨傲比起白日的那些照片，我认为更能体现我所言的“少年感”，也是我认为少年真正应该拥有的气质。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/9.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/10.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;拍完后面这张后，X对我说：“是不是有你给的那个广州市人民医院的例子的feel了？”我粗略看了下效果，确实如他所言，相比起来病态的气质少了一些，但多了些肃穆的氛围，总体在我看来拍得很好，却也没有过多的感想。但当事后我再次打开它，我便明确了这是&lt;strong&gt;我今天最喜欢的一张照片&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;些许杂草和青苔丛生在脚下的木板上，像一张破败的地毯。红砖砌成的墙壁充满着岁月的痕迹，横竖两道水泥交叉着嵌在其中，像是半截巨大的十字架。我就站在这十字架的正下方，昂着头，宁静得闭着眼，任由斜射在墙上的阳光从半张脸上掠过。&lt;/p&gt;
&lt;p&gt;如此一来，这个阶段就算是结束了。我提议先去客村那边吃个饭再回来换衣服进入下个阶段，二人便向那边走去。而在快要到目的地时，X却停下来，并把我叫住，我回首一看，他正望着边上这条城中村小道，若有所思。我走了过去，没想到今天最考验我羞耻心的瞬间就如此开始了：在这个最为繁华的时刻，城中村的小道行人和车辆络绎不绝，他让我站在入口的正中间，逆着人流，尽可能摆出姿势停留...我照做了，于是今天&lt;strong&gt;他最喜欢的一张照片&lt;/strong&gt;就这么诞生了。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/11.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;“我有这么胖，脸有这么方吗？”裸高173，体重62，照镜子认为自己是鹅蛋脸的我，看着这张被像是特稿封面的照片，不禁陷入了沉思。&lt;/p&gt;
&lt;h3&gt;青年&lt;/h3&gt;
&lt;p&gt;晚餐后已是黄昏尾声，我们回到了TIT换了最后一身衣服，进入今天最后的一个形态——青年，也就是现在的我。从最初的白天，到过渡的黄昏，我们终于来到了符合这个阶段的象征——夜晚。不过在去到规划中的拍摄地点之前，我们先拍了张搞事的照片——《某大厂被毕业无助青年》（逃&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/12.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;以及水中的倒影：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/13.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;接下来便去到了二沙岛海心桥旁的艺术公园，开始了预定的行程。到岛上时，暮色已经缓缓降临，远方的天空自上方的紫色逐渐向下渐变为橘红，挡在前面那树木错落的枝丫在夜色的浸染下通体黝黑，静静得沉默着。我们拉着箱子过了马路，在路边观望着来来往往的人群，顺便寻觅着第一个拍摄地点。而就在此时，我忽然感受一股袭来的倦意，便毫不讲究得坐在了一根石柱上，将手撑在了行李箱上。&lt;/p&gt;
&lt;p&gt;“就这么来两张吧。”我下意识对着X提议到，于是第一张青年阶段的照片就这么诞生了。事后再看回来，它应当是准确得体现出了我想表达的青年阶段特质&lt;strong&gt;颓废，伪装，疏离&lt;/strong&gt;中的“颓废”：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/14.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;坐了一会后，我恢复了一些精神，二人便顺着人流继续向前走，不知不觉就来到了海心桥边。海心桥横驾于珠江之上，被其上众多暖白的灯光的映照着，在这夜色中熠熠生辉。在它之下的珠江被微风带起了阵阵涟漪，水面上的倒影随着波纹摇摇晃晃，让身为半个图形程序我不禁在心中感叹：“这渲染果然还是TM得让三次元来啊，这反射，这高光...”&lt;/p&gt;
&lt;p&gt;而就当我这么想的时候，X示意我去往桥边不远处的护栏前方。我走到了那里，背靠护栏，看着他走到了我的左前方，便随着他的视线向右后望去。在立即明白了他的想法后，我微微侧头望向了十点钟的方向，尽可能表现出了第二个特质“伪装”。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/15.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;随后又在珠江边的另一处拍了张青年的隐藏特质&lt;strong&gt;装傻&lt;/strong&gt;（笑&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/16.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;按原计划来说，下一个地点本应是海心桥，但这个点还排的十分长的队伍证明了我的失策，也打消了我们上去的念头。折返后是漫无目的的闲逛，疲惫的我们穿过了公园，越过了马路，正准备就此回家的时候，却意外发现了一块树上满挂着红灯笼的地方。他停了下来，示意我站在这片草地中间，按下了快门：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/17.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;“你这张很像我的美术老师，二十年后再用吧。”他示意我看着照片。&lt;/p&gt;
&lt;p&gt;“嗯...确实有点显老。”但无论如何，至少这张确实大概表现出了第三个特质“疏离”。&lt;/p&gt;
&lt;p&gt;至此，第一天的拍摄结束，感冒药的药效似乎也完全过了。我脑袋有些昏沉，便拒绝了X邀请我去晚上的一个舞会以“认识圈内朋友”的邀请，打车回到了家后，又让司机将他送到了舞会现场。而在车上是，他提到了付款方式，我表示用大额资金习惯用支付宝，随后我问了句“你现在就要还是？”他表示“先给吧，少点利息。”于是出于信任和与人为善，我便将于先说好的三千先转给了他，最后的一千等全部完成了再给。&lt;/p&gt;
&lt;h2&gt;第二日&lt;/h2&gt;
&lt;p&gt;按照计划，本来应当是第二天就立马去棚里拍第三个形态“少女”，但由于X约定的另一个客户的突发时间安排，时间确实相对比较自由的我便调整了时间，改为了次日的下午。&lt;/p&gt;
&lt;h3&gt;前奏&lt;/h3&gt;
&lt;p&gt;大约下午四点半，我到了摄影棚，发现在场的除了X，还有一对情侣——这正是X的另一个客户，据说是他交了许久的朋友。既然他们还在这里，也就意味着还没拍完。起初我坐在沙发上看了他们一小会，觉得确实还蛮般配，尤其是知道他们异地了不少时间最终成了眷属的情况下。&lt;/p&gt;
&lt;p&gt;但过了不久，我便坐到了化妆台前，由X联系的化妆师给我化妆。&lt;/p&gt;
&lt;h3&gt;化妆&lt;/h3&gt;
&lt;p&gt;由于接下来有个场景还是需要少年/青年两个形态出境，所以我先画了个简单的淡妆，遮了个瑕，然后补了个少年/青年的形态，随后便进入了正式的化妆环节。&lt;/p&gt;
&lt;p&gt;化妆师应该是个三十多的姐姐，看起来已经有多年行业经验了。她一边用娴熟的手法给我上着妆，隔离、打底、遮瑕、修眉、假睫毛、眼线等等，一边还和我搭着话。&lt;/p&gt;
&lt;p&gt;“你这是搞COS嘛？”&lt;/p&gt;
&lt;p&gt;“啊？也不算吧，就是明年就三十了，给自己留个纪念。”&lt;/p&gt;
&lt;p&gt;“三十？看起来一点都不像。”她笑了笑：“感觉就和二十出头一样。”&lt;/p&gt;
&lt;p&gt;“谢谢，很多人都说我显年轻。”我已经不在意对方是不是奉承，说到底这也根本不重要。&lt;/p&gt;
&lt;p&gt;“不过你这想法挺好的，我都三十好几了，那时候也没想着给自己留个纪念。”&lt;/p&gt;
&lt;p&gt;“嗯...我是觉得还是得留下点啥吧。”完全是反射性的回答，我也不知道自己在说什么。&lt;/p&gt;
&lt;p&gt;时间就这样过着，过着，直到最后遮瑕的那一步。&lt;/p&gt;
&lt;p&gt;“小伙子你这皮肤有些差啊，遮瑕估计是没办法完全遮住的。”&lt;/p&gt;
&lt;p&gt;“嗯，最近在做激光试试，你尽力吧。”虽然表现得若无其事，但内心还是有些刺痛吧，但确实也没什么办法，我的基因和出身又不是我能决定的。&lt;/p&gt;
&lt;p&gt;“没事没事，反正现在有灯光和后期，你这个只是在妆下会显得明显些。男汉子粗糙点也正常的。”&lt;/p&gt;
&lt;p&gt;“嗯...”再次反射性的回答，我当然知道对方这是出于礼貌，但我也不想因为这礼貌产生任何感激。&lt;/p&gt;
&lt;p&gt;不过无论如何，虽然没有感激，却也没有憎恨和厌恶，纯粹是没有感觉。没有感觉，意味着麻木，但对于某些事情的麻木，对于我而言，可能并不是一件坏事。&lt;/p&gt;
&lt;h3&gt;少女&lt;/h3&gt;
&lt;p&gt;化妆大概在半小时左右结束，后面化妆师还很靠谱得帮我带上了美瞳和假发。在确定没什么问题后，我按照她和X约定的价格，将三百转了过去，随后换上了第一身衣服——白色LO，并走到了摄影棚的中心，形态“少女”的拍摄正式开始。&lt;/p&gt;
&lt;p&gt;这个棚是一个白棚，和我预想的不太一样。我本来以为回到一个实景棚，里面布置成一个温馨的小房间之类的，有些道具来互动，但这里除了白一无所有。不过既来之则安之，我不擅长提要求，也很讨厌麻烦，便只能对着X和后面到来的他的助理小妹妹，摆起了姿势。&lt;/p&gt;
&lt;p&gt;然后在几张样片后我可悲得发现，即便是穿上了女装，我也没有办法回到几年前穿LO时的那种纯洁可爱的感觉了。&lt;/p&gt;
&lt;p&gt;“这个，我有这么胖吗？”我指着样片，又看了看镜中的自己，感受到了明显的反差。&lt;/p&gt;
&lt;p&gt;“......”X似乎忙了一天，很疲惫，没有做过多回复。&lt;/p&gt;
&lt;p&gt;“算了，靠后期吧。”&lt;/p&gt;
&lt;p&gt;当然，事后我查了一些资料才知道“动眼效应”，图像的扁平化也确实会让人显得更胖，不过还是可以靠一些角度和修饰来解决的。但此刻的我只能无奈回到了原地，继续拍那个“祈祷”的姿势，进而完成了“三位一体”的拍摄：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/18.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;后续我又尽可能找感觉来远离我一贯的那种苦大仇深，进而体现出少女设定中&lt;strong&gt;温暖，纯真，善良&lt;/strong&gt;的特质，但尝试了若干张之后，我才终于算是有了一张勉强有感觉的：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/19.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;而此刻的我也将这种显胖的原因甩给了“白色”，众所周知，白色的衣服本就容易显胖。于是我连忙换上了那身暗红色的日常LO，走到了镜头前。但在拍了几张后，感觉表情和神态还是不怎么对。于是X提议将镜子放在我的身边，以此来让我注意自己的动作和神情，而在这种优化的操作下，我像是忽然找到了那种日常的自我，出了几张感觉不错的图，虽然仍然没有笑容：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/20.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;日常这身拍完后，便是最后一身GOTH系的LO，设定里是由“白”反转而成的“黑”。在换上了这身衣服后，对着镜子的我感觉才找到了真正的自我，那种不协调的违和感瞬间消失，我不需要去强行伪装出任何表情，只需保持日常的不屑和高冷即可。也正因此，这一身的出片品质应该是今天最高的：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/21.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;h3&gt;晚餐&lt;/h3&gt;
&lt;p&gt;拍摄大约在九点左右结束，此刻的我已经非常疲惫，匆匆卸妆收拾完后便准备回家。但在这之前，我下意识问了一句：“你们吃了吗？”&lt;/p&gt;
&lt;p&gt;“还没有，那你请我们吃饭吧。”X如此回应。&lt;/p&gt;
&lt;p&gt;“OK，我找找吧。”我觉得大家确实也辛苦了一天，即便这一天里只有一半是在为我辛苦，但只要结果好，也无妨。&lt;/p&gt;
&lt;p&gt;于是很快我便打了个车，前往目的地：最近的一家海银海记。&lt;/p&gt;
&lt;p&gt;“那我就按我的习惯点了。”落座后，我按照习惯，将那老几样——三花趾，五花趾，吊龙，胸口朥，千张，粿条等加入购物车下了单。然后大家便闲扯了起来。&lt;/p&gt;
&lt;p&gt;“你是他带的学生，所以是准备考北影嘛？”我起了个话头，问了问对面看起来比我高的小妹妹。&lt;/p&gt;
&lt;p&gt;“嗯，考明年的。”&lt;/p&gt;
&lt;p&gt;“明年...你今年本科毕业嘛？也就是00的？”&lt;/p&gt;
&lt;p&gt;“嗯...我01的。”&lt;/p&gt;
&lt;p&gt;“......”虽然我一直号称自己永远年轻，抵制成长，但当一个真的01年应届生坐在我面前时，还是下意识感受到了自己的老去。&lt;/p&gt;
&lt;p&gt;“挺好，不过现在这环境，还去考电影是真爱啊，你本科是传媒吗？”&lt;/p&gt;
&lt;p&gt;“不是...是工科的。”&lt;/p&gt;
&lt;p&gt;“读研要三年，这不景气也不能一直这样吧，就赌一下三年后的环境。”X接着话茬，阐释着对方的选择并非没有道理。&lt;/p&gt;
&lt;p&gt;“说的也是，反正这年景哪行都不行，互联网也裁员，读研避避风头也好。”&lt;/p&gt;
&lt;p&gt;接着顺着这个，我们便聊起了疫情防控，进而聊到了康米，又转进到了情感问题，接下来又是青春与理想，然后是人的异化...&lt;/p&gt;
&lt;p&gt;这诸多可说与不可说，宏大与鸡毛蒜皮，严肃和消失与腹中的牛肉。事后想来，也算是这个时代特有、却又在漫长的历史中有着诸多共性的，极其有趣、讽刺又悲哀的场面吧。&lt;/p&gt;
&lt;p&gt;毕竟那些种种神圣和憧憬，最终都不过是饭桌上的谈资罢了。&lt;/p&gt;
&lt;h2&gt;回顾-要求&lt;/h2&gt;
&lt;p&gt;摄影结束后便是漫长对修图的等待，在这等待中我看着这些原片，回想着这两天的拍摄，说是十分满意肯定是不可能的。而想来这各中的不满的源头，应当正是铭刻在我身上那和“要求”这个词的不适性。&lt;/p&gt;
&lt;p&gt;众所周知“要求”是双向的，一个人提出，一个人接受或者否决。而倘若在一段重要的关系中一个人的要求总是被否决，那么提出要求的这个人便会渐渐沉默，这关系便会走向终结——按常理而言应该如此。但这世界上却并非所有关系都能被单方终结，在这个奴隶已经不存在的世界...不，90后的很多亲子关系，和主人奴隶之间的关系可谓神似。在许多家庭中，孩子在成长中逐渐都患上了习得性无助，便失去了“要求”的能力，或是即便没有失去，却也觉得“要求”本身是错误和可耻的。而我，自然也是其中的一员。&lt;/p&gt;
&lt;p&gt;我非常不擅长提出要求，取而代之的则是表面的讨好人格，与实际上“沉默-&amp;gt;消耗忍耐度-&amp;gt;结束关系”的链路。即便在毕业后终于离开了家中的六年中，我也只是学会了在职场合理得提出要求，在生活中即便是非常努力去改变，却也只是收获寥寥。这极大限制了我的能力边界，也让我白白遭受了许多无谓的伤害。&lt;/p&gt;
&lt;p&gt;这些伤害可大可小，我可以举出很多很小却可笑的例子。最近一次印象深刻的小事，是在公司咖啡店发生的。往常在我点了热柠檬茶后，店员一般会在上面套上一个隔热的套子，来防止烫手。而不知为何，从某一天后，店员就不会再主动套这个套子了。没有了套子的柠檬茶，握在手上，十分得烫，但在相当的一段时间呢，我的做法都是拿起就走。不错，即便这温度让我感到十分不适，我仍然忍耐着握着它走到了两百米外的办公区。&lt;/p&gt;
&lt;p&gt;就这样过了数次后，某天的我又像往常一样点了个热柠檬茶。而下一瞬间，我并未直接拿起它就走，而是像忽然开窍一般，拿起了旁边的一个套子，自己套上了。看着此时手中的柠檬茶，我并没有往常那样灼热的触感，这让我恍惚了一下，然后忽然意识到了什么，接下来便是盘旋在我脑海中的一句话：&lt;/p&gt;
&lt;p&gt;“他不给，你就不能要？他妈的我凭什么不能要？”&lt;/p&gt;
&lt;p&gt;伴随着这句话，过去二十八年的种种场景便如潮水一般灌入了我的脑海。其中记忆最深刻的，应该是我大概在大二的某个夜晚。那晚我忽然感受到了强烈的牙痛，那是一种钻心的疼痛，让我坐立不安。但我第一时间想得居然不是去医院，而是忍着，是喝凉水，是用纸团顶着它，是用种种手段来抑制疼痛，以此期望它自己过去，不要带来更多的麻烦。即便是后来实在忍不住去了医院，我一开始也没有告知父母，而是想着通过自己的生活费省吃俭用省下这就医的钱。直到被医生说很严重，不得不花几千块钱来做烤瓷牙套时，我才不得不打电话给父母。&lt;/p&gt;
&lt;p&gt;如此正当的一个要求，当时的我却至始至终觉得自己像个贼一般，不配得到这样的治疗，浪费了他们的钱。但时候细想，他们缺这点钱吗？并不缺。我的牙变成这样是我自己造成的吗？并不是。那为什么我会感到这么耻辱？那么我只能认为这是从小的各种经历和教育，让我养成了一种我之前并未意识到的性格：&lt;/p&gt;
&lt;p&gt;厌恶和他人争执，厌恶麻烦别人，厌恶请求他人。不错，我并不是不懂得“要求”，只是不喜欢“请求”罢了。因为请求意味着亏欠，而我不想亏欠任何人。所以我只能用一些其他的策略来让他人认为“应当主动给我”，而不是“我向你要的”。但显然，这种手段只对于亲密关系能产生作用，而且基本都有着严重的副作用。对于商业化的交换，这最终只能导致自己吃亏的同时，还憋着一肚子气。&lt;/p&gt;
&lt;p&gt;所以回归到这次拍摄来看，我这“不愿请求”的习惯，确实让我们的拍摄饶了一些弯路。但好在后续和朋友的沟通下，他们给出了一些分析和建议，说“你既然支付了成本和精力，那应当需要让自己满意”。这让我再次意识到了我去“要求”的正当性和必要性，于是便联系了X，来为了补上第三日的拍摄：室内，以及一个实景棚的剧情编排。&lt;/p&gt;
&lt;h2&gt;准备&lt;/h2&gt;
&lt;p&gt;定下了第三日拍摄日程，便要开始准备了。准备分为两个部分，其一是对家中的收拾，让其变得更加整洁以适应拍摄。&lt;/p&gt;
&lt;p&gt;家中的收拾倒是没什么，也确实该来个大扫除了。我对这种事的启动成本虽然一向比较高，但一旦启动了就会尽可能做好。从卧室飘窗开始，书桌，床上，客厅沙发，电脑桌，餐桌...垃圾收拾得差不多后，我开始拿着吸尘器处理灰尘。从地面，到书架，再到电视柜，以及架在电视柜两旁的音箱...&lt;/p&gt;
&lt;p&gt;“...这是？”我停了下来，望着其中一个音箱上的日历。这是去年公司发的新春礼品的一部分，而其上的日期则一直停留在了2021年的7月份。&lt;/p&gt;
&lt;p&gt;“......”脑中忽然闪过了一些回忆，它们一件件稍纵即逝，却又连绵不绝：“你追求留下痕迹，我追求飞过和体验”，“你追求的是无以轮比的闪耀”，“可是代价呢？”......&lt;/p&gt;
&lt;p&gt;“行了行了，瞎几把想啥，干活。”把日历叠起来扔到垃圾袋后，房间很快便被我顺利打扫干净了。&lt;/p&gt;
&lt;p&gt;收拾房间后的第二件事，则是推演和购置棚拍所需要的道具。吸取上次的经验，以及按照编排的剧本要求，我列了一个需要购置的物品清单。按理说这些东西应该是平平无奇，网上随便就能买到的，但实际过程却非常艰难，也让我学到了一些奇怪的知识。&lt;/p&gt;
&lt;p&gt;首先是在黑色男装形态手中的那把枪，我本以为搞一把没有实际发射功能的、一比一的模型枪应该不难，但一开始搜遍全网，却都只能买到1:2.05的模型。在纳闷之中我查了下原因，才知道现在对枪模的管制已经如此之严格，哪怕是外形一样没有功能都不行，为数不多可以等比例的枪模还必须报备，交由专业的剧组团队保管。我虽然可以理解这政策，但着实也带来了很多麻烦。当然好在经过大量的搜索和碰巧，最后我还是找到了一把金属外壳、等比例的、可以发射软弹的左轮手枪，也算是有个好结果。&lt;/p&gt;
&lt;p&gt;然后是GOTH女装形态手中的《圣经》，我本来只是想着搞个道具，想着这问题应该不大吧即便没有那买本书拿着也不是不行，结果无论是淘宝还是京东一搜索都发现根本没有结果。于是我在纳闷中又去查了下原因...嗯，原来如此，那也没招。最后我只能用家中那本海德格尔的《林中路》来凑合替代一下。&lt;/p&gt;
&lt;p&gt;再者在国内电商网站上和“圣经”有着相似待遇的，还有手持的金属十字架。我已经不想再去查原因是啥了，就直接放弃了这个构思，换了种构图。&lt;/p&gt;
&lt;p&gt;总之虽然遭遇了意想之外的麻烦，但也勉强算是准备好了所需要的道具，它们被我收拾了起来，静待着拍摄日的到来。&lt;/p&gt;
&lt;h2&gt;专访直播&lt;/h2&gt;
&lt;p&gt;在这第三次的拍摄日程前，还插入了一段《腾讯程序员》视频号对我的专访。这个专访缘起于今年二月内部的一个邀请，但由于疫情一次次推迟，直到现在才终于定下了日期。虽然还有少许风险，我还是决定义无反顾去了直播室。这首先是源于我对不可控性的厌恶——那是早已准备好的事情的一再延误，所派生出的那种悬而未决的感觉。再者，则又是一个宿命般的巧合，因为我忽然意识到——这次专访，不正好也可以算作是这《Project Self》的一部分吗？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;“写真”静态地从外侧记录了我疏远和断绝于这个世界那封闭的“自我”，“访谈”则动态地自内部挖掘了我连接于这个社会那开放的“外延”。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;直播的整体来看算是成功，即便是一开始由于我的小概率事故体质导致除了一些故障（笑），让直播整体延期了半个小时，导致流失了一些观众，但最后数据还是不错：&lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://www.bilibili.com/video/BV1kY4y187my/&amp;quot;&gt;直播访谈录屏&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://mp.weixin.qq.com/s/M-x5wLvFEhRXAyZOmUlTUg&amp;quot;&gt;访谈后续公众号发文&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;访谈结束的第二天，便是最后一日的拍摄了。&lt;/p&gt;
&lt;h2&gt;第三日&lt;/h2&gt;
&lt;p&gt;第三日比原计划晚了一些，但也正碰巧——那是近一周天气最好、没有雨的一天。这次拍摄起前两次有两点区别。其一是我叮嘱了X别拍那种文艺的感觉，早上就搞一些日常的记录，下午就好好拍剧情编排；其二，则是这次多了一个特别的摄影助手。&lt;/p&gt;
&lt;p&gt;这个摄影助手并非和上次一样是X的学生，而是我的一个朋友。这位中大文学在读博士朋友（下称S）曾经鸽过我两次（笑），由于认知上相对接近，比较聊得来，所以认识了快半年还会不时聊聊天。当之前得知我要拍写真时，她觉得这种创作很有趣，就说要来给我当助理，但由于疫情封校遗憾错过。所以得知我还要补第三次，而她正好也能出校，便二话不说做好了计划。&lt;/p&gt;
&lt;h3&gt;日常&lt;/h3&gt;
&lt;p&gt;我们大概在九点半起了床，此时早晨阳光正好，我将滚滚从阳台抱了出来，开始了第一个场景的拍摄。在这个场景中，我的本意是体现出我在做独立游戏的感觉，显示器上的内容也正是如此：一个32寸的屏幕分成了四块，一块是Unity，一块是写逻辑的VS，一块是写剧本VSCode，一块是框架文档。&lt;/p&gt;
&lt;p&gt;然而X似乎并没有回到我的意，而是直接从左前方，也就是显示器的背面进行了拍摄。当然我是可以理解从我侧后方拍的话，位置比较憋屈，所以也没有多说什么。我照计划装模作样敲起了文字，按理说这场景很快就应该过去，但这臭猫不知道犯了什么病，开始折腾。&lt;/p&gt;
&lt;p&gt;“滚滚，你在干啥这么不配合？”我一边按住它的身子，一边将其拖回原位：“你个臭猫，我好吃好喝伺候着，看病花了我四五万，就配合我拍个父慈子孝都不乐意？”&lt;/p&gt;
&lt;p&gt;但滚滚似乎并不能听进这些道理，反而更加变本加厉得想逃跑。&lt;/p&gt;
&lt;p&gt;“哎...”看来它不吃硬的，只能来软的了。我将其抱回了原位，抚摸着他的头和下巴，让他逐渐冷静了下来，虽然还是满脸不高兴，但至少勉强静止，于是照片就这么诞生了：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/22.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;第一张拍完后，剩下的几张倒是没什么特别好说的，打游戏那张的糖糖虽然也想跑，但好在比较小和轻，自然是逃不出我的魔掌（笑：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/23.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/24.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/25.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;h3&gt;正片&lt;/h3&gt;
&lt;p&gt;拍完早上的日常照后已是接近中午，不一会S便到了小区楼下，我在接她的同时也去楼下趟花店，买了束郁金香来作为今天最后的道具。一切准备就绪，我们就打上了车，前往今天本来定好的目的地——81号摄影棚。&lt;/p&gt;
&lt;p&gt;81号摄影棚是一个广东COS摄影圈内的朋友推荐的，我看了看它家的微博宣传图，觉得里面教堂这个景挺不错，后续虽然也和另一家对比了下，但由于感觉这家的态度比较好，也没想太多就定了下来，其收费是：两小时起订，一小时120，定金150。&lt;/p&gt;
&lt;p&gt;本以为到了地方三个多小时即可结束今天的计划，但实际到了这个坐落于城中村后的棚却让我大跌眼镜：三楼小小的场地，随意凌乱摆放的破旧道具，不伦不类的两侧幕布，以及甩手不过问任何布置的老板。我们在不断调整布景，来来回回忙活了一个多小时后，大家都达成了确实没办法达到我想要的效果的共识，于是当断则断支付了两个小时的费用后及时撤离了。&lt;/p&gt;
&lt;p&gt;撤离后的我们站在路上，已是接近下午三点。我们徘徊在原地，不知如何是好。按照我的规划，找一个真是的教堂，例如“圣心大大教堂”自然是最合适的，但这种地方显然不会让我们进去。&lt;/p&gt;
&lt;p&gt;“要不我先申请入教，然后去拍，拍完了再退教？反正我比他们大对信徒对上帝的理解更深刻。”&lt;/p&gt;
&lt;p&gt;“你可别扯了，人家是要让你信的，谁让你理解了。”&lt;/p&gt;
&lt;p&gt;“哎...”&lt;/p&gt;
&lt;p&gt;犹豫，踟蹰，打趣解决不了实际问题。但我忽然灵光一闪，找到了之前那家觉得态度不好而放弃的棚，在S一个电话得到对方回应后，我们便打了个车，火速去了那里，然后发现这确实是一个质量不错的实景棚。&lt;/p&gt;
&lt;p&gt;“虽然还是不大，但这40平的实景棚看起来已经是广州能找到的最适合的地方了，就今儿拍完吧。”我们做出了决定，拍摄就这样开始了。&lt;/p&gt;
&lt;h4&gt;准备&lt;/h4&gt;
&lt;p&gt;这最后一次的拍摄，重点在于“情节”，情节需要演员出境铺就，而这些演员则是我的多个人格。既然是多个人格一起出镜，那必然就会伴随着技术上的合成，而合成，则需要固定的机位和固定的站位。&lt;/p&gt;
&lt;p&gt;为了达成这个目的，我事先准备好了标签纸和记号笔，同时做好了每个情节、每个角色的序号编排，例如第一个场景的白衣少年是“WB S1”，第三个场景的黑衣少女是“BG S3”等等...写好这些标签后，我就需要把它们贴到地面上，来标记到时候我拍摄应当站在的位置。&lt;/p&gt;
&lt;p&gt;图为S配合我演绎情节中的站位，同时我贴标签的场景：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/26.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;准备结束后，拍摄便正式开始。我总共换了四身衣服，来表现四个人格，而每个人格也基本都有符合自己设定的单人照，以及最后那用于合成的十个场景。至于具体剧情，我就不文字描述了，只能说...我尽力了。&lt;/p&gt;
&lt;h4&gt;单人格&lt;/h4&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/27.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/28.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/29.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;h4&gt;多人格&lt;/h4&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/s1.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/s2-1.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/s2-2.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/s2-3.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/s3.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/s4-1.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/s4-2.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/s5.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/s6-1.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/project-self/s6-2.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;h2&gt;结语&lt;/h2&gt;
&lt;p&gt;经过了将近五个小时的漫长拍摄，除了最后的合成，整个企划终于宣告结束。老板人还是不错的，最后收了三个小时的钱，一小时180，加上妆娘的150，加起来共700。为了庆贺，我请二人吃了顿火锅，在整个庆功宴和最后的返程中，我们又闲聊了许多琐事：我个人在理想和现实中游走平衡的焦虑，X事业和生活间的挣扎和平衡，S学业和规划上的压力等等。想来这次聚餐与上次最大的区别，大概就是在于01年的学生妹子换成了94年的在读博士妹子吧，年龄相仿的三人在一起，讨论的话题自然也就现实了许多。也是因此，我再次意识到：无论外在和人设怎样表现出一种青春少年，但人生阶段确实是不同了。&lt;/p&gt;
&lt;p&gt;在回到家中后的第二天，我整理出了S帮我站位的一些花絮发给了她，聊起整个拍摄过程，她直言：“我觉得你的优点挺多的，尤其是细致和认真。”&lt;/p&gt;
&lt;p&gt;面对这个这个评价，我只能无奈一笑：“原来我展现给别人的是这样一种值得褒奖的品质吗？”作为一个标准的INFP，我太清楚自己的拖延和惰性了，但早已根植于我内核的“决定了就一定要做好”的诅咒，却让我不得不花很大精力去反抗我的本能。这一次次细致的规划和设计，无不伴随着极大的焦虑和数次的失眠，客观来讲这对我的身心造成了不小的负担。不但如此，我之所以会这么早就做好尽可能全面的规划，同时事必躬亲不喜欢他人插手，应当还是出于一种深刻的“不信任”——&lt;/p&gt;
&lt;p&gt;似乎从某个时刻起，我就无法由衷得对任何人产生信任了，无论是朋友，还是父母，亦或是其他时刻的我自己。&lt;/p&gt;
&lt;p&gt;自相矛盾，却又强行保持一致。有憧憬光明向其奔跑的一面，也有封闭于漫长黑夜隔绝一切的一面。是一个能很好运用逻辑和理性工作的青年，也是一个拥抱非理性在荒诞和虚无中挣扎的少年。口口声声说着完成最优先，却又无法真的脱离完美主义的陷阱。不断努力尝试扎根于现实的生活，却还是只能悬于空中无法真正得落到地上。&lt;/p&gt;
&lt;p&gt;这样的我未来会成为一个怎样的人呢？会像五年前、十年前的我对现在的我的态度一样吗？我不知道。但我能确定的是，几年、十几年、甚至几十年后的我看到这个项目，一定会产生一个疑问吧：“这个时刻的我，也就是你，究竟是想记录什么，又想表达些什么？”&lt;/p&gt;
&lt;p&gt;我现在能给出的回答是：“这些照片和访谈都是次要的。最重要的，是这整个记录的过程，是表达自我时的那些态度和情绪，是这不断重复的憧憬、尝试、失望、挣扎、渴求、补偿，以及在精疲力尽后却并仍不完美、满含残缺的‘结局’。”&lt;/p&gt;
&lt;p&gt;“正如，你自己的一生。”&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Saturday, 30 Apr 2022 00:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2022.04.30 00:00:article/Create-project-self</guid>
<category>记录</category>
<category>自我</category>
<category>写真</category>
<category>访谈</category>
</item>

<item>
<title>WebGPU实时光追美少女</title>
<link>http://dtysky.moe/article/Create-2021_10_05_a</link>
<description>&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/webgpu-rt/1/0.png&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;作为一个老二刺螈，我进入这个行业的最初动机可以追溯到十年前打通了《Ever17》的那个下午，这个动机就是——美少女。做一个美少女游戏，是我人生的悲愿，而为了完成这个愿望，我必须要从头开始，学习编程、图形学、编写渲染引擎、乃至实现游戏引擎。而在硬件高速发展的现在，&lt;strong&gt;实时光线追踪&lt;/strong&gt;成为了可能，同时Web平台上的新一代图形API&lt;strong&gt;WebGPU&lt;/strong&gt;提供了丰富的能力也可以让我们进行这样的尝试。&lt;/p&gt;
&lt;p&gt;所以为了渲染一个美少女，我一边学习一边实现，最终完成了这个项目和系列文章教程。本系列文章将会论述如何用WebGPU来实现一个实时路径追踪渲染器，从一个简单渲染器为开端层层深入，了解经典路径追踪渲染器的各个部分，以及BRDF模型在路径追踪中的实现。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;当然，最后因为性能太渣，真实感的美少女并没有被渲染出来，只能渲染一个LowPoly的Miku555&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;项目&lt;/h2&gt;
&lt;p&gt;项目的Github仓库为：&lt;a href=&amp;quot;https://github.com/dtysky/webgpu-renderer&amp;quot;&gt;dtysky/webgpu-renderer&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;Demo为：&lt;a href=&amp;quot;https://dtysky.github.io/webgpu-renderer/&amp;quot;&gt;Demo&lt;/a&gt;，注意目前需要最新的&lt;strong&gt;Chrome Canary&lt;/strong&gt;版本，并且打开特定&lt;code&gt;flag&lt;/code&gt;才行，详见项目的&lt;code&gt;readme&lt;/code&gt;。&lt;/p&gt;
&lt;h2&gt;文章&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/407191699&amp;quot;&gt;概览介绍&lt;/a&gt;：将会对整个项目涉及的知识做一个综述。  &lt;/li&gt;
&lt;li&gt;&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/409670661&amp;quot;&gt;WebGPU基础与简单渲染器&lt;/a&gt;：通过自己实现的一个简单渲染器来论述WebGPU的能力。  &lt;/li&gt;
&lt;li&gt;&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/410439684&amp;quot;&gt;路径追踪-场景数据组织&lt;/a&gt;：针对路径追踪，如何组织场景数据，涉及到PBR材质、glTF、场景合并等。  &lt;/li&gt;
&lt;li&gt;&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/411213324&amp;quot;&gt;路径追踪-管线组织与GBuffer&lt;/a&gt;：针对路径追踪，如何组织渲染管线，同时论述GBuffer的生成。  &lt;/li&gt;
&lt;li&gt;&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/413989102&amp;quot;&gt;路径追踪-BVH与射线场景求交插值&lt;/a&gt;：如何构建BVH，以及如何在CS中求交。  &lt;/li&gt;
&lt;li&gt;&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/416164394&amp;quot;&gt;路径追踪-BRDF与蒙特卡洛积分&lt;/a&gt;：论述如何在路径追踪中运用蒙特卡洛采样实现直接光照和间接光照，以及运用BRDF光照模型。  &lt;/li&gt;
&lt;li&gt;&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/416961002&amp;quot;&gt;路径追踪-降噪与色调映射&lt;/a&gt;：如何对充满噪点的图像进行空间和时间的滤波，最后输出如何进行色调映射。  &lt;/li&gt;
&lt;li&gt;&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/417269967&amp;quot;&gt;踩坑与调试心得&lt;/a&gt;：对于WebGPU这样一个实验性的API（至少当时），我是如何进行调试的血泪史（主要是CS部分）。  &lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;后记&lt;/h2&gt;
&lt;p&gt;由于本人水平有限，文章难免会有纰漏，欢迎各位在评论区积极指正。&lt;/p&gt;
&lt;p&gt;以及即便是能搞这些了，我还是做不出来我的美少女游戏，哎......&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Tuesday, 05 Oct 2021 22:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2021.10.05 22:00:article/Create-2021_10_05_a</guid>
<category>WebGPU</category>
<category>路径追踪</category>
<category>光线追踪</category>
<category>图形学</category>
</item>

<item>
<title>报班学素描和水彩半年成果</title>
<link>http://dtysky.moe/article/Create-2021_08_07_a</link>
<description>&lt;p&gt;接近于连续学了半年吧。&lt;br /&gt;
太菜了，感觉啥都没学会，尤其是越到后面生活中哦出现了很多意外比较燥。&lt;br /&gt;
不过无论如何，算是努力过了，有了些基础，以后在想继续也可以有个底子了。&lt;/p&gt;
&lt;p&gt;（这天赋搞啥艺术啊，还是老老实实想着能不能画美少女吧......&lt;/p&gt;
&lt;p&gt;以下按照画的日期正序：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;0&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/0.webp&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;1&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/1.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;2&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/2.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;3&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/3.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;4&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/4.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;5&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/5.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;6&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/6.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;7&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/7.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;8&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/8.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;9&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/9.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;10&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/10.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;11&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/11.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;12&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/12.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;13&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/13.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;14&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/14.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;15&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/15.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;16&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/16.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;17&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/17.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;18&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/18.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;19&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/19.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;20&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/20.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;21&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/21.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;22&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/22.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;23&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/23.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;24&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/24.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;25&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/25.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;26&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/26.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;27&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/27.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;28&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/28.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;29&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/29.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;30&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/30.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;31&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/31.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;32&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/32.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;33&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/33.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;34&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/34.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;35&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/35.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;36&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/36.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;37&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/37.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;38&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/38.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;39&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/39.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;40&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/40.webp&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;41&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2021_08_07a/41.webp&amp;quot; /&gt;&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Saturday, 07 Aug 2021 00:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2021.08.07 00:00:article/Create-2021_08_07_a</guid>
<category>画画</category>
<category>素描</category>
<category>水彩</category>
<category>学习</category>
</item>

<item>
<title>SEIN.JS - 渐进式Web3D解决方案</title>
<link>http://dtysky.moe/article/Create-2020_07_17_a</link>
<description>&lt;p&gt;SEIN.JS是我在支付宝工作两年的技能集大成作，我负责了整体的架构、整个Runtime（除渲染引擎外）的编写、全量注释的编写、整个官网的编写和所有文档的编写、Unity扩展的设计和编写、Webpack所有工具的编写以及大部分扩展的编写，还支持了许多业务（包括作为2020新春五福的3D部分负责人）。SEIN.JS现在已经完全开源，主要的介绍发在了知乎，这里做个存档：&lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/161234727&amp;quot;&gt;SEIN.JS - 渐进式Web3D解决方案&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://zhuanlan.zhihu.com/p/115989449&amp;quot;&gt;亿级前端项目中的3D技术-支付宝2020年新春活动的背后&lt;/a&gt;&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Saturday, 18 Jul 2020 00:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2020.07.18 00:00:article/Create-2020_07_17_a</guid>
<category>SEIN.JS</category>
<category>3D</category>
<category>WebGL</category>
<category>图形学</category>
<category>游戏引擎</category>
</item>

<item>
<title>GIGA GJ2018作品-摩的大飚客</title>
<link>http://dtysky.moe/article/Create-2018_07_08_a</link>
<description>&lt;h2&gt;Muddy Driver (摩的大飚客)&lt;/h2&gt;
&lt;p&gt;一个胡逼游戏。项目工程：&lt;a href=&amp;quot;https://github.com/dtysky/muddy-driver&amp;quot;&gt;Muddy Driver&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;cover&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2018_07_08a/cover.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;h2&gt;需求&lt;/h2&gt;
&lt;p&gt;一个PC。&lt;br /&gt;
四个玩家。&lt;br /&gt;
四个手机。&lt;br /&gt;
良好的局域网。  &lt;/p&gt;
&lt;h2&gt;如何游玩&lt;/h2&gt;
&lt;p&gt;Clone code, 运行:  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;npm install
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;打开 &lt;code&gt;client/src/config.ts&lt;/code&gt;, 修改 &lt;code&gt;url&lt;/code&gt; 为 &lt;code&gt;${your_ip}:4444&lt;/code&gt;，运行:  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;npm run build
npm run server
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;打开 &lt;code&gt;${your_ip}:4444/view&lt;/code&gt;, 出现LOGO：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2018_07_08a/1.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2018_07_08a/2.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;在此画面点击：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2018_07_08a/0.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;使用手机扫描二维码，都就位后点击start按钮，进入游戏后，每个车要两个人操控，一个使用方向传感器控制方向，一个使用滑动触摸控制速度：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2018_07_08a/3.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2018_07_08a/4.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2018_07_08a/5.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;先到终点线的就算赢:  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2018_07_08a/6.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;h2&gt;Staff&lt;/h2&gt;
&lt;p&gt;程序:  &lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://github.com/dtysky&amp;quot;&gt;dtysky(H光)&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/greenSnot&amp;quot;&gt;greenSnot(虫合)&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;美术:  &lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;&amp;quot;&gt;shishi(诗诗)&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;&amp;quot;&gt;spot&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;主策划:  &lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://github.com/greenSnot&amp;quot;&gt;greenSnot(虫合)&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;&amp;quot;&gt;陶然&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;&amp;quot;&gt;dtysky(H光)&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2018_07_08a/staff.jpg&amp;quot; /&gt;  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 08 Jul 2018 23:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2018.07.08 23:00:article/Create-2018_07_08_a</guid>
<category>Gamejam</category>
<category>Babylonjs</category>
<category>Race</category>
<category>Device</category>
</item>

<item>
<title>My world</title>
<link>http://dtysky.moe/article/Create-MyWorld</link>
<description></description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Monday, 18 Jun 2018 22:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2018.06.18 22:00:article/Create-MyWorld</guid>
<category>少年H</category>
<category>世界</category>
<category>列车</category>
</item>

<item>
<title>hana-ui - 一个清新二次元风格的React-UIKit</title>
<link>http://dtysky.moe/article/Create-2018_02_24_a</link>
<description>&lt;p&gt;&lt;img alt=&amp;quot;Logo&amp;quot; src=&amp;quot;http://oekm6wrcq.bkt.clouddn.com/hana-ui/logo.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;hana-ui(&lt;a href=&amp;quot;https://hana-ui.moe&amp;quot;&gt;官网&lt;/a&gt;，&lt;a href=&amp;quot;https://github.com/hana-group/hana-ui&amp;quot;&gt;Github&lt;/a&gt;)是一个二次元清新风格的React-UIKit，使用ES6+SASS+Typescript开发，拥有丰富的组件用以构建复杂前端界面系统，目前已在Bilibili管理后台和一些前端页面进行了应用。  &lt;/p&gt;
&lt;p&gt;有兴趣的来点个&lt;a href=&amp;quot;https://github.com/hana-group/hana-ui&amp;quot;&gt;Star&lt;/a&gt;吧www&lt;/p&gt;
&lt;h2&gt;初心&lt;/h2&gt;
&lt;p&gt;这个项目是在B站完成的，完成者是我和另外两个同事，其名字源于我和其中一位同事的一次饭后讨论，死宅嘛，总想要搞出一些东西为二次元啊业界啊做出些贡献，于是便有了这个UIKit，懂行的人一看&lt;strong&gt;hana-ui&lt;/strong&gt;这个名字大概也就明白这个库是什么风格了。  &lt;/p&gt;
&lt;p&gt;其实不严格来说，这个项目在去年五月份差不多就完全完成并投入使用了，但由于公司等原因一直没有开发出来，而在这空闲的几个月我们将其升级到了React16，并添加了一些新的特性，并进一步完善了官网和文档。&lt;/p&gt;
&lt;h2&gt;定位&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&amp;quot;overview&amp;quot; src=&amp;quot;http://oekm6wrcq.bkt.clouddn.com/hana-ui/hana-overview.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;定位方面，由于我们团队都比较务实，比较讨厌现在吹来吹去的所谓“Design Language”，所以定位也就是一个单纯的React用UIKit，一个UI组件库。&lt;/p&gt;
&lt;h2&gt;设计&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&amp;quot;components&amp;quot; src=&amp;quot;http://oekm6wrcq.bkt.clouddn.com/hana-ui/hana-components.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;基本UIKit应该有组件的都有了。组件分为种子（seeds）、芽（burgeon）和花（flower）三部分，复合性层层递增。  &lt;/p&gt;
&lt;p&gt;风格方面，主题可配置，默认走清新风格。  &lt;/p&gt;
&lt;p&gt;代码方面。主代码走一套ES6加上一套Eslint（项目开始的时候还没上Typescript），不过补上了一套ts头可以和Typescript兼容。  &lt;/p&gt;
&lt;p&gt;测试暂时没有，但在复杂工程中过了很久了所以也算是得到了最好的测试。关于纯前端视图层的单元测试必要性问题这个见仁见智，我的观点是模型可以测，视图方面测试纯属吃力不讨好。&lt;/p&gt;
&lt;h2&gt;请帮助我们！&lt;/h2&gt;
&lt;p&gt;不过想必大家也发现了，现在UIKit整体的设计有些地方还有些糙，这个我们也很无奈。我不知道公司如何定位我们这个组件库，当时（现在也是）处于弱势的我们部门并没有为这个组件库争取到设计资源，所以设计也是我们三个自己操刀的。  &lt;/p&gt;
&lt;p&gt;让前端来做设计嘛，和让设计来写前端差不多，做到这样我们已经尽力了，所以在这里如果有专业的设计师死宅同学给出帮助我们是再欢迎不过了。  &lt;/p&gt;
&lt;p&gt;不过由于不被重视，所以这个库的开源和B站毫无关系，完全是&lt;a href=&amp;quot;https://github.com/hana-group&amp;quot;&gt;hana-group&lt;/a&gt;自行维护，自由度也高一些。  &lt;/p&gt;
&lt;p&gt;二次元本就是一个去中心化的创作集群，由无数有爱人士共同贡献，在这一点上hana-ui也是一样的。&lt;br /&gt;
个人力量毕竟有限，所以希望大家能积极参与！&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Saturday, 24 Feb 2018 16:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2018.02.24 16:00:article/Create-2018_02_24_a</guid>
<category>hana</category>
<category>UIKit</category>
<category>React.js</category>
</item>

<item>
<title>GGJ2018作品-EternalFlame</title>
<link>http://dtysky.moe/article/Create-2018_01_28_a</link>
<description>&lt;p&gt;&lt;img alt=&amp;quot;Cover&amp;quot; src=&amp;quot;http://oekm6wrcq.bkt.clouddn.com/eternal-flame/assets/cover.jpg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;第四次参加GameJam，职位从抱大腿的变成了主程（233&lt;/p&gt;
&lt;p&gt;不过遗憾的是这次魔都居然分为了四个场子。。。人也分散开了，感觉氛围比上此心动的GGJ差（没有女装（逃&lt;/p&gt;
&lt;p&gt;本来这次想休闲搞个20小时能做出来的，没想到还是爆肝到四五点了...虽然最后完成度还是有问题，不过好歹DEMO算是完整，也算不错了。&lt;/p&gt;
&lt;p&gt;果然老了啊，选择自己基本没用过的引擎（phaser）现学现做（各种暴力代码）还是有点“i&amp;apos;m fucked”的感觉，不过好歹结果不错www&lt;/p&gt;
&lt;p&gt;官网地址：&lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://globalgamejam.org/2018/games/eternal-flame&amp;quot;&gt;Eternal Flame&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;游戏地址：&lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;http://oekm6wrcq.bkt.clouddn.com/eternal-flame/index.html?13&amp;quot;&gt;Eternal Flame&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;源代码：&lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://github.com/dtysky/EternalFlame&amp;quot;&gt;dtysky/EternalFlame&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;操作：&lt;/p&gt;
&lt;p&gt;建议PC，PC打开后窗口拖到 9/16的大小，用键盘上下左右控制加速度。&lt;/p&gt;
&lt;p&gt;移动端的话就用那几个按钮吧，不过注意要连续点按（时间不够没做完长按的&lt;/p&gt;
&lt;p&gt;（下次在下一定要做黄油（每届都这么说的（逃&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 28 Jan 2018 23:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2018.01.28 23:00:article/Create-2018_01_28_a</guid>
<category>Gamejam</category>
<category>phaser</category>
</item>

<item>
<title>D2-现代前端-对视觉和交互的探索</title>
<link>http://dtysky.moe/article/Create-2017_12_17_a</link>
<description>&lt;p&gt;今年我作为分享者参与了阿里D2会议，做了一个关于B站近来对视觉和交互方面探索的分享。在这里，我将以一篇文章的方式，将分享里PPT的内容再论述一遍，算是加深印象，也方便未来鞭策自己。&lt;/p&gt;
&lt;p&gt;PPT在此：&lt;a href=&amp;quot;https://www.jianguoyun.com/p/DfQBE0MQs43CBhialT4&amp;quot;&gt;现代前端-对视觉和交互的探索&lt;/a&gt;&lt;br /&gt;
&lt;a href=&amp;quot;https://tianchi.aliyun.com/competition/videoStream.html?from=singlemessage#postsId=3623&amp;quot;&gt;视频在此&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;圈内的大家想必都明白一个现状——现在的WEB前端圈子、尤其是国内的前端圈子，工程师们自己对前端工作的评价和用户对前端工作的评价其实是脱节的。也就是说，很多时候，我们自己觉得用了一个十分牛逼的技术，做了很多工程、架构上的突破，但用户却要么没什么感觉、要么反而觉得还不如原来的。诚然，我们可以用所谓用户的“惯性”或者“不懂”来麻痹自己，但这并不能解决任何问题。  &lt;/p&gt;
&lt;p&gt;个人来看，这其实和近年来前端的飞速发展和从传统领域一下搬运了很多概念有关。飞速发展并非不好，但现在来看，相比传统前端“直接服务于用户感官”的方向，大前端方向显然吸引了更多前端从业者的目光。这也不难理解，毕竟对于个人发展而言，掌握更多的技术资源总是好的，而且相较于传统前端的视觉和交互方向，大前端看起来能做更多事、更能被老板重视。可悲的是，这一点在国内很多时候也确实是事实——这样导致的结果就是我上面所言，前端从业者逐渐脱离了用户、不再去研究用户观感和交互，而是一窝蜂向着后端侵袭。  &lt;/p&gt;
&lt;p&gt;这使得当下的WEB前端处于一个尴尬的位置——抛弃了老本行，和后端抢饭碗。但本质上，在后端领域，我们其实也就是二把手，根本接触不到核心、也做不到核心。所以，我觉得抛弃“视觉”和“交互”这两个属于我们本职的技术是不合理的。我知道很多程序员都会觉得单纯粗暴的逻辑、优雅的工程架构更加有亲和性，这一点曾身为FPGA工程师的我也十分清楚。但我们也不能为做效果、探索交互麻烦就不去研究它，如果失去了这两样，我们前端的核心竞争力又到底是什么——毕竟只是写逻辑的话，后端或者算法同学不一定比我们差，对吧。  &lt;/p&gt;
&lt;p&gt;工程化确实要研究，如何让系统更加鲁棒也是必要的，代码规范、设计模式、基本数据结构和算法、FP等也确实需要学习，但那些是每个程序员都应该做到的，和前后端并无太大关系。我们真正的核心竞争力还是在于研究如何让用户更好地体验到新技术带来的感官冲击、带来的交互便捷，这才是我们的老本行。  &lt;/p&gt;
&lt;p&gt;所以这一年来，我在B站的工作基本完全由活动后台系统转到了大型活动开发，在这样的一些活动上，我得以运用一些新的技术、去做一些新的尝试。其中一个明显的例子在于在这些活动上我干掉了IE全家桶、安卓5以下、iOS8.3以下，这使得在做活动的时候也可以伸展拳脚。  &lt;/p&gt;
&lt;p&gt;而这也是我本次分享主题的来源——《现代前端——对视觉和交互的探索》。  &lt;/p&gt;
&lt;p&gt;本次分享分为几部分：  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;第一部分是我关于前面所做活动的回顾，技术分析我以前也放过了，主要是&lt;a href=&amp;quot;http://dtysky.moe/article/Skill-2017_06_12_a&amp;quot;&gt;BML2017主视觉技术剖析&lt;/a&gt;、&lt;a href=&amp;quot;http://dtysky.moe/article/Skill-2017_08_27_a&amp;quot;&gt;Bilibili《七夕之约 - Double;7》技术剖析&lt;/a&gt;还有下周要上线的圣诞活动（活动文章已经写好，等上线放出）。毕竟是一场技术分享，所以还是要有一些干货的。不过受限于时间，无法讲的太细，所以可能会有些同学觉得太散了，这一点可以利用我前面所写的文章补齐。  &lt;/li&gt;
&lt;li&gt;第二部分是对前端老本行在过去和现在状态的一个总结，描述了前端从单调页面到现在的SPA发展了许久，并且有了Electron和RN等使得WEB前端逐渐和桌面应用和APP等分担压力，抛出了一个问题——Web前端的下一步应该怎么走？能不能像大前端方向一样，从传统领域搬点东西？&lt;/li&gt;
&lt;li&gt;第三部分则承接前面的问题，论述了本人在其他领域的一些见闻，道出了桌面应用、移动应用、数据分析和艺术几个领域一直以来的发展和趋势，指明了前端能走的路子。&lt;/li&gt;
&lt;li&gt;第四部分则是审视自己，表明了Web前端的潜力和责任——我们拥有最普及的平台，所以有把新技术带给用户的责任，而且现在Web各种标准一个接一个出现，也带给了我们这些能力。&lt;/li&gt;
&lt;li&gt;最后是以一个桌面、移动、VR三端适配，具有物理引擎的DEMO为例的展望，并分享了一些我觉得酷炫的网页例子。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;综上，其实本人是有一个诉求、也希望前端大家能回归初心，不要放弃前端的传统任务、也就是那个&lt;strong&gt;初心&lt;/strong&gt;（这么看起来这个分享更适合上一届2333）。当然，人各有喜好，不过其实一种技术的发展普及是必然依靠广泛从业者的努力的，如果大家都不去做了，那么——  &lt;/p&gt;
&lt;p&gt;当然，大前端方向还是要学习的，这已经成为了前端工程师的一个门槛，但在此之外，我么能做的、应该做的，还有更多。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 17 Dec 2017 23:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2017.12.17 23:00:article/Create-2017_12_17_a</guid>
<category>前端</category>
<category>D2</category>
<category>PPT</category>
</item>

<item>
<title>【React/Flask】BlogReworkPro-Rework the BlogRework</title>
<link>http://dtysky.moe/article/Create-2016_10_14_a</link>
<description>&lt;p&gt;Github工程在此：&lt;a href=&amp;quot;https://github.com/dtysky/BlogReworkPro&amp;quot;&gt;BlogReworkPro&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;重构&lt;a href=&amp;quot;http://dtysky.moe/article/Create-2016_03_12_a&amp;quot;&gt;BlogRework&lt;/a&gt;，这是此Blog迎来的第四次重构了，和上一次的间隔比预期要早一些，不过这种事早点没啥坏处。这次重构主要是重写了前端、修了一些后端的BUG，跟进ES6，用Eslint和Flow约束代码规范，上了React最佳实践全家桶并且实现了完美的服务端渲染，加了Memory Cache，样式换成了less，DOM语义化也做了，构建工具也换成了gulp，也就是说，上一次遗留的Feature基本都搞定了。这次重构对个人的能力提升显著，还是很值得的。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;基础架构&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;添加了一些Bash和Npm脚本，并采用了新的以环境变量为基础的配置文件，二者配合使得开发发布更加轻松和工程化。&lt;/li&gt;
&lt;li&gt;跨域访问控制。  &lt;/li&gt;
&lt;li&gt;正确使用了Forever.js使得进程守护更加完备。  &lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;后端&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;在生产环境下，使用tornado的wsgi框架来替代Flask自己的server，增强性能和稳定性。  &lt;/li&gt;
&lt;li&gt;使用virtualenv构建独立的python来隔离系统原生的python，屏蔽系统差异（虽然不如docker），详见&lt;a href=&amp;quot;https://github.com/dtysky/BlogReworkPro/blob/master/install.sh&amp;quot;&gt;install.sh&lt;/a&gt;。  &lt;/li&gt;
&lt;li&gt;增强了日志管理器，使其更加完善，详见&lt;a href=&amp;quot;https://github.com/dtysky/BlogReworkPro/blob/master/Back/utils.py&amp;quot;&gt;Back/utils/logger&lt;/a&gt;。  &lt;/li&gt;
&lt;li&gt;将&lt;code&gt;watchdog.observers.Observer&lt;/code&gt;替换为&lt;code&gt;watchdog.observers.polling.PollingObserver&lt;/code&gt;，消去了一些BUG。  &lt;/li&gt;
&lt;li&gt;修复了一些写入数据库时的对比BUG。  &lt;/li&gt;
&lt;li&gt;实现了Memory cache，详见此文章：&lt;a href=&amp;quot;http://dtysky.moe/article/Skill-2016_10_13_a&amp;quot;&gt;【Flask/React】此博客服务端的缓存实现&lt;/a&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;前端&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;上了React + Redux + React-router + Immutable全家桶，详见此文章：&lt;a href=&amp;quot;http://dtysky.moe/article/Skill-2016_10_09_a&amp;quot;&gt;【React/Redux/Router/Immutable】React最佳实践的正确食用姿势&lt;/a&gt;。   &lt;/li&gt;
&lt;li&gt;Less带我飞，取代了原生CSS，详见此文章：&lt;a href=&amp;quot;http://dtysky.moe/article/Skill-2016_10_14_a&amp;quot;&gt;【Less】实现可选参数以及各种autoprefixer&lt;/a&gt;。  &lt;/li&gt;
&lt;li&gt;完全使用ES6语法，并使用Eslint和Flow来进行代码规范化约束，详见&lt;a href=&amp;quot;https://github.com/dtysky/BlogReworkPro/blob/master/Front/.eslintrc&amp;quot;&gt;Front/.eslintrc&lt;/a&gt;。  &lt;/li&gt;
&lt;li&gt;将回调基本全部用Promise替换，回调地狱Bye~  &lt;/li&gt;
&lt;li&gt;消除了Jquery，改用superagent来做ajax请求，比如&lt;a href=&amp;quot;https://github.com/dtysky/BlogReworkPro/blob/master/Front/src/actions/source.js&amp;quot;&gt;Front/src/action/source.js&lt;/a&gt;，消去了JS动画库的依赖，完全使用CSS3动画替代。  &lt;/li&gt;
&lt;li&gt;用Katex替换了MathJax来做Tex解析，自己写了自动渲染文章的转换器，详见&lt;a href=&amp;quot;https://github.com/dtysky/BlogReworkPro/blob/master/Front/src/utils.js&amp;quot;&gt;Front/src/utils/renderWithKatex&lt;/a&gt;。  &lt;/li&gt;
&lt;li&gt;DOM结构语义化。  &lt;/li&gt;
&lt;li&gt;用Gulp替换了Grunt来构建Task，详见&lt;a href=&amp;quot;https://github.com/dtysky/BlogReworkPro/blob/master/Front/gulpfile.js&amp;quot;&gt;Front/gulpfile.js&lt;/a&gt;。  &lt;/li&gt;
&lt;li&gt;重绘了一些SVG图标，使其完美化，比如&lt;a href=&amp;quot;https://github.com/dtysky/BlogReworkPro/blob/master/Front/src/theme/image/playstation.svg&amp;quot;&gt;playstation.svg&lt;/a&gt;。  &lt;/li&gt;
&lt;li&gt;增强了主题，优化了很多细节，使其更加有设计感，比如&lt;code&gt;pagenation&lt;/code&gt;，文章列表的样式。  &lt;/li&gt;
&lt;li&gt;将一些资源进行打包或者内嵌，对图片进行压缩，减少向服务端的请求次数，使得渲染更加流畅，比如字体和小图标这种就打入了两个文件并内嵌为base64，用webpack实现自动内嵌。  &lt;/li&gt;
&lt;li&gt;完美的服务端渲染，详见此文章：&lt;a href=&amp;quot;http://dtysky.moe/article/Skill-2016_10_11_a&amp;quot;&gt;【React/Redux】深入理解React服务端渲染&lt;/a&gt;。  &lt;/li&gt;
&lt;li&gt;实现了Memory cache，详见此文章：&lt;a href=&amp;quot;http://dtysky.moe/article/Skill-2016_10_13_a&amp;quot;&gt;【Flask/React】此博客服务端的缓存实现&lt;/a&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;彩蛋&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;404页面： &lt;a href=&amp;quot;http://dtysky.moe/x&amp;quot;&gt;任意一个找不到的页面&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;打开控制台。&lt;/li&gt;
&lt;/ol&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Friday, 14 Oct 2016 22:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2016.10.14 22:00:article/Create-2016_10_14_a</guid>
<category>Blog</category>
<category>重构</category>
<category>React.js</category>
<category>Redux</category>
<category>Flask</category>
<category>MongoDB</category>
<category>Express.js</category>
<category>服务端渲染</category>
</item>

<item>
<title>MoeNotes</title>
<link>http://dtysky.moe/article/Create-2016_05_20_a</link>
<description>&lt;h2&gt;MoeNotes&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&amp;quot;logo&amp;quot; src=&amp;quot;//src.dtysky.moe/image/moe-notes/logo.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;演示：&lt;br /&gt;
&lt;a href=&amp;quot;http://www.bilibili.com/video/av4703145/&amp;quot;&gt;演示视频&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;Github:&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/MoeNotes&amp;quot;&gt;MoeNotes&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;下载：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/MoeNotes/releases&amp;quot;&gt;下载&lt;/a&gt;  &lt;/p&gt;
&lt;h2&gt;介绍&lt;/h2&gt;
&lt;p&gt;MoeNotes是一个简单的日记写作软件（当然，我也用其写一些不是特别复杂的小说和文章），不同于印象笔记、Onenote、Leanote等，本软件没有这么大的野心，它所要解决的问题仅仅是“本地展示”，也就是在本地管理你的日志文件，并提供一个类似于Onenote的分类体验。&lt;br /&gt;
本软件使用Markdown作为笔记编写语言，适配GFM语法并增加语法高亮和数学公式，每一篇日记都会以.md文件的形式保存到本地而不是数据库中，用户可以自行选择如何同步这些文章以及将它们同步到多少地方，完全不依赖于平台，这也是我编写本软件而不使用现成日记软件的一个主要的原因。当然，对文章和分类进行拖动排序也是被支持的。除此之外，本软件还支持“即写即看”，“专注写作”和“专注阅读”三种模式，也可以进行主题的切换和自定义，还支持PDF和HTML的导出，由于是基于web进行得开发，所以扩展起来也十分方便和简单。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;preview-main&amp;quot; src=&amp;quot;//src.dtysky.moe/image/moe-notes/preview-main.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;preview-books&amp;quot; src=&amp;quot;//src.dtysky.moe/image/moe-notes/preview-books.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;preview-export&amp;quot; src=&amp;quot;//src.dtysky.moe/image/moe-notes/preview-export.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;h2&gt;许可&lt;/h2&gt;
&lt;p&gt;Copyright © 2015, 戴天宇, Tianyu Dai (dtysky &amp;lt; dtysky@outlook.com &amp;gt;). All Rights Reserved. This project is free software and released under the GNU General Public License (GPL V3).&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Friday, 20 May 2016 10:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2016.05.20 10:00:article/Create-2016_05_20_a</guid>
<category>Moe</category>
<category>Note</category>
<category>Electron</category>
<category>Javascript</category>
<category>Node.js</category>
<category>Markdown</category>
</item>

<item>
<title>[Flask/React/MongoDB]BlogReworkIII-如何搭建一个动态Blog</title>
<link>http://dtysky.moe/article/Create-2016_03_12_a</link>
<description>&lt;p&gt;&lt;a href=&amp;quot;https://github.com/dtysky/BlogRework&amp;quot;&gt;项目源在这里&lt;/a&gt;&lt;br /&gt;
这是此Blog迎来的第三次重构了，使用Flask、React、Mongo、Express等将原先的静态网站完全替换为了单页应用，当然是响应式设计。重构的理由非常简单—这种简单也是源于我习惯性的随性而为。当然，虽然理由简单，一开始设想得也很简单，但实际做起来却又是另外一番景象了。通过这次重构，我最大的收获或许并不是技术上的提升，而是对工程周期的评估和把握能力的提升—作为唯一的管理和执行者，亲眼见证了一个工程是如何从两天的预想周期膨胀为一周、随后膨胀为半个月、最后膨胀为一个月的。作为一个能力平平的平常人综合征患者，或许只有这点坚持的精神还能拿的出手了吧（笑&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Blog发展历程&lt;/h2&gt;
&lt;p&gt;建博客，也是要按照基本法的，你不能说建，你就建，总是想搞个大新闻，又没什么干货，这样，还不如纯粹装个逼，你说是不是？当年的我啊，还是太年轻，图样图森破，上台拿衣服，没什么审美，也没什么技术，所以，搞出的那个东东啊，自己觉得一颗赛艇，其实，在删的时候，比谁都快，搞得连个底子都没有留，到时候后悔了，我还是要负责的吧？&lt;/p&gt;
&lt;h3&gt;2014.05&lt;/h3&gt;
&lt;p&gt;由于未来找工作的考虑以及当时泛滥的助人之心，用初步可用的Python功底加上刚上手的Html + Css技术，在&lt;a href=&amp;quot;https://github.com/getpelican/pelican&amp;quot;&gt;Pelican&lt;/a&gt;框架的帮助下用十天左右首次将Blog搭建起来。此次使用的是默认主题中的一个基于bootstrap的主题，嘛，标准的技术博客风格吧。当时还没有服务器和域名等等的概念，所以这个Blog只是被简单地托管到了&lt;strong&gt;dtysky.github.io&lt;/strong&gt;上（现在已经无效）。  &lt;/p&gt;
&lt;h3&gt;2014.06&lt;/h3&gt;
&lt;p&gt;对HTML+CSS有了更加深入的认识，第一次的重构开始。由于当时的审美还停留在一个比较low的水准，加上当时的中二还没有经过否定之否定这个螺旋上升的过程，所以无论是主题还是分类都比较不堪入现在的目，而且当时对很多东西还是不了解，基本所有的布局都是用静态图像完成的。不过值得一提的是，在这一次，我初步使用了JS—我对这个语言的初步影响并不好，觉得他的语法就是SHIT。&lt;br /&gt;
原始设计已经遗失，唯一还存在的就是这一篇当时的记录233：&lt;a href=&amp;quot;http://dtysky.moe/article/Life-Summary_old&amp;quot;&gt;世界线启动&lt;/a&gt;。&lt;br /&gt;
看看当时的分类www（其实现在看来也蛮带感的，我还是挺喜欢的：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;经验而谦逊的现实&lt;/li&gt;
&lt;li&gt;雅致而耗散的诗人&lt;/li&gt;
&lt;li&gt;矛盾而真诚的世界&lt;/li&gt;
&lt;li&gt;纯粹而残缺的歌姬&lt;/li&gt;
&lt;li&gt;平凡而美丽的生活&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;2015.02&lt;/h3&gt;
&lt;p&gt;半年后的春节期间完成。随着技术的进一步提升和更进一步的审美，加上中二的否定之否定的螺旋上升（不要在意期间发生了什么），便有了这一次的重构。此次重构主要是样式上的变更，首先将分类转换，然后修改了整体的布局，并基于jquery实现了一些动画。此次重构后的界面和现在的界面十分接近，不同的只有左侧动画（原先是先滑出左侧，再划回来）、播放器和一些载入动画，当然，最大的不同自然还是静态网站和动态网站的区别了。&lt;br /&gt;
不仅使界面上的修改，在此次重构中我注册了现在使用的&lt;strong&gt;dtysky.moe&lt;/strong&gt;这个域名，同时将网站搬移到了DigitalOcean的VPS上，用NGINX作为服务器。此外还加入了Disqus的评论系统、bshare的分享系统以及各个搜索引擎的SEO。&lt;br /&gt;
这次做好了保留工作，老样式的网站保留在这里：&lt;br /&gt;
&lt;a href=&amp;quot;http://old.dtysky.moe&amp;quot;&gt;old.dtysky.moe&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;2016.02&lt;/h3&gt;
&lt;p&gt;又是春节期间，也是毕业后的第一个春节。在毕业的这半年内，我完成了一次离职，并从FPGA工程师转为了软件工程师（现在是WEB）。&lt;br /&gt;
某日，我发现我还是想要加一个音乐播放器在网站上的，所以也就需要将Blog变成动态网站，这就是为什么我花了一个月的业余时间的理由。。。&lt;/p&gt;
&lt;h2&gt;To(Never www)Do list&lt;/h2&gt;
&lt;h3&gt;Server端的Cache&lt;/h3&gt;
&lt;p&gt;在现在的设计下，前端每一次的请求都会导致后端的一次数据库查询操作，并且对于那些有分页的、列举文章的页面，Client端会先从服务端拉取所有的文章简介，然后存储在前端的Cache中。这其实是不太好的，首先这可能会浪费一些带宽（当然，在GZIP的压缩下，对比JS和CSS文件这些数据量都是小CASE），其次比较重要的一点是会造成后端端数据库的查询压力，对于性能不那么强的VPS，这对并发还是有比较大的影响的。考虑到Blog的文章更新还是相对较慢的，所以一个简单Cache的存在还是可以允许的。&lt;br /&gt;
具体的设计，无非就是在某些数据被请求时先去找Cache，如果没有就查询数据库并将其加入Cache，然后在文章有更新时将Cache清空即可。 &lt;/p&gt;
&lt;h3&gt;用Redux和ES7对前端再次重构&lt;/h3&gt;
&lt;p&gt;现在的设计对SEO并不友好（虽然我已经使用了React-router的服务端渲染（由于有AJAX存在，所以单独使用对SEO并没什么用）并且使用了fragment这个meta tag提示搜索引擎这是一个ajax页面（但这毕竟只能对谷歌有效）），所以我需要一个方法来解决React服务端渲染和客户端的数据同步问题，这个可能只能看看REDUX这样的框架了。&lt;br /&gt;
至于ES7么。。。我觉得我暂时无法用ES5写出干净的前端代码，而下一次重构的时候ES7也差不多出来了。。。&lt;/p&gt;
&lt;h2&gt;本次设计&lt;/h2&gt;
&lt;h3&gt;后端&lt;/h3&gt;
&lt;h4&gt;架构&lt;/h4&gt;
&lt;p&gt;用Python实现。&lt;br /&gt;
作为Blog的后端，无非由六个方面需要考虑，其一是对原始文章的解析，其二是数据库管理，其三是文件监控，其四是Sitemap和RSS的生成，其五是WEB服务器，最后一个就是作为辅助的日志管理了，以下是一张基本的设计框架图，然后根据这张图来简单介绍我此次的设计和用到的技术。
//src.dtysky.moe
&lt;img alt=&amp;quot;FlowChart1&amp;quot; src=&amp;quot;http://src.dtysky.moe/image/blog/2016-03-12a/1.svg&amp;quot; /&gt;&lt;/p&gt;
&lt;h4&gt;文章解析&lt;/h4&gt;
&lt;p&gt;手写HTML文章是笨蛋才会去做的事情（虽然我的确手写过一段时间233），现在一般Blog文章的编写都是使用Markdown或者Restructuredtext，冰鞋比起常常用于Documents编写语言的后者，前者由于语法简洁等原因使用范围更广，所以本Blog的文章使用也是Markdown。&lt;br /&gt;
Markdown固然足够满足文章正文的需求，但有一些meta元素，比如文章标题、作者、分类、时间等要素还是需要自己去想办法添加的，我这里借鉴、或者说是延续了Pelican的设计，将所有的meta元素添加到了文章头，和正文用空行隔开，分别对meta和正文进行解析，最后汇总，所以最后编写的文章实际上是这样的：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;Title&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;test&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;Authors&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;dtysky&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;Tags&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;test1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;test2&lt;/span&gt;

&lt;span class=&amp;quot;err&amp;quot;&gt;#&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;h1&lt;/span&gt;
&lt;span class=&amp;quot;o&amp;quot;&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;要注意的是这里并没有Category这个标签，这是因为为了延续之前的设计，并且为了简化工作（其实就是懒...），所以直接将该文章所在的目录作为了分类；除此之外，Authors这个标签也不是必须的，如果没有，解析器会使用配置文件中的default_author代替。  &lt;/p&gt;
&lt;h5&gt;Meta解析&lt;/h5&gt;
&lt;p&gt;Meta解析的实现是十分简单的，无非就是将meta的tag和value作为键值对写入一个hashtable中，单考虑到日后的可扩展性，如上面的结构图所示，我将其分为了两个部分：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;MetaParsers：用于将原始的以字符串的形式存在的meta文本解析为一个hashtable或者任何需求的数据形式，所有parsers都继承自&lt;code&gt;MetaDataParser&lt;/code&gt;父类，文章解析程序在初始化的时候会载入每一个子类并创建一个对象，并在解析时调用该对象的&lt;code&gt;parse&lt;/code&gt;方法，对该对象对应的meta元素的值进行解析，并返回被解析过的数据。  &lt;/li&gt;
&lt;li&gt;SlugWrappers：用于生成已经解析过的metadata的url，也就是会在最终的url里出现的、表征该metadata的唯一定位符，这个可以按照喜好自己来，我大部分是默认为不转换（也就是url和原始数据一致），诚然这可能会带来一些SEO问题，但为了防止冲突和便于日后管理，我舍弃了原先Pelican自带的大写转小写、汉字转拼音的做法。这一步会在&lt;code&gt;parse&lt;/code&gt;完成后执行，所有子类对应对象的&lt;code&gt;warp&lt;/code&gt;方法会被调用，将输入的每一个单个的meta元素转换为url。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;通过这两部的解析，原始的meta文本就被转换为了可以供后续流程处理的数据，在我自身的设计中，可能会出现在url中的metadata都会有&lt;code&gt;view&lt;/code&gt;和&lt;code&gt;slug&lt;/code&gt;两个元素，一个用于显示，一个用于定位。&lt;br /&gt;
得益于这种设计，meta都是可扩展的，如果我想添加一个meta，只需要分别从&lt;code&gt;MetaDataParser&lt;/code&gt;和&lt;code&gt;SlugWrapper&lt;/code&gt;继承一个子类，简单重写一下其中的某些成员函数便可以实现。  &lt;/p&gt;
&lt;h5&gt;Markdonw解析&lt;/h5&gt;
&lt;p&gt;这里直接使用了Python的&lt;code&gt;Markdown&lt;/code&gt;库，但另外加入了&lt;code&gt;Pygments&lt;/code&gt;语法高亮插件，有需要的时候也可以加入其他插件（比如表格）。  &lt;/p&gt;
&lt;h4&gt;数据库管理&lt;/h4&gt;
&lt;p&gt;文章解析之后便需要存到地方，这个地方自然就是数据库了（当然静态页面也可以，不过那并不是此Blog的做法），这里选取的是&lt;code&gt;MongoDB&lt;/code&gt;，原因么...最近用得比较多，顺手就这么选了。这个数据库本身没有什么说的，用起来还是很简单，这部分主要难度还是在于如何管理数据库的写入（这可后面要说到的文件监控有很大关系），考虑解耦的原则，这一部分只负责接受数据和指令对数据库进行操作，不负责决策。&lt;br /&gt;
和上面的设计一样，我设计了一个&lt;code&gt;DatabaseWriter&lt;/code&gt;父类，所有的Meta和正文的写入类都继承自这个父类，之后可以重写父类的方法来完成逻辑，在最基本的设计中，每个子类都拥有三个对外的接口函数&lt;code&gt;insert&lt;/code&gt;、&lt;code&gt;update&lt;/code&gt;和&lt;code&gt;delete&lt;/code&gt;，分别用于想数据库中插入一篇文章、更新一篇文章或者删除一张文章，这是一种增量式的设计，保证每次只会对北改动过的文章进行解析和写入。这一步在文章解析后被执行，最终被调用的就是上面提到的那几个方法，接受解析过的完整文本，将数据写入子类对应的表中。&lt;br /&gt;
这里提一句，被写入数据库中的记录是包含文章文件的路径的，这会在后面用到。数据库的设计在此：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/BlogRework/blob/master/Back/Database.md&amp;quot;&gt;Database desigin&lt;/a&gt;  &lt;/p&gt;
&lt;h4&gt;文件监控&lt;/h4&gt;
&lt;p&gt;文件解析和数据库管理都搞定之后，缺少的就是提供输入激励的东西了。这里采用的数据驱动的设计，程序监听文件的状态，在状态发生变化时响应这些变化并作出响应的处理。&lt;br /&gt;
这里选取的是&lt;code&gt;Watch dog&lt;/code&gt;这个系统状态监听库，其中的&lt;code&gt;FileSystemEventHandler&lt;/code&gt;这个模块中提供了良好文件状态事件支持，只需要指定一个监听的文件夹并去重写它预设的&lt;code&gt;on_create&lt;/code&gt;等方法便可以实现逻辑（这里需要注意，在OSX和LINUX下事件触发的状况不同，所以配置文件中才会有&lt;code&gt;is_linux&lt;/code&gt;这个选项），设计如下：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;当文件新建时，触发&lt;code&gt;on_create&lt;/code&gt;事件，解析文章并将其&lt;code&gt;insert&lt;/code&gt;入数据库。&lt;/li&gt;
&lt;li&gt;当文件被删除时，触发&lt;code&gt;on_delete&lt;/code&gt;事件，通过文件名找到数据库中该文章对应的记录，反向找到正文和所有meta，随后调用所有&lt;code&gt;writer&lt;/code&gt;的&lt;code&gt;delete&lt;/code&gt; 方法。&lt;/li&gt;
&lt;li&gt;当文件被修改时，触发&lt;code&gt;on_modified&lt;/code&gt;事件，解析文章并将其&lt;code&gt;update&lt;/code&gt;入数据库。&lt;/li&gt;
&lt;li&gt;当文件被移动时，等价于一次删除和一次新建。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;如此，文件的解析部分基本就完成了。  &lt;/p&gt;
&lt;h4&gt;Sitemap和RSS&lt;/h4&gt;
&lt;p&gt;这两个基本是Blog的标配了，一个用于SEO一个用于用户订阅，其实也没什么难度，就是每次文本解析完毕后读取数据库中所有的文章根据格式生成几个XML文件，稍微研究一下二者的各市标准就行，不过这其中还是稍微有点坑的：&lt;br /&gt;
在XML中插入HTML需要转义：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;entity_ref&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;{&lt;/span&gt;
    &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;&amp;lt;&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;&amp;amp;lt;&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;&amp;amp;gt;&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;&amp;amp;&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;&amp;amp;amp;&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;&amp;#39;&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;&amp;amp;apos;&amp;quot;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;
    &lt;span class=&amp;quot;s1&amp;quot;&gt;&amp;#39;&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt; &lt;span class=&amp;quot;s2&amp;quot;&gt;&amp;quot;&amp;amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&amp;quot;p&amp;quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;考虑到这二者实在没有什么扩展性的必要，我就直接写死了，RSS生成的有全站、作者和分类。&lt;/p&gt;
&lt;h4&gt;WEB服务器&lt;/h4&gt;
&lt;p&gt;数据源准备好之后就是想前端给数据的事了，所以需要开一个WEB服务器。这里选取了Flask作为服务器框架，路由规则也比较简单不再赘述。和上面大多数设计一样，每一条路由规则都对应一个继承自&lt;code&gt;WebHandler&lt;/code&gt;的父类，这个父类又继承自Flask中的View类，父类通过重写View类的&lt;code&gt;dispatch_request&lt;/code&gt;方法来响应请求（采用这种方式来做路由而不是装饰器的方式是为了便于后面路由的统一管理）。&lt;br /&gt;
程序初始化时，程序首先会找到&lt;code&gt;WebHandler&lt;/code&gt;的所有子类并注册路由，之后接收到请求时，程序首先会找到该条路由规则对应的&lt;code&gt;handler&lt;/code&gt;对象，然后调用它的&lt;code&gt;dispatch_request&lt;/code&gt;方法，&lt;code&gt;dispatch_request&lt;/code&gt;方法又会调用&lt;code&gt;_handle&lt;/code&gt;方法进行最终响应的实现。&lt;/p&gt;
&lt;h4&gt;日志管理&lt;/h4&gt;
&lt;p&gt;在主体功能之外还有一个重要的功能是被需求的——日志管理。系统需要有全局的&lt;code&gt;logger&lt;/code&gt;来记录关键的信息同步打印并输出到文件，由于这个比较简单我就自己造轮子了：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;logger输出的信息有三个等级—error、info和warnning，分别打印为不同的颜色并输出到不同的文件。&lt;/li&gt;
&lt;li&gt;每天新建一个log文件便于分析。  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;至此，后端的实现基本完成。&lt;/p&gt;
&lt;h3&gt;前端&lt;/h3&gt;
&lt;p&gt;Node.js实现。
前端主要分为两部分—Client和Server，其中Client是大头，负责和用户进行交互，Server负责将Client端需求的数据传送到用户设备。在本设计中Server还有服务端渲染和SEO的职能。&lt;br /&gt;
当然，一时由于经验，而是由于。。。ES5也没规范到哪去，感觉JS这个语言还是有很多缺陷的，ES6似乎好了很多，嘛。。。先凑合着吧，code style的确很烂我承认。&lt;/p&gt;
&lt;h4&gt;工具链&lt;/h4&gt;
&lt;p&gt;前端开发最好还是有一个用于开发测试的工具链，这对于效率的提升是立竿见影的。这里我选择了&lt;code&gt;Grunt&lt;/code&gt;和&lt;code&gt;Webpack&lt;/code&gt;，前者用于自动化事务管理，后者用于打包和测试项目。我的设计如下：&lt;br /&gt;
//src.dtysky.moe
&lt;img alt=&amp;quot;FlowChart2&amp;quot; src=&amp;quot;http://src.dtysky.moe/image/blog/2016-03-12a/2.svg&amp;quot; /&gt;&lt;/p&gt;
&lt;p&gt;其中&lt;code&gt;debug&lt;/code&gt;、&lt;code&gt;server-build&lt;/code&gt;、&lt;code&gt;client-build&lt;/code&gt;三个事务分别用于测试、打包Server端程序和打包Client端程序，&lt;code&gt;build&lt;/code&gt;事务包含了前两者，简化操作。  &lt;/p&gt;
&lt;h4&gt;Client&lt;/h4&gt;
&lt;p&gt;Client负责的是用户交互，使用了&lt;code&gt;React&lt;/code&gt;和&lt;code&gt;React-router&lt;/code&gt;作为View和前端路由，具体的设计如下：  &lt;/p&gt;
&lt;h5&gt;View&lt;/h5&gt;
&lt;p&gt;基础显示，包括整体视图的布局和一些动画效果，页面属于响应式设计，为PC和移动端分别走了适配，页面组件包括：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Home，显示所有文章的列表。&lt;/li&gt;
&lt;li&gt;Authors， 显示所有作者的列表。&lt;/li&gt;
&lt;li&gt;Tags， 标签云。&lt;/li&gt;
&lt;li&gt;Author，显示某个作者所有文章的列表。&lt;/li&gt;
&lt;li&gt;Tag，显示某个标签所有文章的列表。&lt;/li&gt;
&lt;li&gt;Category，显示某个分类所有文章的列表。&lt;/li&gt;
&lt;li&gt;Article，显示文章。&lt;/li&gt;
&lt;li&gt;Title，标题栏，显示所有分类。&lt;/li&gt;
&lt;li&gt;Left-image，左侧图像滑窗。&lt;/li&gt;
&lt;li&gt;Menu和Menu-phone，前者在PC端的右下角，后者在移动端的上面。&lt;/li&gt;
&lt;li&gt;Footer，移动端的最下方。&lt;/li&gt;
&lt;li&gt;Music-player，音乐播放器。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;动画效果包括：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;元素色彩和基本位移操作，使用CSS中的&lt;code&gt;transition&lt;/code&gt;实现。&lt;/li&gt;
&lt;li&gt;元素旋转和特殊运动，使用&lt;code&gt;animation&lt;/code&gt;和&lt;code&gt;keyframe&lt;/code&gt;实现。&lt;/li&gt;
&lt;li&gt;左侧的图像滑动，使用&lt;code&gt;Velocity-react&lt;/code&gt;库实现。  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;在页面资源方面，将原来的png图标全部封装成了字体和svg文件，找不到线程的就自己手撸...最终素材大小和请求次数的确降低了许多，封装工具在&lt;a href=&amp;quot;https://icomoon.io/app/#/select&amp;quot;&gt;这里&lt;/a&gt;。&lt;/p&gt;
&lt;h5&gt;等待、错误和404&lt;/h5&gt;
&lt;p&gt;这些细节的地方也是需要关注的，具体是什么效果就自己试吧www：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;等待界面，自己的log加上半圆边框不断旋转，“少女祈祷中”是什么梗就不说了，这里其实是想搞一个抽象的、机械齿轮转动的效果。&lt;/li&gt;
&lt;li&gt;一般错误界面，未知错误的界面，一般看不到的，基本就是机械齿轮卡壳的感觉。&lt;/li&gt;
&lt;li&gt;404界面，直接&lt;a href=&amp;quot;/404&amp;quot;&gt;在这&lt;/a&gt;看吧&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h5&gt;Meta修改&lt;/h5&gt;
&lt;p&gt;虽说是单页应用，但每个不同的页面有自己的meta tag还是很重要的，比如&lt;code&gt;title&lt;/code&gt;、&lt;code&gt;keywords&lt;/code&gt;之类的，这里我是用了React-helmet组件对他们进行了修改（当然，本质上应该还是DOM操作就是，图个方便。）&lt;/p&gt;
&lt;h5&gt;分享组件&lt;/h5&gt;
&lt;p&gt;在每篇文章的右上角都会有一个分享的图标，本次重构去掉了对bshare的依赖，自己写了个分享组件，觉得设计的还成，九大社交网站+本页面二维码。  &lt;/p&gt;
&lt;h5&gt;音乐播放器&lt;/h5&gt;
&lt;p&gt;这也是导致本次重构的元凶了，这个播放器基于&lt;a href=&amp;quot;https://github.com/DIYgod/APlayer&amp;quot;&gt;APlayer&lt;/a&gt;，并做了些许的定制性修改。播放源需要自己在配置文件中进行配置，在当前设计中每次修改完音乐列表就必须重新打包发布一遍Client端的代码，不过把它提出来也不是什么难事，有时间吧。。。&lt;br /&gt;
在我自己的设计中，这个播放器还和文章meta中的&lt;code&gt;music&lt;/code&gt;标签关联，当文章中存在这个标签时，播放器会重新载入文章中制定的音乐列表进行播放，否则就会载入配置中默认的列表。  &lt;/p&gt;
&lt;h5&gt;前端路由&lt;/h5&gt;
&lt;p&gt;这个是用&lt;code&gt;React-router&lt;/code&gt;实现的，也是单页应用的精髓，有了它，伪静态的页面便是轻而易举，他会抓取浏览器的历史并完成响应的路由，页面每次只会更新路由指定的区域。&lt;/p&gt;
&lt;h5&gt;缓存&lt;/h5&gt;
&lt;p&gt;为了避免请求过多，这里建立了一个&lt;code&gt;Cache&lt;/code&gt;用于缓存之前向后端请求过的数据，在页面每次加载之前程序会首先从Cache里取数据，如果没有才会请求并将获得的数据加入Cache中。&lt;/p&gt;
&lt;h5&gt;其他&lt;/h5&gt;
&lt;p&gt;Google analysis和Mathjax的支持以及......彩蛋。&lt;br /&gt;
我认为彩蛋是一个网站必须的东西，它可以出现在Meta里，也可以出现在Code的Comments里，而我，则是将其放在了Console...&lt;/p&gt;
&lt;h4&gt;Server&lt;/h4&gt;
&lt;p&gt;Server框架是&lt;code&gt;Express&lt;/code&gt;，也算是从主流了，主要完成：  &lt;/p&gt;
&lt;h5&gt;静态资源&lt;/h5&gt;
&lt;p&gt;用&lt;code&gt;Express&lt;/code&gt;自带的&lt;code&gt;static&lt;/code&gt;中间件实现，此条规则用于访问静态资源。&lt;/p&gt;
&lt;h5&gt;服务端渲染&lt;/h5&gt;
&lt;p&gt;对所有的正常页面请求，使用&lt;code&gt;React&lt;/code&gt;的&lt;code&gt;renderingToString&lt;/code&gt;方法在服务端进行首页渲染，遗憾的是对于本设计这并不能解决SEO问题（前面说到过），所以，就当是提升用户体验吧。&lt;/p&gt;
&lt;h5&gt;针对搜索引擎的页面&lt;/h5&gt;
&lt;p&gt;每一条正常的url都对应着一条特殊的url -&amp;gt; (/xxx -&amp;gt; /jade/xxx)，这个页面是为搜索引擎（或者说就是谷歌）提供的，具体原理是在正常页面的meta中加入一个&lt;code&gt;fragment&lt;/code&gt;的tag，谷歌找到这个tag后就会在原始url后加上&lt;code&gt;?_escaped_fragment_&lt;/code&gt;，我们可以在Server捕捉它并将准备好的另外一个页面提交给谷歌。我所准备的页面是用&lt;code&gt;jade&lt;/code&gt;作为模板的，并用&lt;code&gt;requests&lt;/code&gt;向后端发起请求来获取数据。这些页面去掉了动画和播放器，基本可以看做纯粹由HTML+CSS构成的静态页面了。  &lt;/p&gt;
&lt;h5&gt;日志管理&lt;/h5&gt;
&lt;p&gt;这里同样需要一个Logger，我选取的是&lt;code&gt;Tracer&lt;/code&gt;这个库，将其封装为了一个中间件。  &lt;/p&gt;
&lt;p&gt;到此，前端基本完成。&lt;/p&gt;
&lt;h5&gt;其他&lt;/h5&gt;
&lt;p&gt;NGINX作为反向代理服务器，Forever.js做进程守护。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Saturday, 12 Mar 2016 22:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2016.03.12 22:00:article/Create-2016_03_12_a</guid>
<category>Blog</category>
<category>重构</category>
<category>React.js</category>
<category>Flask</category>
<category>MongoDB</category>
<category>Express.js</category>
<category>Velocity.js</category>
<category>Node.js</category>
<category>Python</category>
<category>Markdown</category>
<category>WatchDog</category>
<category>Jade</category>
<category>Css</category>
<category>SEO</category>
</item>

<item>
<title>毕设PPT</title>
<link>http://dtysky.moe/article/Create-2015_06_05_a</link>
<description>&lt;p&gt;将鼠标指向框内即可。&lt;br /&gt;
设计的是到一定的步骤按&amp;quot;Return&amp;quot;返回，点下一个大分类。&lt;br /&gt;
没有可以按的时候按空格或者方向键。&lt;/p&gt;
&lt;hr /&gt;
&lt;div style=&amp;quot;text-align: center;margin-top: 50px&amp;quot;&gt;
    &lt;iframe  style=&amp;quot;border-style: dotted;border-color: black&amp;quot; border-style= align=&amp;quot;middle&amp;quot; width=&amp;quot;800px&amp;quot; height=&amp;quot;600px&amp;quot; src=&amp;quot;http://show.dtysky.moe/graduation-ppt/ppt.html&amp;quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 07 Jun 2015 16:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.06.07 16:00:article/Create-2015_06_05_a</guid>
<category>FPGA-Imaging-Library</category>
<category>毕业设计</category>
<category>impress.js</category>
</item>

<item>
<title>毕设项目V1.0发布</title>
<link>http://dtysky.moe/article/Create-2015_06_02_a</link>
<description>&lt;p&gt;这部分是致谢，放在这里算了。。。虽说最后论文从六万被砍到一半。。。嘛，没经验废话太多吧。  &lt;/p&gt;
&lt;p&gt;项目主页在这里：&lt;br /&gt;
&lt;a href=&amp;quot;http://fil.dtysky.moe/&amp;quot;&gt;FPGA-Imaging-Library&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;论文在这里：&lt;br /&gt;
&lt;a href=&amp;quot;http://dtysky.moe/tag/fpga-imaging-library.html&amp;quot;&gt;F-I-L 论文&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;在写完论文的最后一个字时，我感到了莫大的喜悦和恐惧，喜悦的是作为毕业设计的部分终于算是比较完美得被完成，恐惧的是这个开源项目要想存活下去，还有很多东西需要完成，而我不知道自己是否有这个精力和决心。&lt;br /&gt;
四年前，我来到了这个东南大学，一年的懵懂和迷惘之后，感谢黄安杰同学的引导和鼓励，我走了出来，开始利用学校基于的资源和平台认真学习技术。一晃便是三年，三年间，我完成了一个大型SRTP项目，在本院张小国老师、电子学院刘新宁老师和机械学院邓老师的热心指导下，自学了软件、机械、硬件设计方面的众多知识，这也为我的毕业设计打下了良好的基础。&lt;br /&gt;
十二个月前，我参加了本校和Xilinx合作的暑期学校，在一个月的学习和研究后主动成为了Xilinx的实习生，在做了一些工作之后，最终决定在Xilinx完成毕业设计，这要感谢王立辉老师和陆家华经理，给我提供了去做自己想做的东西的机会。&lt;br /&gt;
这个项目最初是以设计一个五子棋系统为导向的，但是很遗憾由于摄像头设备的原因无法完成完美的去噪，而当意识到之时时间已经不足以从头再来了，所以我选择进行转型，世界上有用轮子的人，但也有造轮子的人，而在FPGA的图像处理方面，虽然已经有了一些公司，比如NI的FPGA图像库，但是基本都是闭源的，都需要在支付不菲的费用之后获得授权。所以，出于对开源项目的热情，我选择去实现一个开源的FPGA图像处理库，并竭尽所能让其标准化和规范化，对用户提供友好的测试流程，这也算是对将进入的工程界做一次前置演练吧。在协议方面，我选择了LGPL协议，选择这个协议的原因是——我希望有更多的人可以使用它，但是拒绝将其本质上的商业化，虽然可能也只是我的一厢情愿吧。发布选择了建立博客所使用的Pelican框架，波形图使用了Wavedrom进行绘制，同时感谢Github的Student Developer Pack，提供了一年免费的Digital Ocean的VPS，用于搭建项目主页。&lt;br /&gt;
再次感谢王立辉老师、陆家华经理和和Xilinx中前辈的指导，让我能够最终完成计划中的任务。&lt;br /&gt;
最后，向提供了测试图像的画师表示感谢：  &lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;http://www.pixiv.net/member.php?id=122500&amp;quot;&gt;月岡月穂&lt;/a&gt; &lt;a href=&amp;quot;http://www.pixiv.net/member.php?id=420928&amp;quot;&gt;LM7&lt;/a&gt; &lt;a href=&amp;quot;http://www.pixiv.net/member.php?id=2824699&amp;quot;&gt;cotta&lt;/a&gt; &lt;a href=&amp;quot;http://www.pixiv.net/member.php?id=2823291&amp;quot;&gt;041&lt;/a&gt;  &lt;a href=&amp;quot;http://www.pixiv.net/member.php?id=60263&amp;quot;&gt;H2SO4&lt;/a&gt; &lt;a href=&amp;quot;http://www.pixiv.net/member.php?id=414162&amp;quot;&gt;パセリ&lt;/a&gt; &lt;a href=&amp;quot;http://www.pixiv.net/member.php?id=3952&amp;quot;&gt;おにねこ&lt;/a&gt; &lt;a href=&amp;quot;http://www.pixiv.net/member.php?id=1444045&amp;quot;&gt;色原みたび&lt;/a&gt; &lt;a href=&amp;quot;http://www.pixiv.net/member.php?id=225421&amp;quot;&gt;ぜろきち&lt;/a&gt; &lt;a href=&amp;quot;http://www.pixiv.net/member.php?id=2671042&amp;quot;&gt;方向錯亂&lt;/a&gt; &lt;a href=&amp;quot;http://www.pixiv.net/member.php?id=19365&amp;quot;&gt;あきのん&lt;/a&gt; &lt;a href=&amp;quot;http://mikumikumiku.blogbus.com/tag/%E5%88%9D%E5%BF%83%E7%A4%BE/&amp;quot;&gt;初心社&lt;/a&gt;  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Tuesday, 02 Jun 2015 22:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.06.02 22:00:article/Create-2015_06_02_a</guid>
<category>FPGA-Imaging-Library</category>
<category>毕业设计</category>
</item>

<item>
<title>Message board for FPGA-Imaging-Library</title>
<link>http://dtysky.moe/article/Create-2015_05_04_a</link>
<description>&lt;h2&gt;Progress:&lt;/h2&gt;
&lt;p&gt;Building documents...  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;What ?&lt;/h2&gt;
&lt;p&gt;F-I-L is a open source library for image processing on FPGA, it already contents many useful operations, and is updating. All the operations are packaged to IPCores, and of course they follow a same and standardized interface, moreover, each of them can work on piplines-mode and req-ack mode.&lt;br /&gt;
F-I-L是一个FPGA平台的开源的图像处理库，已经拥有了许多常用操作，并在不断更新中。这些操作被以IP核的形式进行了封装，遵循同一种规范化的接口，同时具有流水线和请求响应两种使用模式。
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://fil.dtysky.moe/theme/image/what.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;How ?&lt;/h2&gt;
&lt;p&gt;Certainly, all IPCores have their software simulations, functional simulations and testing on board, the same folder structure and interface can make user do simulations and testing conveniently.&lt;br /&gt;
大部分IP都拥有自己的软件仿真、功能仿真和板上测试，并且具有相同的文件结构和接口，能够让使用者很方便得进行测试和仿真。目前IP形式只支持Xilinx的Vivado套件，不排除未来会向Quartus搬移的可能。 
&lt;img alt=&amp;quot;&amp;quot; src=&amp;quot;http://fil.dtysky.moe/theme/image/how.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;More&lt;/h2&gt;
&lt;p&gt;More informations are &lt;a href=&amp;quot;http://fil.dtysky.moe&amp;quot;&gt;here&lt;/a&gt;. &lt;br /&gt;
更多信息请看&lt;a href=&amp;quot;http://fil.dtysky.moe&amp;quot;&gt;这里&lt;/a&gt;。  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Monday, 04 May 2015 02:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.05.04 02:00:article/Create-2015_05_04_a</guid>
<category>FPGA-Imaging-Library</category>
</item>

<item>
<title>My Resume</title>
<link>http://dtysky.moe/article/Create-MyResumeEn</link>
<description>&lt;div id=&amp;quot;resume-header&amp;quot;&gt;
    &lt;img id=&amp;quot;resume-photo&amp;quot; src=&amp;quot;/theme/image/head.png&amp;quot; alt=&amp;quot;head&amp;quot;&gt;
    &lt;div class=&amp;quot;resume-title&amp;quot;&gt;
        &lt;h2&gt;Tianyu Dai&lt;/h2&gt;
        &lt;p&gt;E-Mail : dtysky@outlook.com&lt;/p&gt;
        &lt;p&gt;Homepage : dtysky.moe&lt;/p&gt;
        &lt;p&gt;Github : dtysky&lt;/p&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;hr /&gt;
&lt;h2&gt;Skills&lt;/h2&gt;
&lt;h4&gt;Platform:&lt;/h4&gt;
&lt;p&gt;FPGA, PCB, Software, Machine   &lt;/p&gt;
&lt;h4&gt;Tools：&lt;/h4&gt;
&lt;p&gt;Expert : VHDL, Verilog, SystemVerilog, Python, Html, Css, Cadence&lt;br /&gt;
Competent : C, C#, JS, Matlab, AutoCad&lt;br /&gt;
Average  : Unity3d, AE, PR, PS  &lt;/p&gt;
&lt;h4&gt;Knowledge background：&lt;/h4&gt;
&lt;p&gt;Machine vision, CPU, Embedded system  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Major Projects&lt;/h2&gt;
&lt;h3&gt;梦见星空之诗 - Aria der Freiheit und des Seins：&lt;/h3&gt;
&lt;p&gt;Discription：An indie game。&lt;br /&gt;
Size: Large &lt;br /&gt;
Role：Planner, Writer, Programmer&lt;br /&gt;
Progress：Refactoring the scenario&lt;br /&gt;
Used skills:  &lt;br /&gt;
Writing, Python, Ren&amp;apos;py&lt;br /&gt;
Source: Closed source &lt;br /&gt;
Completed work：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Game script parser  &lt;/li&gt;
&lt;li&gt;Manuscript for game, 700000 words  &lt;/li&gt;
&lt;li&gt;system and GUI for game  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;3D Displayer：&lt;/h3&gt;
&lt;p&gt;Discription：An 3d displayer based on a high-speed rotation led array &lt;br /&gt;
Size: Large &lt;br /&gt;
Role：Work individually&lt;br /&gt;
Progress：Basically finished&lt;br /&gt;
Used skills:  &lt;br /&gt;
PCB, FPGA, C, C#, Autocad&lt;br /&gt;
Source: &lt;a href=&amp;quot;https://github.com/dtysky/3D_Displayer_Controller&amp;quot;&gt;Controller&lt;/a&gt;，&lt;a href=&amp;quot;https://github.com/dtysky/Led_Array&amp;quot;&gt;PCB&lt;/a&gt;，&lt;a href=&amp;quot;https://github.com/dtysky/3D_Displayer_Machine&amp;quot;&gt;Machine&lt;/a&gt; &lt;br /&gt;
Completed work：   &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;mastered Cadence suite, designed a 120*114 LED array composed by 0603LED, PMOS, NMOS the pixel density is 15.2d/cm^2. At the same time, I finished the controlling circuit that integrates trigger, register, power, and the control of the LED array. It has a 4-layer structure with high density wiring. The manuscript is mainly wrote by python, which improved the mapping efficiency  &lt;/li&gt;
&lt;li&gt;learned to use FPGA; using a series of  Cyclone IV components provided by Altera, with VHDL, I&amp;apos;ve finished:&lt;br /&gt;
1) DDR2 controller, starting from sketch. Coding with Testbench, completed the packaged data testing with Modelsim, also succeed the on-board test&lt;br /&gt;
2) LED controller, starting from sketch. Using ROM completed the on-board test&lt;br /&gt;
3) Learned to utilize the high-speed USB chip cy68013 on slave-fifo mode, finished the FPGA controlling component and tested on board  &lt;/li&gt;
&lt;li&gt;With C#, using the API that is provided by cy68013 chip .net, utilizing object-oriented, multi-tasking ability, etc programmed the host computer  &lt;/li&gt;
&lt;li&gt;With C, designed the firmware for Cy68013  &lt;/li&gt;
&lt;li&gt;Designed the mechanical parts, includes the base, the coupling, and the scaffold for the LED array (AutoCAD)   &lt;/li&gt;
&lt;li&gt;Matlab based 3d images slicing    &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Game script parser：&lt;/h3&gt;
&lt;p&gt;Discription：An parser which can convert a DSL(Domain Specific Language) to  Ren&amp;apos;py scripts, the DSL was designed by myself.&lt;br /&gt;
Size: Normal &lt;br /&gt;
Role：Work individually&lt;br /&gt;
Progress：Basically finished&lt;br /&gt;
Used skills:  &lt;br /&gt;
Python, Ren&amp;apos;py&lt;br /&gt;
Source: &lt;a href=&amp;quot;https://github.com/dtysky/Gal2Renpy&amp;quot;&gt;Gal2Renpy&lt;/a&gt; &lt;br /&gt;
Completed work：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Learning ren’py(a game engine) and python  &lt;/li&gt;
&lt;li&gt;Designing a DSL(Domain Specific Language), completed the source parsing and mapping with ren’py script, then migrating the User Interface, it has good expandability  &lt;/li&gt;
&lt;li&gt;A plug-in for sublime-text, improving the input efficiency  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;FPGA Imaging Library：&lt;/h3&gt;
&lt;p&gt;Discription：An indie game。&lt;br /&gt;
Size: Large &lt;br /&gt;
Role：Work individually&lt;br /&gt;
Progress：Building&lt;br /&gt;
Used skills:  &lt;br /&gt;
FPGA, Verilog, Systemverilog, Python, Image processing&lt;br /&gt;
Source: &lt;a href=&amp;quot;https://github.com/dtysky/FPGA-Imaging-Library&amp;quot;&gt;FPGA-Imaging-Library&lt;/a&gt;   , and will be published here: &lt;a href=&amp;quot;fil.dtysky.moe&amp;quot;&gt;fil.dtysky.moe&lt;/a&gt; &lt;br /&gt;
Completed work：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Graying, Threshold, Enhancement  &lt;/li&gt;
&lt;li&gt;Character output (Paused)  &lt;/li&gt;
&lt;li&gt;Frame controller, Rows generator, Window generator  &lt;/li&gt;
&lt;li&gt;Mean fitter, Rank fitter, Erosion, Dilation, Template matching, Auto threshold  &lt;/li&gt;
&lt;li&gt;Harris  &lt;/li&gt;
&lt;li&gt;Pan, Scale, Mirror, Crop, Roate, Shear, Affine transform  &lt;/li&gt;
&lt;li&gt;More...&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;My Homepage：&lt;/h3&gt;
&lt;p&gt;Discription：A blog for recording my learning experience&lt;br /&gt;
Size: Small &lt;br /&gt;
Role：Work individually&lt;br /&gt;
Progress：Done&lt;br /&gt;
Used skills:  &lt;br /&gt;
Writing, Python, Ren&amp;apos;py&lt;br /&gt;
Source: Closed source &lt;br /&gt;
Completed work：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Learning about pelican(Based on jinjia2), css, html, js, bulit on VPS。  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;A sample MIPS CPU：&lt;/h3&gt;
&lt;p&gt;Discription：A sample single cycle MIPS CPU, designed with verilogHDL, bulit on xilinx vivado, for XUP(Xilinx uinversity project) &lt;br /&gt;
Size: Small &lt;br /&gt;
Role：Work individually&lt;br /&gt;
Progress：Done&lt;br /&gt;
Used skills:  &lt;br /&gt;
FPGA, Verilog, Systemverilog, Python&lt;br /&gt;
Source: &lt;a href=&amp;quot;https://github.com/dtysky/SIMPLE_MIPS_CPU&amp;quot;&gt;SIMPLE_MIPS_CPU&lt;/a&gt; &lt;br /&gt;
Completed work： &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Designed and tested ALU, REGFILE, CONTRLO_UNIT, DATAPATH, INST_MEM, DATA_MEM modules, and connected them for a CPU which is used for functional simulation    &lt;/li&gt;
&lt;li&gt;Designed and tested KEY2INST, SHOW_ON_LED modules, and connected them for a CPU which is used for testing on board      &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h1&gt;Education&lt;/h1&gt;
&lt;p&gt;2011.08 - Now &lt;br /&gt;
Bachelor Southeast University(China), Instrument Science And Engineering College – Control Technology And Instruments  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Record&lt;/h2&gt;
&lt;p&gt;2014, HackShanghai No.3 (OpenTTT, Member)&lt;br /&gt;
2014, 36kr Nanjing hachathon No.4 (Stars, Leader) &lt;br /&gt;
2014.10 - Now, Xilinx R&amp;amp;D Intern&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;Hobby&lt;/h1&gt;
&lt;p&gt;Reading, Music, Writing。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Tuesday, 28 Apr 2015 10:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.04.28 10:00:article/Create-MyResumeEn</guid>
<category>Resume</category>
</item>

<item>
<title>体三维显示器-FPGA控制部分</title>
<link>http://dtysky.moe/article/Create-2015_03_22_c</link>
<description>&lt;p&gt;此部分代码在：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/3D_Displayer_Controller/tree/master/VHDL_PLANB&amp;quot;&gt;https://github.com/dtysky/3D_Displayer_Controller/tree/master/VHDL_PLANB&lt;/a&gt;
&lt;br&gt;
体三维显示器为真三维显示的一种，即可以在真是的空间内显示一个三维的图像，图像占据一个真实的三维空间，不需要借助任何辅助视觉器件，我采用的方案是二维LED点阵的旋转，LED点阵为自行设计，最后是FPGA的控制部分。&lt;br /&gt;
名为PLANB，顾名思义，这其实是一个妥协的方案，原本方案是USB（cy68013）+DDR2，本来也实现的差不多了，但一开始忽略的某个问题直接延续到了最后，嘛，usb那块的教程以前写过了:&lt;br /&gt;
&lt;a href=&amp;quot;http://dtysky.github.io/tag/68013.html&amp;quot;&gt;http://dtysky.github.io/tag/68013.html&lt;/a&gt;&lt;br /&gt;
至于DDR2控制器的编写教程和仿真，有时间再说吧。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1.LED阵列控制&lt;/h2&gt;
&lt;p&gt;LED阵列控制需要配合LED阵列的电路设计，也就是buffer到trigger的模型，同时还要考虑到由于电路板不完美造成的噪声等等。
&lt;br&gt;
&lt;strong&gt;1.扫描流程:&lt;/strong&gt;  &lt;br /&gt;
配合LED阵列电路的设计，刷新需要按照以下流程进行：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;扫描&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-03-22c/1.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;&lt;br&gt;
&lt;strong&gt;2.状态机：&lt;/strong&gt;&lt;br /&gt;
根据扫描流程和整体方案设计状态机,由于没有什么特别的需求，所以只设计以下几种状态：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;rst：复位&lt;br /&gt;
free：IDLE状态，初始化或者复位后的状态&lt;br /&gt;
work：工作状态  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;初始化时进行复位，复位结束跳转free状态，在free状态时如果接收到&lt;strong&gt;begin&lt;/strong&gt;信号则跳转到work状态，开始工作。  &lt;/p&gt;
&lt;p&gt;&lt;br&gt;
&lt;strong&gt;3.扫描注意事项：&lt;/strong&gt;&lt;br /&gt;
扫描时，由于电路设计不完美所以可能会出现串扰现象，也就是当某一行有效时可能会让其他行同时有效，行和列之间也可能会出现这样的现象，为了避免这样的情况发生，需要采取一些软件上的措施，我采用的方案是在每一次进行列变换前先对前一行进行消隐，由于行有效是在某个信号上升沿的时候送出（trigger的时钟信号），所以在正常显示时下保证此信号为高电平，在切换时或者消隐时在进行一下High-&amp;gt;Low-&amp;gt;High的变换，构造一个上升沿，经测验，该方案有着很大的效果，噪声大大减小。  &lt;/p&gt;
&lt;p&gt;LED控制部分的代码在这里：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/3D_Displayer_Controller/blob/master/VHDL_PLANB/LED/LED.vhd&amp;quot;&gt;https://github.com/dtysky/3D_Displayer_Controller/blob/master/VHDL_PLANB/LED/LED.vhd&lt;/a&gt;  &lt;/p&gt;
&lt;h2&gt;2.图像解码&lt;/h2&gt;
&lt;p&gt;图像解码，也就是对三维切片编码后的每一帧压缩图像进行解码，由上一篇文章：  &lt;br /&gt;
&lt;a href=&amp;quot;http://dtysky.github.io/articles/ti-san-wei-xian-shi-qi-matlabde-san-wei-qie-pian.html&amp;quot;&gt;体三维显示器-Matlab的三维切片&lt;/a&gt;&lt;br /&gt;
可知，每一帧图像存的实际上是发亮点的坐标值，这就需要一个解码单元来这样的数据进行解码，转换成可以LED可以显示的位图。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;1.解码流程：&lt;/strong&gt;
解码基本流程如下：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;解码&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-03-22c/2.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.状态机：&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;整体流程可以分为以下状态：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;read_next：读下一个坐标点，转入judge状态&lt;br /&gt;
judge：判断当前点坐标是否与上一个坐标的纵坐标相等，如果是转入set状态，否则转入do_write状态&lt;br /&gt;
do_write：将上一行写入fifo，同时跳转到clear状态&lt;br /&gt;
set：将当前行的x置1&lt;br /&gt;
clear：对当前行，当前x坐标前的所有点写0  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如此，便可以完成解码。&lt;br /&gt;
代码在这里：&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/3D_Displayer_Controller/blob/master/VHDL_PLANB/DECODER/DECODER.vhd&amp;quot;&gt;https://github.com/dtysky/3D_Displayer_Controller/blob/master/VHDL_PLANB/DECODER/DECODER.vhd&lt;/a&gt;  &lt;/p&gt;
&lt;h2&gt;3.整体&lt;/h2&gt;
&lt;p&gt;整体，即把解码单元和扫描单元连接起来，这里采用的是双缓存模式，也就是说：  &lt;/p&gt;
&lt;p&gt;实例化fifoA和fifoB两个fifo，当A填充完成后开始扫描，A在提供扫描数据时B填充数据，B扫面时A填充，这样可以保证总是有数据输出，这其实是一个宏观上的流水线：  &lt;/p&gt;
&lt;p&gt;&lt;img alt=&amp;quot;整体&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-03-22c/3.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;然后，整个控制部分就完成了。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 22 Mar 2015 19:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.03.22 19:00:article/Create-2015_03_22_c</guid>
<category>体三维</category>
<category>LED阵列</category>
<category>FPGA</category>
<category>VHDL</category>
</item>

<item>
<title>体三维显示器-机械部分</title>
<link>http://dtysky.moe/article/Create-2015_03_22_a</link>
<description>&lt;p&gt;&lt;strong&gt;体三维显示器的机械工程：&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/3D_Displayer_Machine&amp;quot;&gt;https://github.com/dtysky/3D_Displayer_Machine&lt;/a&gt;&lt;br /&gt;
&lt;br&gt;
体三维显示器为真三维显示的一种，即可以在真是的空间内显示一个三维的图像，图像占据一个真实的三维空间，不需要借助任何辅助视觉器件，我采用的方案是二维LED点阵的旋转，LED点阵为自行设计，这里记录了如何设计一个机械结构让LED阵列旋转起来。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1.设计&lt;/h2&gt;
&lt;p&gt;机械部分分为底座、联轴器、电刷以及PCB支架四个部分，同时还涉及电机的选型。
&lt;br&gt;
&lt;strong&gt;1.电机:&lt;/strong&gt;  &lt;br /&gt;
电机主要要考虑的是负载问题，本装置轴向负载大约为2KG，大约10cm的旋转半径，考虑到成本和够用就行的理念，最终选择了一个120W的直流电机作为驱动源。  &lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;2.底座：&lt;/strong&gt;&lt;br /&gt;
底座，也就是放置电机的机械结构，考虑到一般的钢板物理属性（5mm厚度)，如果将电机的底部触地，直接放在底座之上，肯定会导致不稳妥的情况出现，所以这里选择了使用拱形吊桥结构，也就是将电机吊在底座上面，以此稳底座的下盘。后来出于需求，又在底座下面加了一个大钢板，进一步稳定。  &lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;3.支架：&lt;/strong&gt;&lt;br /&gt;
支架的作用是稳固LED阵列，给其提供支撑，基本设计是一个L型的折弯，由于面积比较大，如果直接上的话会有相当的重量，导致负载过大，所以对其实行了挖空工艺，将主要版面的中间部分挖空来减轻重量。&lt;br /&gt;
支架的主要部分是为了配合LED的螺丝孔固定阵列，下面的部分则是为了和联轴器进行连接。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;4.联轴器：&lt;/strong&gt;&lt;br /&gt;
顾名思义，连接电机的轴和PCB支架，这个的设计主要考虑到电机轴的构造。联轴器分为两部分，上面是和支架连接，下面则是和电机的轴连接，和支架连接的部分只需要根据支架底部的孔钻孔即可，和轴连接的部分也需要打几个孔，不过是为了和轴做紧固连接，这个轴属于一侧削平靠摩擦力禁锢的那种，所以可以用逻辑拧紧进行固定。&lt;br /&gt;
联轴器上下两个部分的连接可以使用螺丝进行，没什么特别的。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;5.电刷：&lt;/strong&gt;&lt;br /&gt;
为了解决旋转供电问题，解决这个问题一般有电刷和无限供电两种方式，考虑到风险和电流问题，最终选择了电刷的方案。在联轴器上面放一个集电环，然后通过自己设计的电刷与集电环的连接来供电，集电环是铜质材料，电刷是石墨，实测效果良好。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2.生产&lt;/h2&gt;
&lt;p&gt;大学好的一点就是工培中心，以下皆在工培中心和学校工厂加工。&lt;br /&gt;
&lt;br&gt;
&lt;br&gt;
&lt;strong&gt;1.电机:&lt;/strong&gt;  &lt;br /&gt;
购买所得：  &lt;br /&gt;
&lt;img alt=&amp;quot;电机&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-03-22a/0.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;2.底座：&lt;/strong&gt;&lt;br /&gt;
注意各个部分的尺寸都必须到位！  &lt;br /&gt;
&lt;img alt=&amp;quot;底座-CAD&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-03-22a/11.png&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;底座-实物&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-03-22a/12.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;3.支架：&lt;/strong&gt;&lt;br /&gt;
挖空由于并不要求十分精确，所以采用了很一般的工艺，没有必要上线切割：  &lt;br /&gt;
&lt;img alt=&amp;quot;支架-CAD&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-03-22a/21.png&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;支架-实物&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-03-22a/22.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;4.联轴器：&lt;/strong&gt;&lt;br /&gt;
分成两个部分！CAD图纸和最后成品有区别：  &lt;br /&gt;
&lt;img alt=&amp;quot;联轴器-CAD&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-03-22a/31.png&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;联轴器-实物&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-03-22a/32.jpg&amp;quot; /&gt;&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;5.电刷：&lt;/strong&gt;&lt;br /&gt;
可调节式设计：&lt;br /&gt;
&lt;img alt=&amp;quot;电刷-CAD&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-03-22a/41.png&amp;quot; /&gt;&lt;br /&gt;
&lt;img alt=&amp;quot;电刷-实物&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-03-22a/42.jpg&amp;quot; /&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3.合体&lt;/h2&gt;
&lt;p&gt;机械部分总共花费约800，整体良好，整体如下：&lt;br /&gt;
&lt;img alt=&amp;quot;合体&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-03-22a/5.jpg&amp;quot; /&gt;&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 22 Mar 2015 14:10:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.03.22 14:10:article/Create-2015_03_22_a</guid>
<category>体三维</category>
<category>机械</category>
</item>

<item>
<title>体三维显示器-Matlab的三维切片</title>
<link>http://dtysky.moe/article/Create-2015_03_22_b</link>
<description>&lt;p&gt;&lt;br&gt;
体三维显示器为真三维显示的一种，即可以在真是的空间内显示一个三维的图像，图像占据一个真实的三维空间，不需要借助任何辅助视觉器件，我采用的方案是二维LED点阵的旋转，LED点阵为自行设计，这里记录了三维切片方面的东西。&lt;br /&gt;
老实说这只是一个无奈的方案，因为各种各样的原因吧，比如时间和优先级之类的，现在只能切规则几何体，并且要手动输入坐标对。&lt;br /&gt;
同时由于没有使用矩阵运算，所以切片会很慢。。。嘛，暂时就这样了，基本所有切片算法的思路都是这样的。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1.实现&lt;/h2&gt;
&lt;p&gt;分为定义端点、获取边界、构造边方程、构造切片平面并切片、平面坐标映射、坐标系变换、去归一化几个部分
&lt;br&gt;
&lt;strong&gt;1.端点定义:&lt;/strong&gt;  &lt;br /&gt;
这段代码定义了一个规则正方体的十二条边：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;zeros&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;12&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,:,:)=[[&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.6079&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.4078&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.4982&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];[&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.4690&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.0140&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.3235&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]];&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,:,:)=[[&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.4690&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.0140&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.3235&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];[&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.0920&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.2363&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.3235&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]];&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,:,:)=[[&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.0470&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.6582&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.4982&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];[&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.0920&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.2363&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.3235&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]];&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,:,:)=[[&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.0470&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.6582&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.4982&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];[&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.6079&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.4078&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.4982&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]];&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;5&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,:,:)=[[&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.6079&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.4078&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.4982&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];[&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0920&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.2363&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.3235&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]];&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;6&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,:,:)=[[&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.0470&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.6582&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.4982&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];[&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.4690&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.0140&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.3235&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]];&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;7&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,:,:)=[[&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.0920&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.2363&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.3235&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];[&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.6079&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.4078&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.4982&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]];&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;8&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,:,:)=[[&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.4690&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0140&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.3235&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];[&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.0470&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.6582&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;0.4982&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]];&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;9&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,:,:)=[[&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0920&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.2363&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.3235&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];[&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.0470&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.6582&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.4982&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]];&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;10&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,:,:)=[[&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.6079&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.4078&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.4982&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];[&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.0470&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.6582&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.4982&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]];&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;11&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,:,:)=[[&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.4690&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0140&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.3235&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];[&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.6079&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.4078&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.4982&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]];&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;12&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,:,:)=[[&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0920&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.2363&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.3235&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];[&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;0.4690&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mf&amp;quot;&gt;1.0140&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt; &lt;span class=&amp;quot;mf&amp;quot;&gt;1.3235&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]];&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;br&gt;
&lt;strong&gt;2.获取边界：&lt;/strong&gt;&lt;br /&gt;
确定整个立方体的边界范围，以此作为方程解的定义域：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tmpx&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(:,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;tmpy&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(:,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;tmpz&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(:,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;tmpx2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(:,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;tmpy2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(:,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;tmpz2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(:,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;xmax&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;([&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tmpx&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tmpx2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]);&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;xmin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;min&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;([&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;min&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tmpx&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;min&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tmpx2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]);&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;ymax&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;([&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tmpx&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tmpx2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]);&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;ymin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;min&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;([&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;min&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tmpy&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;min&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tmpy2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]);&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;zmax&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;([&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tmpz&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tmpz2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]);&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;zmin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;min&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;([&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;min&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tmpz&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;min&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tmpz2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)]);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;br&gt;
&lt;strong&gt;3.构造边的方程组：&lt;/strong&gt;&lt;br /&gt;
由于任意一条空间直线的方程都可以表现为：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;a11*X + a12*y + a13*0 = a14&lt;br /&gt;
a21*x + a22*0 + a23*z = a24   &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这样的形式的方程组，所以对于每一个边，需要算6个系数：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;zeros&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;length&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(:,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)),&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;length&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(:,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;x1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;x2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;y1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;y2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;z1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;z2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;p&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;c&amp;quot;&gt;%x,y&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)=&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)=&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)=(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;c&amp;quot;&gt;%y,z&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)=&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)=&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)=(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y1&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;br&gt;
&lt;strong&gt;4.构造切面并切片：&lt;/strong&gt;&lt;br /&gt;
由于本设备是按绕中轴旋转，所以构造的切面应当符合“每旋转i度”构造一次的规则，本次设置为没旋转一度切一次，总共360切片，由于旋转过程中出现的对称现象，所以实际上只需要切一半（180度）即可：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=[];&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;ii&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;180&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=[&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;tan&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;deg2rad&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)),&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;j&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;length&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(:,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;))&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;f1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;strcat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;num2str&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;j&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)),&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;#39;*x+&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num2str&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;j&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)),&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;#39;*y+&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num2str&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;j&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)),&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;#39;*z+&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num2str&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;j&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)));&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;f2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;strcat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;num2str&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;j&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)),&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;#39;*x+&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num2str&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;j&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)),&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;#39;*y+&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num2str&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;j&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)),&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;#39;*z+&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num2str&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;j&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)));&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;f3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;strcat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;num2str&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;j&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)),&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;#39;*x+&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num2str&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;j&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)),&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;#39;*y+&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num2str&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;j&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)),&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;#39;*z+&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num2str&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;a&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;j&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)));&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;f4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;strcat&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;num2str&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)),&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;#39;*x+&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num2str&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)),&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;#39;*y+&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num2str&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)),&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;#39;*z+&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;num2str&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;b&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)));&lt;/span&gt;
        &lt;span class=&amp;quot;p&amp;quot;&gt;[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;]=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;solve&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;f1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;f2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;f4&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;#39;x&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;#39;y&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;s&amp;quot;&gt;&amp;#39;z&amp;#39;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;~&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;isempty&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;~&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;isempty&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;~&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;isempty&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
            &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xmin&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;xmax&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ymin&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ymax&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;zmin&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;zmax&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
                &lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ii&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,:)=[&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;
                &lt;span class=&amp;quot;n&amp;quot;&gt;ii&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ii&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
            &lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt; 
    &lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ii&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,:)=[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;ii&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ii&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;br&gt;
&lt;strong&gt;5.平面坐标映射：&lt;/strong&gt;&lt;br /&gt;
这个比较容易理解，由于切得的坐标是空间坐标，而在LED显示的是平面坐标，所以要进行一下坐标映射，基本思路就是保留空间矢量的Z分量，将其作为新平面矢量的Y分量，而后取空间点相对于中轴的距离作为新的X分量即可：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tymax&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;max&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;abs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;zmax&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;),&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;abs&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;zmin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;));&lt;/span&gt;
&lt;span class=&amp;quot;n&amp;quot;&gt;res2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=[];&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;length&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;res2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,:)=[&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt; &lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;];&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;res&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;3&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;tx&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;sqrt&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;x&lt;/span&gt;^&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;^&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;y&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;
            &lt;span class=&amp;quot;n&amp;quot;&gt;res2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)=&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tx&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt;
            &lt;span class=&amp;quot;n&amp;quot;&gt;res2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;tx&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;ty&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;z&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
        &lt;span class=&amp;quot;n&amp;quot;&gt;res2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;zmax&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;ty&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;注意，这里同时进行了Y轴坐标系的变换。&lt;br /&gt;
之后进行对称操作，便可以得到另一半的切片：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;len&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;length&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;res2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;length&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;res2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;res2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;len&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)=&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;-&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;res2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;res2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;len&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;res2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;br&gt;
&lt;strong&gt;6.坐标系变换和去归一化：&lt;/strong&gt;&lt;br /&gt;
这个没什么好说的，根据自己实际的情况选取参数，对切片结果进行X向的坐标系变换和方大取整：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;resfin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;=[];&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;for&lt;/span&gt; &lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt; &lt;span class=&amp;quot;p&amp;quot;&gt;=&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;:&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;length&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;res2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;
    &lt;span class=&amp;quot;n&amp;quot;&gt;resfin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,:)=&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;round&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;res2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,:)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;*&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;36&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;);&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;if&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;resfin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt; &lt;span class=&amp;quot;o&amp;quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&amp;quot;n&amp;quot;&gt;resfin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;2&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;==&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;0&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;else&lt;/span&gt;
       &lt;span class=&amp;quot;n&amp;quot;&gt;resfin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)=&lt;/span&gt;&lt;span class=&amp;quot;n&amp;quot;&gt;resfin&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;(&lt;/span&gt;&lt;span class=&amp;quot;nb&amp;quot;&gt;i&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;,&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;1&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;)&lt;/span&gt;&lt;span class=&amp;quot;o&amp;quot;&gt;+&lt;/span&gt;&lt;span class=&amp;quot;mi&amp;quot;&gt;59&lt;/span&gt;&lt;span class=&amp;quot;p&amp;quot;&gt;;&lt;/span&gt;
    &lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt;
&lt;span class=&amp;quot;k&amp;quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;hr /&gt;
&lt;h2&gt;2.结果&lt;/h2&gt;
&lt;p&gt;以下是上面例子的输出，由于上面所言的边界问题有些瑕疵：&lt;br /&gt;
&lt;img alt=&amp;quot;图解&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-03-22b/1.png&amp;quot; /&gt;  &lt;/p&gt;
&lt;p&gt;完整代码看这里：  &lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://github.com/dtysky/3D_Displayer_Controller/blob/master/VHDL_PLANB/DECODER/CreatMif/test.m&amp;quot;&gt;https://github.com/dtysky/3D_Displayer_Controller/blob/master/VHDL_PLANB/DECODER/CreatMif/test.m&lt;/a&gt;&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Sunday, 22 Mar 2015 13:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.03.22 13:00:article/Create-2015_03_22_b</guid>
<category>体三维</category>
<category>切片</category>
<category>Matlab</category>
</item>

<item>
<title>游戏人物文字设定</title>
<link>http://dtysky.moe/article/Create-2015_02_21_a</link>
<description>&lt;p&gt;这里记录了原创游戏人物的原始设定，方便日后交流，由于是人设，所以暂且不包括表情和姿势（和画师详谈），服装统一为第一服装（大多是校服）。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;我方主要角色&lt;/h1&gt;
&lt;h2&gt;戴寒苍/戴天宇&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
虚无/耗散&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：18岁（现实）&lt;br /&gt;
性格：吐槽别扭受&lt;br /&gt;
服装：校服&lt;br /&gt;
眼色：黑&lt;br /&gt;
发色：银带黑&lt;br /&gt;
发型：待定&lt;br /&gt;
身高：177cm&lt;br /&gt;
肤色：正常色&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式1：&lt;/strong&gt;&lt;br /&gt;
年龄：10岁（“梦”）&lt;br /&gt;
性格：沉着&lt;br /&gt;
服装：特殊—小（类似西服）&lt;br /&gt;
眼色：黑&lt;br /&gt;
发色：银带黑&lt;br /&gt;
发型：待定&lt;br /&gt;
身高：150cm&lt;br /&gt;
肤色：正常色&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Meteor/命月晗樱&lt;/h2&gt;
&lt;p&gt;&lt;img src=&amp;quot;/theme/image/fw/char-soul.jpg&amp;quot; style=&amp;quot;position:absolute;right:0px;z-index:-1;&amp;quot;&gt;&lt;/img&gt;
&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
谎言/真实&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：18岁（现实）&lt;br /&gt;
性格：冰山毒舌&lt;br /&gt;
服装：校服&lt;br /&gt;
眼色：深红&lt;br /&gt;
发色：银&lt;br /&gt;
发型：双马尾&lt;br /&gt;
身高：174cm&lt;br /&gt;
肤色：纯白&lt;br /&gt;
胸围：B-&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式1：&lt;/strong&gt;&lt;br /&gt;
年龄：10岁（“梦”）&lt;br /&gt;
性格：元气温柔&lt;br /&gt;
服装：特殊—小（白色连衣裙）&lt;br /&gt;
眼色：淡红&lt;br /&gt;
发色：银&lt;br /&gt;
发型：双马尾&lt;br /&gt;
身高：140cm&lt;br /&gt;
肤色：纯白&lt;br /&gt;
胸围：A--&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;苓苏.蒲兰特&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
泉&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：17岁（现实）&lt;br /&gt;
性格：天然（前）小恶魔（后）&lt;br /&gt;
服装：校服&lt;br /&gt;
眼色：淡绿&lt;br /&gt;
发色：浅绿&lt;br /&gt;
发型：单侧马尾&lt;br /&gt;
身高：168cm&lt;br /&gt;
肤色：白偏微黄&lt;br /&gt;
胸围：A&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式1：&lt;/strong&gt;&lt;br /&gt;
年龄：17岁（梦界）&lt;br /&gt;
性格：腹黑小恶魔&lt;br /&gt;
服装：哥特风&lt;br /&gt;
眼色：深绿&lt;br /&gt;
发色：深绿&lt;br /&gt;
发型：单侧马尾&lt;br /&gt;
身高：168cm&lt;br /&gt;
肤色：纯白&lt;br /&gt;
胸围：A&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;月雏末&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
幻想&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：18岁（现实）&lt;br /&gt;
性格：软妹弱气（前）正常（后）&lt;br /&gt;
服装：校服&lt;br /&gt;
眼色：浅粉&lt;br /&gt;
发色：粉&lt;br /&gt;
发型：散短发&lt;br /&gt;
身高：166cm&lt;br /&gt;
肤色：白偏微黄&lt;br /&gt;
胸围：F（外观B）&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式1：&lt;/strong&gt;&lt;br /&gt;
年龄：18岁（梦界）&lt;br /&gt;
性格：冷漠强气&lt;br /&gt;
服装：童话骑士风&lt;br /&gt;
眼色：橙&lt;br /&gt;
发色：橙黄&lt;br /&gt;
发型：散短发&lt;br /&gt;
身高：163cm&lt;br /&gt;
肤色：白偏微黄&lt;br /&gt;
胸围：B&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;玖暮&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
理&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：18岁（现实）&lt;br /&gt;
性格：强气傲娇（前后）&lt;br /&gt;
服装：校服&lt;br /&gt;
眼色：淡紫&lt;br /&gt;
发色：紫&lt;br /&gt;
发型：单马尾&lt;br /&gt;
身高：178cm&lt;br /&gt;
肤色：白偏中黄&lt;br /&gt;
胸围：C&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式1：&lt;/strong&gt;&lt;br /&gt;
年龄：18岁（梦界）&lt;br /&gt;
性格：弱气/病娇&lt;br /&gt;
服装：魔女风&lt;br /&gt;
眼色：深紫&lt;br /&gt;
发色：火红&lt;br /&gt;
发型：单马尾&lt;br /&gt;
身高：179cm&lt;br /&gt;
肤色：苍白&lt;br /&gt;
胸围：C&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;恒晓&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
道&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：18岁（现实）&lt;br /&gt;
性格：伪装笨蛋（前）豪爽透彻（后）&lt;br /&gt;
服装：校服&lt;br /&gt;
眼色：黑&lt;br /&gt;
发色：苍蓝&lt;br /&gt;
发型：越帅越好&lt;br /&gt;
身高：181cm&lt;br /&gt;
肤色：一般&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式1：&lt;/strong&gt;&lt;br /&gt;
年龄：18岁（梦界）&lt;br /&gt;
性格：阴暗/冷静&lt;br /&gt;
服装：中国古代神话风&lt;br /&gt;
眼色：深蓝（戴眼镜）&lt;br /&gt;
发色：苍白&lt;br /&gt;
发型：越帅越好&lt;br /&gt;
身高：181cm&lt;br /&gt;
肤色：苍白&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;弗莱士.待暮.斯代达斯特&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
星空（未知）&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：18岁（现实）&lt;br /&gt;
性格：沉着温柔自我厌恶伪装（前）冷静温柔（后）&lt;br /&gt;
服装：校服&lt;br /&gt;
眼色：淡黄&lt;br /&gt;
发色：金&lt;br /&gt;
发型：贵族式正统&lt;br /&gt;
身高：178cm&lt;br /&gt;
肤色：白&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式1：&lt;/strong&gt;&lt;br /&gt;
年龄：18岁（梦界）&lt;br /&gt;
性格：疯狂/冷静&lt;br /&gt;
服装：正装&lt;br /&gt;
眼色：深黄&lt;br /&gt;
发色：金&lt;br /&gt;
发型：贵族式正统&lt;br /&gt;
身高：178cm&lt;br /&gt;
肤色：白&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;无名&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
空&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：22岁（立绘破损一）&lt;br /&gt;
性格：平凡&lt;br /&gt;
服装：无&lt;br /&gt;
完全纯色&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式1：&lt;/strong&gt;&lt;br /&gt;
年龄：22岁（立绘破损二）&lt;br /&gt;
性格：平凡&lt;br /&gt;
服装：&lt;br /&gt;
破损程度1&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式2：&lt;/strong&gt;&lt;br /&gt;
年龄：22岁（立绘破损三）&lt;br /&gt;
性格：神秘&lt;br /&gt;
服装：&lt;br /&gt;
破损程度2&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式3：&lt;/strong&gt;&lt;br /&gt;
年龄：22岁（完整）&lt;br /&gt;
性格：冷静&lt;br /&gt;
服装：校服&lt;br /&gt;
眼色：蓝&lt;br /&gt;
发色：白&lt;br /&gt;
发型：帅&lt;br /&gt;
身高：180cm&lt;br /&gt;
肤色：白&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;散夜&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
实感&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：黑影&lt;br /&gt;
性格：神秘&lt;br /&gt;
服装：无&lt;br /&gt;
无&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式1：&lt;/strong&gt;&lt;br /&gt;
年龄：外观12岁（灵魂）&lt;br /&gt;
性格：情感渴求（前）温柔（后）&lt;br /&gt;
服装：黑色连衣裙&lt;br /&gt;
眼色：黑带紫&lt;br /&gt;
发色：黑&lt;br /&gt;
发型：黑长直&lt;br /&gt;
身高：145cm&lt;br /&gt;
肤色：苍白&lt;br /&gt;
胸围：B&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;其他主要角色&lt;/h1&gt;
&lt;h2&gt;霍普.蒲兰特&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
执念&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：40岁&lt;br /&gt;
性格：完全理智&lt;br /&gt;
服装：正统西服&lt;br /&gt;
眼色：蓝&lt;br /&gt;
发色：白金&lt;br /&gt;
发型：待定&lt;br /&gt;
身高：180cm&lt;br /&gt;
肤色：白&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;莱特.休斯.斯代达斯特&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
奉献&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：外观9岁（灵魂）&lt;br /&gt;
性格：沉着温柔&lt;br /&gt;
服装：休闲西服&lt;br /&gt;
眼色：淡黄&lt;br /&gt;
发色：淡黄&lt;br /&gt;
发型：正统偏帅&lt;br /&gt;
身高：145cm&lt;br /&gt;
肤色：白&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;应龙&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
爱&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：外观18岁（异世界神）&lt;br /&gt;
性格：浩然之气&lt;br /&gt;
服装：神话服装&lt;br /&gt;
眼色：深蓝&lt;br /&gt;
发色：苍白&lt;br /&gt;
发型：越帅越好&lt;br /&gt;
身高：181cm&lt;br /&gt;
肤色：苍白&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;斯诺&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
荣耀&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：40岁&lt;br /&gt;
性格：死板强压&lt;br /&gt;
服装：正统西服&lt;br /&gt;
眼色：深黄&lt;br /&gt;
发色：金&lt;br /&gt;
发型：完全正统&lt;br /&gt;
身高：180cm&lt;br /&gt;
肤色：白&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Ghost&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
复仇&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：外观20岁（复仇集合体）&lt;br /&gt;
性格：仇恨戏谑&lt;br /&gt;
服装：待定&lt;br /&gt;
眼色：绿&lt;br /&gt;
发色：无（小丑面具）&lt;br /&gt;
发型：待定&lt;br /&gt;
身高：175cm&lt;br /&gt;
肤色：中黄&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;戴麟&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
彼方&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：外观30岁&lt;br /&gt;
性格：超越善恶&lt;br /&gt;
服装：休闲&lt;br /&gt;
眼色：黑&lt;br /&gt;
发色：黑&lt;br /&gt;
发型：规矩偏帅&lt;br /&gt;
身高：178cm&lt;br /&gt;
肤色：正常色&lt;br /&gt;
性感胡渣&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;“零”&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
正义&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：？？？岁&lt;br /&gt;
性格：全知，全不知&lt;br /&gt;
服装：无&lt;br /&gt;
无立绘&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;其他角色&lt;/h1&gt;
&lt;h2&gt;花夜月&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
平凡&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：18岁&lt;br /&gt;
性格：略害羞，开朗&lt;br /&gt;
服装：校服&lt;br /&gt;
自由发挥&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;菲克&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
善意之伪&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：外观40岁&lt;br /&gt;
性格：好人&lt;br /&gt;
服装：家居服&lt;br /&gt;
和善中年大叔&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;中年男人&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特性：&lt;/strong&gt;&lt;br /&gt;
救赎&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;形式0：&lt;/strong&gt;&lt;br /&gt;
年龄：40岁&lt;br /&gt;
性格：压抑&lt;br /&gt;
服装：西服&lt;br /&gt;
中年男人&lt;br /&gt;
&lt;br&gt;&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Saturday, 21 Feb 2015 14:20:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.02.21 14:20:article/Create-2015_02_21_a</guid>
<category>原创游戏</category>
<category>Galgame</category>
<category>人设</category>
</item>

<item>
<title>体三维显示器-PCB部分-LED控制板</title>
<link>http://dtysky.moe/article/Create-2015_02_19_a</link>
<description>&lt;p&gt;&lt;strong&gt;体三维显示器的PCB工程：&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/Led_Array&amp;quot;&gt;https://github.com/dtysky/Led_Array&lt;/a&gt;&lt;br /&gt;
&lt;br&gt;
体三维显示器为真三维显示的一种，即可以在真是的空间内显示一个三维的图像，图像占据一个真实的三维空间，不需要借助任何辅助视觉器件，我采用的方案是二维LED点阵的旋转，LED点阵为自行设计，这里记录了控制板的设计心得。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1.指标&lt;/h2&gt;
&lt;p&gt;既然LED阵列已经设计完毕，那么其控制电路也就可以开始考虑了。 
这个控制电路应当是怎样的一种形式？但LED打样成功后我思索着这个问题，思考的约束主要来自于如下几个部分： &lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;1.IO数量:&lt;/strong&gt;  &lt;br /&gt;
LED阵列被分成了三块（为了最低的亮度考虑），每块分辨率为120x38，这也就是说按照逐行扫描的方案我需要(120+38)x3个IO去控制整个电路，一开始是想直接用FPGA芯片的IO去搞，甚至花了相当的时间去考虑如何设计一个FPGA核心板（太年轻了），当经历了漫长的选型、设计之后，甚至连原理图都快要完成的并且芯片已经买到手的时候，我发现了一个事实——这是不可能的任务。一个如此多IO脚的FPGA核心板至少十层以上，并且对于我这种菜鸟一次成功率基本为0，而我的经费和时间是有限的。&lt;br /&gt;
经过了基友和老师的劝告，我放弃了执念，选用了现成的开发板——黑金某个板子，IO口二百多个，这也让我不得不去考虑别的方案。&lt;br /&gt;
这个方案是大家所熟悉的，一百LED点阵都会用这种方式——Latch。&lt;br /&gt;
当然，出于某些原因我并没有用Latch，而是用了Trigger。&lt;br /&gt;
利用Trigger锁存数据来达到多路复用的目的，第一个周期将行送出锁存、第二个周期送出列这种形式，当然实际控制会复杂一些（消隐之类的）。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;2.负载：&lt;/strong&gt;&lt;br /&gt;
我选用了Trigger复用IO口的方案，这样一个IO就要对多个Trigger进行驱动，这是不被允许的，因为一般而言这种主控芯片可以负载的电容都是有限的，所以我不得不去考虑其他的方案。 
这时候，就要Buffer出场了，Buffer即为缓冲器，一个Buffer带载Latch的能力一般在4-6个，完全满足我的要求。&lt;br /&gt;
要注意的是，Buffer仅仅起到提高负载的作用，不对逻辑产生任何影响。 &lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;3.特殊引脚：&lt;/strong&gt;&lt;br /&gt;
特殊引脚就是Trigger的时钟引脚，相对于一般的数据引脚，时钟引脚的错误带来的后果更为严重，所谓牵一发而动全身，所以我在布线的时候为其做了特别的保护，加大线宽和线距以及让附近的走线尽量少。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2.设计&lt;/h2&gt;
&lt;p&gt;指标了解之后便可以开始电路设计。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;1.选型：&lt;/strong&gt;&lt;br /&gt;
Buffer选用TI的&lt;strong&gt;SN74alvch162827&lt;/strong&gt;，Trigger选用TI的&lt;strong&gt;SN74alvch16721&lt;/strong&gt;，保证质量。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;2.电源：&lt;/strong&gt;&lt;br /&gt;
除了LED电流之外还要解决这些芯片的供电问题，和LED阵列一样，选择了一个TI的电源模块，按照建议进行了布局。  &lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;3.电路：&lt;/strong&gt;&lt;br /&gt;
电路实际设计的过程比较痛苦，因为走线很多约束不少而面积被LED阵列限制所以双层板肯定不够，最终敲定了四层板。  &lt;br /&gt;
&lt;br&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3.生产&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1.布局布线：&lt;/strong&gt;&lt;br /&gt;
很痛苦，由于布线很紧张所以没有办法自动化，只有手慢慢拉，而且国内的打样厂一般不给打盲孔，所以无法让每一层物尽其用，但还是勉强布完了，如下：  &lt;br /&gt;
&lt;img alt=&amp;quot;layout&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-02-19a/1.png&amp;quot; /&gt;&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;2.打样：&lt;/strong&gt;&lt;br /&gt;
难度比较高，被第一家打样厂拒了之后第二家打样的，花了1K5，比LED阵列还贵。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;3.焊接：&lt;/strong&gt;&lt;br /&gt;
焊接也是蛋疼，找的师傅焊接，花了500。    &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4.总结&lt;/h2&gt;
&lt;p&gt;板子总共花费约2K5，整体良好。&lt;br /&gt;
说实话，作为一个菜鸟的第一块四层板，我感到十分庆幸。&lt;br /&gt;
板子如下：&lt;br /&gt;
&lt;img alt=&amp;quot;pab&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-02-19a/2.jpg&amp;quot; /&gt;&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Thursday, 19 Feb 2015 19:20:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.02.19 19:20:article/Create-2015_02_19_a</guid>
<category>体三维</category>
<category>PCB</category>
<category>LED阵列</category>
<category>Buffer</category>
<category>Trigger</category>
</item>

<item>
<title>体三维显示器-PCB部分-LED阵列</title>
<link>http://dtysky.moe/article/Create-2015_02_18_a</link>
<description>&lt;p&gt;&lt;strong&gt;体三维显示器的PCB工程：&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&amp;quot;https://github.com/dtysky/Led_Array&amp;quot;&gt;https://github.com/dtysky/Led_Array&lt;/a&gt;&lt;br /&gt;
&lt;br&gt;
体三维显示器为真三维显示的一种，即可以在真是的空间内显示一个三维的图像，图像占据一个真实的三维空间，不需要借助任何辅助视觉器件，我采用的方案是二维LED点阵的旋转，LED点阵为自行设计，这里记录了LED阵列的设计心得。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1.指标&lt;/h2&gt;
&lt;p&gt;既然要设计一个LED点阵，要考虑的无非就是颜色、大小、密度、亮度，以及无法避免的成本问题了。
对于这些指标，当然是越高越好，比如全彩、1080p、400ppi、高亮啥的，一开始我也是这么想的，但越往后设计越感受到自己的天真。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;1.密度:&lt;/strong&gt;    &lt;br /&gt;
对于密度而言，通常的led封装到0402已经是封底了，而0402LED的大小能达到的最高密度也就是1/(0.4x0.2)=12.5ppi，而且这个理论密度是根本不可能达到，因为我们必须要为焊盘与布线预留间距（国内PCB打样厂的线宽线距一般要求不低于0.2mm），so...吹了&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;2.大小：&lt;/strong&gt;&lt;br /&gt;
PCB的大小直接受到密度和成本的限制，对于PCB而言成本主要收到层数、大小和孔数的限制，再加上板子最后是要被电机带动旋转的，所以太大将会有风阻和力矩等问题。当然，最后选择的120x114（要知道这个分辨率的LED数量是1w+）实际上还是大了（以我当时的水准驾驭程度和成本的综合考虑而言是相当冒险的）。那么为什么我还要选择这么大的分辨率呢？当然是为了爱，因为当时实验所得这个分辨率下刚好可以显示一个萌妹子的轮廓www。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;3.色彩：&lt;/strong&gt;&lt;br /&gt;
颜色方面也是十分尴尬，能实现全彩自然是好的，但对于一个体三维显示器，彩色要考虑的事情非常多。&lt;br /&gt;
首先需要明白，RGB的LED的色彩是通过不同色彩通道的光混合而成，如果仅仅依靠“亮”与“不亮”来进行调色，三个通道最多出现8种颜色的光，更多的色彩是通过PWM来实现的，即调节一个大周期内每个通道的光的占空比，也就是调节每种颜色的光在一个发光周期中的能量（平均功率），这一点直接回影响控制器件的选择，因为如果想要实现全彩就必须在一个发光周期内至少进行256次PWM输出，加上LED扫描（后面会提到）的所占用的周期，这个要求的速度是比较高的，就算先不考虑控制部分，这一点也直接影响了LED的亮度，不停的变换会让LED的有效亮度减少，由于环境光的影响，LED的亮度将会随着扫描次数（包括色彩调节）大幅下降，由于分辨率已经定的比较大，导致扫描已经占用了不少周期，如果在这种情况下再加上色彩调节，不但会使亮度可能下降到一个不可见的等级，就算可见，色彩在此等亮度下可分辨的程度也是极低的，毫无意义。  &lt;br /&gt;
当然我们说如果换一种扫描方式，加上优秀的供电（LED的接受电流极限一般是几ma到几十ma）和牛X的LED（最高亮度可达几千LM），全彩也不是不可能，但这时候就会遇到其他的问题了——钱，和PCB布局的难度。&lt;br /&gt;
对于一个全彩LED显示电路，6层的PCB是至少的（想要完全解决EMIEMC问题），而EMIEMC问题的解决是需要大量经验和理论的，本人无能，暂时无法解决。同时对于PCB工艺的要求也比较高，再者全彩SMD LED的价格比较高，PCB的价格又是随着层数成几何级上升的，对于一个新手而言失败概率极高，所以对于当时作为一个菜鸟的我，还是妥协了。&lt;br /&gt;
综合考虑，我选择了0603单色翠绿的LED，灰度暂时没有考虑。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;4.亮度：&lt;/strong&gt;&lt;br /&gt;
亮度虽然在上面说过了，不过还是有其他的部分没有提到。&lt;br /&gt;
亮度不仅仅要考虑LED本身，还要考虑供电部分，一开始是准备直接用控制芯片的IO脚来驱动LED，但很快就被否决了。由于显示条件的限制，我并不可能给每一个LED都加上单独的驱动源，所以肯定是以行扫描或者列扫描的方式来驱动，这也就是说每一行LED共阳，每一列LED共阴。&lt;br /&gt;
在这种情况下，驱动电流明显不够不说，每个引脚都以最大功率输出会使得芯片整体负载过高。&lt;br /&gt;
所以我只有进行迂回战术，选用中转的电源来驱动LED电路，这将会在下面说明。
&lt;br&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2.设计&lt;/h2&gt;
&lt;p&gt;指标了解之后便可以开始电路设计。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;1.LED：&lt;/strong&gt;&lt;br /&gt;
LED是显示的核心，目标是亮度高、功耗低、便宜，最终我选择了国内某厂的LED，其最大电流是20mA，亮度也算可以。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;2.驱动源：&lt;/strong&gt;&lt;br /&gt;
由于不能够用控制芯片的IO脚驱动，但仍然要用其进行控制，所以我们需要考虑用间接的方法实现IO脚对LED的控制，这样我们就会自然地想到三极管，用PMOS作高端驱动控制阳极，NMOS做低端驱动控制阴极，MOS管的栅极链接到IO上，便可以实现IO对LED的间接控制，这样也解决了LED供电不足的问题。&lt;br /&gt;
当然,MOS管的选取需要考虑到LED的驱动电压和IO的输出电压，否则阈值范围不匹配就完蛋了www&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;3.电源：&lt;/strong&gt;&lt;br /&gt;
电源部分的要求是稳定和足够的功率，在本例中可以预见到的最大瞬时电流输出为3.3V下的120x20=2.4A,我选择的是TI的某个电源模块，最大输出电流5A，宽电压输出，使用简单，十分满足需求。&lt;br /&gt;
这里提一下良心的TI，一百来块的东西就这么随便申请成功还包顺丰www&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;4.电路：&lt;/strong&gt;&lt;br /&gt;
综合下来，LED、电源、MOS管全部选型完毕，接下来就是实际电路的设计了。&lt;br /&gt;
实际电路设计要考虑到的东西不少，但最主要的还是以下几点：  &lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;器件间距： 每个器件的焊盘肯定是不能够直接在一起的，而且厂家也有最低间距限制，所以必须确定好器件间距。&lt;/li&gt;
&lt;li&gt;线宽线距： 和上面出于相似的考虑，而且走线相比器件而言，更多地不是工艺而是干扰等问题。&lt;/li&gt;
&lt;li&gt;电源网络线宽： 这个单独拿出来说是因为电源网络比较特殊，一般的走线不需要考虑电流大小的问题，而电源网络由于电流很大不得不考虑，网上有专门的计算器，可以用来估算一下，给电源网络一个比较宽的线宽。那么如果线宽受到板子的限制无法加宽怎么办？方法还是有的，也是我所采用的保险措施——将电源网络的铜皮裸漏出来，放在丝印层之上，这样可以手动给它加锡，从厚度上解决问题。&lt;/li&gt;
&lt;li&gt;孔径：过孔的直径，主要约束因素也是电流大小，同样可以通过计算器来计算，另外厂商也有一定的限制，比如内孔0.2mm，外孔0.3mm。  &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;设计结束后便可以开始绘制电路板了。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3.生产&lt;/h2&gt;
&lt;p&gt;生产的第一步是电路板绘制，首先是原理图的设计，这点不在赘述，教程可以看本博客的&lt;a href=&amp;quot;http://dtysky.github.io/tag/pcb.html&amp;quot;&gt;PCB系列帖子&lt;/a&gt;，需要注意的是以下几点：&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;1.布局布线：&lt;/strong&gt;&lt;br /&gt;
LED的数量达到五位数的级别，手动布局是完全不可取的做法，面对此等难题只能够借助于自动化的工具，好在Allegro支持脚本，所以我选择了用Python生成了&lt;strong&gt;.scr&lt;/strong&gt;脚本，之后再运行脚本即可，脚本的内容如下：  &lt;/p&gt;
&lt;div class=&amp;quot;highlight&amp;quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;add  connect;
pick  201.3  424.3;
pick  201.3  423.7;
done;
add  connect;
pick  203.3  424.3;
pick  203.3  423.7;
done;
add  connect;
pick  205.3  424.3;
pick  205.3  423.7;
done;
add  connect;
pick  207.3  424.3;
pick  207.3  423.7;
......
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这样一来，五位数的约束性“半自动布局”便很快完成了（其实也用了一个来小时），接下来可以用同样的方式去布线，整体的板子很快就可以完成:&lt;br /&gt;
&lt;img alt=&amp;quot;layout&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-02-18a/1.png&amp;quot; /&gt;&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;2.模块化：&lt;/strong&gt;&lt;br /&gt;
分割越细查错越容易，这是不变的真理，面对如此庞大的一个设计，模块化是必须的。&lt;br /&gt;
我将LED阵列作为了一个模块，将所有控制引脚全部引出到排针，降低风险。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;3.个性化：&lt;/strong&gt;&lt;br /&gt;
好不容易做出了个大东西，而且是个人的作品，你是否想要加点个性标志呢？&lt;br /&gt;
对于我而言当然是想的，而且是非常想，所以我花了一些功夫研究如何在PCB上印图案，也获得了成功，具体的方法将会在PCB教程系列写到。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;4.打样：&lt;/strong&gt;&lt;br /&gt;
联系打样厂家，注册账号，上传完整的设计文件（包括各层的Gerber文件、钻孔文件），算清费用，之后等待几天便拿到了产品。我这个板子的打样费总共1K+&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;5.贴片：&lt;/strong&gt;&lt;br /&gt;
这么多的SMD芯片不可能自己焊，我找了一家贴片厂去进行焊接。&lt;br /&gt;
由于只是个人的一个板子，一般的厂家是不给焊的，最后好不容易连蒙带骗游说了一家，为我进行了焊接。&lt;br /&gt;
当然最后板子来回几次后还是有问题，我也懒得继续纠缠了，再继续下去说不定有更多其他的岔子，所以我保守了一下。&lt;br /&gt;
板子贴片费2K6。  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4.总结&lt;/h2&gt;
&lt;p&gt;板子总共花费约5K，整体良好，有些许行列的MOS管缺焊。&lt;br /&gt;
说实话，作为一个菜鸟的第一块板子，我感到十分庆幸。&lt;br /&gt;
板子如下：&lt;br /&gt;
&lt;img alt=&amp;quot;pcb&amp;quot; src=&amp;quot;//src.dtysky.moe/image/blog/create-2015-02-18a/2.png&amp;quot; /&gt;&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Wednesday, 18 Feb 2015 17:20:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.02.18 17:20:article/Create-2015_02_18_a</guid>
<category>体三维</category>
<category>PCB</category>
<category>LED阵列</category>
</item>

<item>
<title>博客重构</title>
<link>http://dtysky.moe/article/Create-Summary</link>
<description>&lt;h2&gt;概要&lt;/h2&gt;
&lt;p&gt;这次重构博客的主要原因是因为找到了未来的方向，以及对自身价值的进一步认定，博客的分类将会简化，从平时的点滴积累，大部分仍然会是技术和文化类的东西。另外，也将作为原创游戏进度更新的地点。
同时，由于原来博客的界面并不友好且繁杂，所以我对整个页面进行了重新的设计，为移动端和PC端分别做了样式，enjoy it!  &lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;分类说明&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Create：&lt;/strong&gt;&lt;br /&gt;
原创游戏更新，以及自己的技术作品展示。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;Skill&lt;/strong&gt;&lt;br /&gt;
技术文章，探讨自己的一些技术经验和心得。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;Art&lt;/strong&gt;&lt;br /&gt;
一些对世界的感悟，以及一些文学拙作。&lt;br /&gt;
&lt;br&gt;
&lt;strong&gt;Life&lt;/strong&gt;&lt;br /&gt;
日常琐事。  &lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Monday, 09 Feb 2015 16:44:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2015.02.09 16:44:article/Create-Summary</guid>
<category>格式化</category>
</item>

<item>
<title>两年来的总结</title>
<link>http://dtysky.moe/article/Create-2014_12_19_a</link>
<description>&lt;h2&gt;体三维显示器相关&lt;/h2&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;1. PCB：&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://github.com/dtysky/Led_Array&amp;quot;&gt;https://github.com/dtysky/Led_Array&lt;/a&gt;  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;电路部分，其中转接板-Expand默认为黑金某核心板的转接板。&lt;br /&gt;
LED阵列侧边的引脚预留给光电传感器，防止误差积累，转接板侧边引脚预留给USB开发板和蓝牙设备。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;2. 机械部分：&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://github.com/dtysky/3D_Displayer_Machine&amp;quot;&gt;https://github.com/dtysky/3D_Displayer_Machine&lt;/a&gt;  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;其中作为整体的那个dwg文件中没有替换后面修改的联轴器。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;3. 控制部分：&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://github.com/dtysky/3D_Displayer_Controller&amp;quot;&gt;https://github.com/dtysky/3D_Displayer_Controller&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;70%完成度，只需要把DDR2控制器完成或者替换就90%了，做法都有着良好的注释，不过代码写的比较早，不怎么好看，也懒得重构了。&lt;br /&gt;
包含USB和PC通信的上位机、固件、FPGA部分，LED的FPGA控制部分（包括双缓存），DDR2控制器（缺少PHY）。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;4. DDR2控制器：&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://github.com/dtysky/DDR2_CONTROLLER&amp;quot;&gt;https://github.com/dtysky/DDR2_CONTROLLER&lt;/a&gt;  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;缺少PHY，包括控制器主体、分别用VHDL和Systemverilog完成的Testbench，其中SV的具有随机测试功能，同时包含修改好的1G DDR2模型，直接在Modelsim内混合仿真即可。  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;剧本解析器&lt;/h2&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;a href=&amp;quot;https://github.com/dtysky/Gal2Renpy&amp;quot;&gt;https://github.com/dtysky/Gal2Renpy&lt;/a&gt;&lt;br /&gt;
文档坑爹中，等有时间+心情补完。  &lt;/p&gt;
&lt;h2&gt;感言&lt;/h2&gt;
&lt;hr /&gt;
&lt;p&gt;两年前，我是个什么都不会、生活没有方向的菜鸟。&lt;br /&gt;
这时候，学姐出现在了我的面前，她那与我相似的性格吸引了我，我们谈了很多，黄油哲学人生物理耳机无所不谈，最后，我做出了一个选择——将梦想继续下去。&lt;br /&gt;
我翻出了高二时写的设定稿，将其不断重构，使其逐渐完整，然后在某人引导下看培根霍布斯休谟尼采海氏康德等等，都是为了在了解世界的同时，让作品有一些深意，这也导致后来我完成了某选修课的5W字民哲论文和被人吐槽剧本写得像论文，但是，我的确在进步。&lt;br /&gt;
进步是必要的，也是必然的，在以上的前提下，我找到了目标，回归了那个拼搏和努力的我。&lt;br /&gt;
“不懂怎么做，不会做，怕做不完，怎么办？”&lt;br /&gt;
舍友：“先接了再说，东西可以学，机会只有那么点。”&lt;br /&gt;
这里感谢我的舍友给了我这些鼓励和帮助，大一就带领学长学姐拿国奖的他毫无疑问为我提供了强大的技术支援和信心（舍友就是拍视频那个）。&lt;br /&gt;
FPGA不会？学&lt;br /&gt;
PCB不会？学&lt;br /&gt;
CAD不会？学&lt;br /&gt;
C不会？学&lt;br /&gt;
C#不会？学&lt;br /&gt;
Python不会？学&lt;br /&gt;
只要有心学，这些东西都不是问题。&lt;br /&gt;
托他们的福，我也能够在如此低的GPA下找到一个还好的工作，也赢得了每一个面试官的尊重。  &lt;/p&gt;
&lt;p&gt;以上是鸡血正能量，以下是现实  &lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;现实的分界线  &lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;并不是你想做到什么就能做到，可行性分析是重要的。&lt;br /&gt;
综合才能、财力、时间，以及现有科技水准才能得到一个最终的答案。&lt;br /&gt;
这个项目在我看来，最终无疑是&lt;strong&gt;失败&lt;/strong&gt;的。&lt;br /&gt;
不但距离一开始天马行空的“三片DMD全彩投影体三维”等想法相去甚远，甚至连妥协后的计划都没有完成。&lt;br /&gt;
因为我的热情已经被一次又一次的琐事磨灭了。&lt;br /&gt;
企划、采购、联络外包，亲自跑工厂和别的装作自己要批量，唯唯诺诺，为了价格磋商，为了成本计算每一笔小开支，这些东西都是完成这个东西必要的，但却是我不想去做的。&lt;br /&gt;
&lt;strong&gt;我只是想搞技术而已，我不想管什么和商人的交流。&lt;/strong&gt;&lt;br /&gt;
但现实是残酷的，你不得不去做这些事情，或者说这些事情必须有人去做。&lt;br /&gt;
然而我选择了单打独斗。&lt;br /&gt;
我牺牲了所有课余活动，学到了很多，代价是自身能力不足没有完成作品。&lt;br /&gt;
&lt;strong&gt;我不希望这件事再发生在我现在唯一的梦想——那个GAL上。&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;我曾也以为我一直能够作为一个中二少年在自己的世界奔跑下去&lt;br /&gt;
但果然还是无法抵挡外面那宽广的世界&lt;br /&gt;
就像那通往银河的列车&lt;br /&gt;
在永恒孤独的铁道，向着路过行者致敬  &lt;/p&gt;
&lt;h2&gt;Thanks&lt;/h2&gt;
&lt;hr /&gt;
&lt;p&gt;感谢学校提供的机会和资金，感谢舍友的技术支援，感谢在米帝读PHD的学姐的引导（嫁给我吧），感谢TI的样片支持，感谢老师们的一些指导，感谢Cypress蛋疼的片子和支持。  &lt;/p&gt;
&lt;p&gt;Over。&lt;/p&gt;</description>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">dtysky</dc:creator>
<pubDate>Friday, 19 Dec 2014 23:00:00 -00:00</pubDate>
<guid>tag:http://dtysky.moe,2014.12.19 23:00:article/Create-2014_12_19_a</guid>
<category>日常</category>
</item>

</channel>
</rss>