Normal view

There are new articles available, click to refresh the page.
Before yesterdayDandy's Blog

我做了一个可以锻炼视力的 App

15 April 2020 at 17:30

没错,我做了一个新 App,可以帮助你放松眼睛、缓解屏幕疲劳,甚至改善视力。

说实话,要不是因为这次疫情,估计打死我也想不到会做个这样的 App。

本来我视力一直都挺好的,也许是因为我常年在外旅行摄影,即使在家也保证每天都有户外运动,所以每天平均下来的屏幕时间并不多。没想到疫情一来,不仅旅行计划全部取消,整个生活节律都被打乱了。居家隔离期间,屏幕看得越来越多,没几天就明显感觉到视力有下降的趋势。

有天和一位眼科医生朋友聊起这事,他建议我每天做几次眼保健操,坚持一段时间会有好转。我做了几天之后,确实有些效果,不过也遇到了三个问题:

  1. 总是想不起来要做;
  2. 经常遗漏具体步骤;
  3. 无法确认动作是否到位。

正常人遇到这种情况设个备忘录就行,但我属于爱瞎折腾的人,加上宅在家也是闲着,不如做个 App 来督促一下自己吧。

@dandyweng 发布于 2020 年 2 月 20 日的微博:这两天受到鼓舞了,脑子里又蹦出来一个特别牛逼的 idea,看看几天能做出来,反正 flag 我就先立在这了[doge]

其实我经常做一些自娱自乐的小应用,大部分都没有公开发布,因为我觉得并没有什么实用价值,比如用手机 3D Touch 屏幕称重的电子秤,没想到却在一夜之间爆红网络,这也激励了我把这次的想法落地,做成可以上架的 App。

这三个问题中,前两个相对好解决,第三个就要运用一些黑科技了。正好两年前我就研究过用 iPhone X 的 TrueDepth 相机(就是那个用来 Face ID 刷脸解锁的传感器)结合 ARKit 框架进行眼球追踪方法,这次正好可以派上用场。

15 天之后,Eyeye 诞生了。

Eyeye 视力锻炼:运用 AR 眼球追踪技术,指导你的日常视力锻炼。眼保健操新体验:App 会指导你做出每一步动作,并通过 TrueDepth 相机系统捕捉并分析眼球的运动轨迹和眼周肌肉的微弱变化、眼睛与屏幕之间的距离等全方位数据,确保你准确到位地完成每一个动作;缓解视力疲劳:帮助你缓解由于长时间盯着屏幕、书本等导致的眼睛疲劳,非常适合经常高负荷用眼的人群;养成健康护眼习惯:内置每日推送提醒,促使你建立定期活动眼睛的好习惯,减缓视力下降,长期坚持甚至可以在一定程度上改善视力。前往 App Store 搜索 Eyeye 免费体验。

Eyeye 目前有两种模式,完全免费的「轻松一下」提供了一套基础动作,可以有效缓解长时间高负荷用眼导致的眼疲劳;如果你想更进一步,只需要 18 元就可以解锁「进阶锻炼」获取更高效、全方位的锻炼方式,同时也可以支持一下我后续的开发维护工作。

具体锻炼步骤都是我的那位眼科医生朋友提供的,按他的说法,只要每天坚持锻炼至少三次,大部分人(包括成年人)的视力都是可以慢慢改善的。至于实际效果如何,从我这段时间的个人体验来看确实很不错,感觉自己的视力基本恢复到了往年的正常水平,大家也可以体验一下,然后与我分享你的实际感受。

如果你的设备不支持眼球追踪功能,依然可以在手动模式下使用:只需按照指示做出所需的动作,然后轻触屏幕的任意位置即可继续下一个动作。App 虽然不能判断你的动作是否到位,但依然能够起到提醒与引导作用,可以解决我所遇到的那三个问题的前两个。

Eyeye 刚刚上线,目前的功能还比较简单,我会在接下来的时间里慢慢完善它。如果你有什么好的想法,可以在评论区聊聊。

如果你喜欢,别忘了在 App Store 给写个五星好评,非常感谢!

点此前往 App Store 下载

前往 App 官网了解更多

个人网站第七次改版

23 April 2019 at 07:37

从 2010 年起,我每年都会重新设计与开发一次自己的个人网站,每次改版都会整理自己在过去一年里所学到的东西并与大家分享。直到 2015 年,几乎是在准备第六次改版的同时,我忽然有了二维信息流、模块化内容和私密社交网络的想法,于是开始构思如何将这些想法转化为一个产品、设计基本界面、开发交互原型,Vary 就此诞生。不过时间精力受此影响,第六版网站设计非常简单,开发过程也很仓促,没想到却是服役最久(1297 天)、最多人看过(99.6 万)的版本。

2016 年开始,我几乎所有的时间精力都用在了 Vary 的开发上,无暇顾及个人网站,持续到 2017 年底才暂告一段落,紧接着又开始为我的个人摄影作品展示平台 Camarts 重新设计和开发全新版本,直到今年首个测试版 PWA 上线之后,我才有心思回顾这些年的成长历程,开始第七次改版。

迟到了三年的第七版,终于在今天正式上线。

新版运用了时下流行的超大字号、渐变文字、弥漫投影、错位堆叠等设计元素,继续保持极简风格;技术方面大量应用了 CSS Grid 和 CSS 变量等新规范,也首次尝试使用 GSAP 开发基于页面滚动的渐进式视差动画效果;这也是我首个贯彻 Progressive Enhancement 理念——即内容优先,将页面布局和基本浏览体验与 JS 解耦,无框架或构建工具等硬性依赖,使用纯手写的静态 HTML 与原生 JS 使得在 iPad 上开发成为可能……更硬核的东西就暂且省略,如果大家有兴趣我再另起一篇技术文章详述。

好了,那就不多说,点击这个链接即可体验新版:https://www.dandyweng.com

One More Thing

嘿嘿,再多说几句。其实在开发新版网站期间,我又做了一个 app,叫做 Markly。

如果你像我一样,无论去哪里都用手机拍很多照片,那么你一定会喜欢 Markly——将你拍摄照片的地点展现在精美地图上的数据可视化应用。

Markly 会自动扫描你的相册并统计拍照地点,让你轻松用全新的视角看看你的生活:在哪儿生活过?平时都喜欢去哪里?去过什么地方旅行?一目了然。还有更多功能即将推出,比如显示你的旅行足迹、统计你去过多少个国家和城市等等。

相册和位置信息含有个人隐私数据,承载了生活的方方面面,因此 Markly 十分注重数据的私密性,仅读取每张照片附带的 GPS 元数据,不读取影像内容,并且所有数据都在本机处理。因此,在征得你的明确许可之前:

* 不会上传任何从你相册里获取到的数据;
* 不会以任何方式改动你的相册或者照片。

Markly 目前还在初期开发阶段,预计在下个月上线,小范围的 TestFlight 内测会在近日开放。如果你有兴趣参与测试,可以识别下方的二维码关注我的微信公众号,然后发送你的邮箱来申请,名额有限,先到先得。

摄影十年

28 December 2018 at 16:28

即将过去的 2018 是我开始学习摄影的第十年,是时候回顾一下自己的摄影历程了,同时分享我的新年新计划。

从胶片相机的时代起,我就对拍照这件事有着浓厚的兴趣,无论去哪里玩,我都要从父母手中抢过相机来拍照。虽然照片能看的寥寥,浪费了不少胶卷,热情却丝毫不减。有了数码相机后,拍照更是成为我生活中不可或缺的部分,八九岁的我会趴在草地上精心布景,给自己的四驱车和乐高玩具拍上几张酷酷的照片。虽然照片拍得越来越多,我对摄影依然只有非常朦胧的概念,直到几年后一次旅途上的经历才触发了量变到质变的过程。

2007 年秋天,我随家人沿着滇藏公路自驾旅行。那是我第一次进入高原藏区,雪山草甸和布满秋色的树林辉映而成的美景目不暇接,让我兴奋不已。途径海拔四千多米的白马雪山垭口时,我全然不顾高原反应跑上附近的一个山坡拍照。那是个视野绝佳的地方,有个专业摄影团队正在拍摄,却看到个拿着卡片机的小朋友来抢位置,觉得很是有趣,便跟我聊了聊摄影。临走前,其中一位摄影师还让我用他的专业相机给他们拍了几张合影。

那是我第一次真正接触摄影,才知道这是一门高深的学问,亲手体验到了一种很厉害的相机叫做单反,有一大堆按钮,还有各种各样的镜头,拍出来的照片可好看了!这在还差几天才过 12 岁生日的我心里留下了深深的印象,在接下来的旅途里对此念念不忘,每当遇到拿着单反相机的摄影爱好者,我总要凑过去一探究竟。不久后到达梅里雪山,在附近的客栈里遇到一位来自上海的摄影师,他正架着超长焦的镜头对着雪山拍摄,见我非常好奇,便让我透过他相机的取景器瞄了一眼——雪山上的沟沟壑壑都能看得清清楚楚,而我手上的卡片机却怎么都拍不出好效果。当时我就下定决心:我也要有自己的单反相机,好好学摄影。

2008

年初,我如愿以偿地得到了一台入门级的单反相机——佳能 400D 和一枚 18-55mm 的镜头,还有几本摄影方面的入门书籍。在这一年里,我无论去哪里都带着相机,哪怕跟父母去市场买菜也要把每样蔬果都从不同的角度和参数拍个遍,然后仔细对比差异;回家之后,便反复研读那几本摄影书,书上没讲明白的地方,我也一定要在 Google 和维基百科上找到答案;下次出门时,再把理论知识付诸实践。如此往复几个月后,我去东南亚旅游时已经能拍出还算不错的作品了。

2009

在开始学习摄影之后,我更爱旅行了。这两大爱好可真是天作之合。从这一年起,渐渐形成了以自然风光为主的拍摄风格。

2010

随着我摄影水平的持续提高,那台小单反已经不能满足需求了,于是给父母写了一篇《请款报告》阐述了专业级相机对我的必要性。经过批准之后,我买来一台佳能 5D Mark II 相机,一直使用至今。几年后我才知道,当时正是家里的经济困难期,买那台相机的开销掏空了父母的所有存款。

2011

这年秋天,我再次走进藏区,开始了四年环游中国的旅行摄影计划。这次有了专业器材的加持,更不会放过任何美景。拍到了许多优秀作品后,我萌生了将它们分享给更多人看看的想法。对于正在学习编程和网页设计的我而言,做一个专门用于展示自己摄影作品的网站似乎是理所当然的。

2012

1 月 1 日,我的摄影网站 Camarts 正式上线。这个名字由 Camera 和 Arts 拼接而成的网站在当时只是个用着默认主题的 WordPress 博客,虽然并没有几个访问者,我依然乐此不彼地不断更新完善着它。到下半年时就有了完全自己设计开发的界面,接着我又用最新的技术开发了移动端 Web app。为了让更多人看到我的成果,我还很调皮地将它安装到了苹果专卖店的所有展示机上。

2013

我自己设计和开发的个人主页意外获得了一些网站的推荐,随之而来的是媒体与知名人士的转发关注,Camarts 的访问量因此剧增,高峰时段一小时的访问量就相当于前一年的总和;我的摄影作品因此获得了一些网站和新闻媒体的转载与刊登,也让我有幸登上同济大学等多所高校和 TED 的讲台与大家分享我的故事。

2014

秋季我前往新疆拍摄,这次旅途标志着我已经将足迹遍布了中国的所有省份和直辖市。

2015

这一年我将目光放到了国外,开始了在美国拍摄的行程。于此同时,我先为 Camarts 推出了微信公众号,又为其设计开发了相应的 iOS app,意味着这个三年前的小博客已经成长为一个跨终端的摄影作品展示平台。

2016

随着 Camarts 功能的逐渐丰富和我每年拍摄作品数量的持续增长,一些问题开始显现。我在当年一篇博客文章里写道:

五六年前我开发 Camarts 的时候,制定了现在这种以多张照片为一辑的发布方式。这些照片通常在同一个地方拍摄,或是表达相同的主题,配上一些说明文字,颇有讲故事的感觉。优点不言自喻,缺点也渐渐显现:后期需要处理大量的照片,费时费力;展示方式受到局限,不便索引……传统的发布和展示方式已不再适合。

确实,当时我从拍摄到发布一张作品的工作流非常繁杂:每次旅行之后,都要将所有照片统一进行梳理和后期处理,然后再筛选和归档到不同的主题,接着逐张压缩并加上水印,再写好说明文字后,按照严格的时间顺序择期连载发布。

这个过程非常耗费时间和精力,而我每年拍摄作品越来越多,能用于后期处理的时间却越来越少,导致作品堆积的情况——都 2016 年了,我却还在发布 2014 年拍摄的作品。而这还不是唯一的问题:

按照原先的计划,Camarts 在连载完《西北行》系列,即 2014 年秋季新疆之行的摄影作品后,紧接着就开始发布 2015 年春季在美国旅行期间拍摄的作品。这时我才发现,Camarts 上的摄影作品以在中国拍摄的为主,在索引结构等方面对于国外的情况考虑得并不充分。

从技术角度来看,Camarts 的架构已经落后了。毕竟当初开发的时候,我只是一个 15 岁的初学者,目标仅仅是做一个类似于博客的网站,能发照片就行。虽然后来不断地迭代更新,丰富了功能,但本质依然是个基于 WordPress 的博客类网站……扩展性也十分有限。此外还有一系列历史遗留问题,包括图片分辨率太低、EXIF 信息条遮挡照片、Logo 水印太大等等。

要想彻底解决这些问题,最好的方法就是重构——全部推倒重来。但当时我的所有时间精力都投入到了一个试验项目(后来诞生了轻量社交网络和内容创作工具 Vary)的设计和开发工作上,连继续发布作品都无暇顾及。

在这些因素的综合影响之下,Camarts 在 1 月 28 日连载了新疆之行的最后一辑作品,随之正式停止更新。

2017

Vary 正式上线。在设计、开发和运营这个互联网产品的过程中,我收获了太多,得以站在全新的高度上以前所未有的视野重新审视,也积累了更扎实的编程知识来实现新的愿景。

随着全新的 Camarts 在脑海里渐渐成型,在 10 月 1 日这天,我为新版写下了第一行代码。

几天后,我买来一架 DJI Mavic Pro 无人机,开始尝试航拍所带来的全新视角;又过了几天,便踏上前往欧洲拍摄的旅途。

2018

上半年是非常忙碌的,我只能在难得的业余时间里为新版 Camarts 添砖加瓦。

也许老天爷觉得我太过忙碌了,想让我好好休息休息——今年六月的一天,我用来开发的电脑毫无征兆地坏掉了,从那天起,我几乎全是在等着修电脑、等着买新电脑、等着修新电脑、继续修新电脑……的日子里度过的,直到此时此刻,我在写这篇文章的时候,依然在等着修电脑。不过在这期间,我做了两件事情。

首先是设计了一个全新摄影工作流。这个工作流我之后会专门写篇文章来详细介绍,现在先说个概括:在 iPad Pro 上进行后期处理,配合云服务以及我自己设计开发的软件来管理与发布作品。这个流程是高度自动化的,我只需要做些基本决策,其余部分都交给程序自动完成。

其次,我将这十年里的所有摄影作品都仔细梳理了一遍。2015 年至今拍摄的作品虽然没有公开发布,我旅行摄影的脚步却从未停止,在这四年里的足迹已经横跨美洲东西海岸、遍布近二十个欧洲国家和国内许多风景绝美的地方,拍摄了大量的作品。可惜拍完就堆放在硬盘里,不但没有经过整理,连我自己都没有认真欣赏过,其中一些甚至已经被遗忘了。这次的梳理过程中,我精挑细选出了一部分放进了 iPad Pro 里,开始运用全新的工作流来做后期处理,以备在全新的 Camarts 上发布。

那么,新版到底是什么样的?

新计划

与旧版那样简单的个人网站不同,新版是以国际化的现代互联网产品的标准来设计和开发的,这一标准将会在诸多方面有所体现。

在大家能看到的网站和 app 的背后,我先设计并开发了一个庞大的系统,用来协助后续的运营工作。目前我已经为新版 Camarts 搭建了分布式的服务器架构、设计了高性能的数据库,又用最先进的技术开发了后台程序、配合新工作流的照片管理软件和自动化辅助工具等等。下一步,就是开发大家能看到的网站前端了。

按照最初的计划,网站早就该上线了,但因为连续修电脑的事情让开发进度严重滞后,现在还没能回到正轨,可能还要几个月的时间才能实现心目中的各项功能。于是我决定改变一下开发方式:只实现最简单的作品浏览功能后,就先悄悄上线一个基础版本。在我不断完善、增加功能的过程中,大家可以先看到些新作品,同时一起见证网站开发过程。

如果一切顺利(保佑我电脑不要再坏了…)这个版本会在 2019 年 1 月 28 日,即旧版停更三周年之际在全新的域名(https://camarts.app)上线,开始发布这十年里的所有精选摄影作品。当然,未来拍摄的新作也会在第一时间更新。

这些作品的后期处理会是一个持久的过程(曾在旧版发布过的作品也会以现在的审美标准重新筛选和后期处理后才在新版发布),所以新版在网站功能和内容丰富程度上恐怕一时还无法取代经营多年的旧版。因此,旧版网站和 iOS app 还会继续维护到 2020 年。

在新的一年里,我的主要时间精力都将集中在 Camarts 上。在新版达到我心目中的样子时,我再写篇文章详细介绍它的方方面面。

在新的十年里,我将踏上更多摄影之旅,走遍世界。于此同时,借助新版 Camarts 高扩展性的先进架构,我可以敏捷地调整网站功能、设计和摄影工作流,实现未来所想。

我会通过 Camarts 微信公众号及时分享新版的开发动态、推送新作品,大家可以扫描下方的二维码关注。为避免混淆,也让我能够更方便地通过微信公众号收集反馈信息,微信机器人已于今日下线。

Unity + AR 体验记

5 March 2018 at 10:18

在 iOS 11 发布时,作为其重磅功能之一的 ARKit 就引起了我强烈的兴趣。在最初的 beta 版本放出后,我就第一时间下载更新,并开始研究它的应用方法。

那段时间我正好也对著名的游戏引擎 Unity 很感兴趣,不满足于制作小游戏的我便开始琢磨如何将 iOS 的 ARKit 和 Unity 结合在一起来实现 AR(即 Augmented Reality,增强现实)效果。这时我发现了 Unity 的 ARKit 插件,它让开发者可以很方便地调用相关 SDK。借助小时候掌握的建模技能和这么些年来的编程经验,轻松实现了这样的 AR 效果:

由于不久后我的兴趣点又被吸引到了物联网智能家居上,那一次 AR 探索也就浅尝即止了。最近几天,来自瑞典的一位设计师 Peder Norrby 实现的幻象应用 TheParallaxView 走红网络,他除了用到上述的 Unity ARKit 插件外,还结合了 iPhone X 的 TrueDepth 传感器进行面部追踪,根据眼睛与屏幕的相对位置和距离来调整 Unity 场景的相机,由此实现虚拟的 3D 立体视觉效果。

完整视频:秒拍 | YouTube

这个应用最初只是在国外的开发者圈子里流传,随后经过微博科技圈几位博主的转发,在国内也成为了热门,许多人都迫不及待地想亲身体验它的效果。我此前就已经在 Twitter 上关注了作者,这次也是近水楼台先得月,经过交流之后,借助他提供的设计思路和源代码,我也在自己的 iPhone X 上实现了这个效果。

虽然原作者正在尝试将这个应用上架 App Store,却在审核环节遇到了些麻烦,目前不知何时或能否成功上架。我本想写一篇教程告诉想马上体验的朋友们我是如何自己实现的,转念一想这需要安装 Unity 和 Xcode 这两个大型软件及各种插件,还得自行配置开发环境和编译源代码,其中环节和变数太多,对于没有相关经验的朋友们可能并没有太大帮助。所以我打算将自己编译的版本通过 Apple 官方的软件测试平台 Testflight 与大家分享,如果你有兴趣可以在本文的评论处报名并留下自己的邮箱,若报名的人数足够多,我会在征得原作者的首肯之后申请 Testflight 并向大家发送邀请和安装方法。

后续我也会找时间来进一步研究 AR 方面的技术,并尝试将 Unity + ARKit 与 Cinema 4D 和 Asset Forge 等建模工具结合来实现更多酷炫好玩的东西,有新发现时我会第一时间在这里与大家分享。

💾

💾

微信小程序初体验

1 October 2017 at 08:28

在一年多前微信刚刚推出小程序的时候我是很感兴趣的,早早下载了它的开发工具来研究,可是后来发现微信却不允许个人开发者自己发布小程序,我的开发计划便不了了之了。

直到今年,微信总算是对个人开发者开放了小程序的部分功能,虽然有着诸多限制,但总算允许以个人名义发布了,于是我开始给自己开发一个小程序版的博客。

目前这个小程序已经发布,用微信扫一扫上图里圆形的小程序码即可在小程序里查看这篇文章。如果你正在手机上查看,可以将上图保存到相册,然后在微信的扫描二维码处点击从相册选择图片。

小程序主要是用 JS 配合微信自己的 WXML 和 WXSS(本质上就是 HTML 和 CSS)来开发,对于有着 Web 前端开发经验的我来说很容易上手。不同之处在于这里不能直接操作 DOM,也不能用 jQuery 之类熟悉的框架,而是用上了类似 React 和 Vue 的 MVVM 架构模式。最近几年 Web 前端领域日新月异,我却将重点转移到了 iOS 端开发,恰好错过了使用这类新架构的机会,这次借机学习一下新东西,就当是重新入门前端开发了,嘿嘿。

我的博客是基于 WordPress 的,只要对自带的 RESTful API 稍加调整即可使用,所以服务器端基本没有多少开发工作。小程序客户端方面,需要实现的是个人简介、文章列表、正文、评论和留言板等界面。

文章列表页

先从用来展示最新文章列表的首页开始,这个页面比较简单,从服务器获取 JSON 格式的文章列表数据,然后遍历显示标题、摘要和图片即可,再给每个条目都绑定点击事件,点击之后携带文章 ID 跳转到正文页。小程序页面间是通过类似于 HTTP GET 的方式传值的,需要手动把参数拼接到 URL,一点也不优雅。

正文页

正文页传输过来参数会自动被小程序封装到一个 options 对象里,从这里读取到文章 ID 之后就可以将它发送到服务器接口请求文章数据了。不过 WordPress 的文章正文是 HTML 格式的,而微信小程序并不支持原生 HTML 标签,因此无法直接显示内容。还好,经过一番搜寻,找到了 wxParse 这个可以将简单的 HTML 转换为 WXML 的框架,它会通过 JS 匹配所有的 HTML 节点,然后将它们转换成 JS 对象,再用一个非常复杂的 template 模板把内容渲染出来。虽然绕了一个大弯子,不过正文页总算是实现了。

评论页 / 留言板

这是相对来说逻辑最复杂、开发工作量最大的一个界面。嗯,其实逻辑本身并没有多复杂,工作量也不应该这么大,毕竟我的博客网页版就是用 JS 异步获取评论的,理论上逻辑都差不多,搞不好还能复用一部分代码……

开发微信小程序的时候,写着熟悉的 JS 和 CSS,用着 Chrome 的调试器(虽然只是个阉割版),恍惚间总会觉得自己正在轻松自如地做网页,然后时不时就会被现实啪啪两下耳光扇清醒。

刚开始制作评论页面时还挺顺利的,获取从上一个页面传进来的文章 ID,然后从服务器端接口获取文章列表,页面滚动到底部时自动载入更多等等都很容易实现。样式方面确实复用了网页版的许多 CSS 样式,所以开发起来还算轻松,全然不知一只脚已经踩进了大坑。

当我准备处理回复评论显示的时候才发现,微信小程序竟然不能递归嵌套 DOM 元素!WordPress 的评论嵌套是子评论中保存父评论的 ID 字段来实现的,在绝大多数编程语言中,只需写个简单的递归即可实现类似下面这样的树状结构:

这样即可将相关的评论都显示在一起,方便“盖楼”回复。可是 WXML 的 DOM 元素必须静态写好,除了 for 循环之外似乎并不能在 runtime 动态添加节点,然而 for 循环只能用来生成单层结构,不能嵌套;我试过用 template 嵌套自身来实现递归,失败;用 template A 来嵌套 template B 可行,但 B 却不能再套回 A,失败;用 for 单层渲染,然后用 @ 的方式实现类似于微博的评论显示方式,体验太差,放弃。那就先全部渲染出来,再用 JS 把 DOM 挪到对应的父元素里?抱歉,不可以直接操作 DOM。

上网搜了一圈,吐槽这一点的开发者不少,比如 wxParse 的作者在一篇文章中提到的:

无法循环使用模版下,如何处理多级 HTML 嵌套解析?既然不能够实现完美的代码较少的方式,那我们就采用一种笨办法,手动循环出 N 个模版,因为在实际场景中,尤其是在文章的 HTML 中不会出现超过 10 级的嵌套,那么这样我们可以手动写 10 个模版,进行循环调用。

难道真的得用这么笨的方法来写?何况文章内的 HTML 嵌套或许不会超过十级,但评论盖楼就是另一回事了。不甘心,继续查找解决方案。翻遍了官方文档,看完了为数不多的开源项目,浪费了若干小时的宝贵时间,却并没有找到更好的方法。有一个同样基于 WordPress 的开源微信小程序也是用手动写死五层嵌套的方式来实现的:

这对于有代码整洁强迫症的我来说简直是无法忍受的。然而在这个问题上耗费的时间精力已经超过开发整个微信小程序(包括服务器 API 调整)的总和,我不得不妥协,在代码整洁和功能完整之间选择了一个平衡点:手动写了一层嵌套。所以现在只能看到评论的一级回复,给这个回复的再次回复在小程序里是显示不出来的。思路虽然简化了,但实际开发过程中还是有很多需要额外处理的地方,细节我就略过不表了。

相对而言,发布 / 回复评论的页面就简单多了,界面就是一个评论表单,只需获取评论者的微信昵称和头像 URL,跟着表单数据一起提交给服务器即可。本想将评论者的微信头像 URL 作为一个 commentmeta 存到 WordPress 的数据库里,没想到才出狼窝又入虎穴,又踩到了 WP REST API 的一个大坑:自带的保存评论接口不能提交 commentmeta 字段。关键是不能提交你明说也行啊,我想别的办法就好,偏偏官方文档又语焉不详,让我查了一大圈,最后一气之下自己重写了整个接口。这个经历让我更坚定了 Camarts 改版时要自己写 CMS 的想法。

再加上点赞等功能之后,评论功能总算是完成了,留言板复用了评论页面,只是稍微调整了一下界面细节使之更适合显示较长的内容。麻烦之处在于留言板的入口,我的设计是在界面底部的 Tab Bar 导航栏上,微信小程序提供了这个组件,目测是原生实现的,切换起来挺流畅;不足之处在于样式太死板,可供自定义的选项不够丰富,调整了半天依然实现不了心目中的设计,只好自己用 WXML 和 WXSS 实现了,虽然麻烦些,但可以完全自己设计,在 iOS 端还能实现半透明模糊效果,看起来漂亮多了。

个人简介页

Tab Bar 上的另一个页面是我的个人简介页。原本我打算将我个人主页(dandyweng.com)上的内容全部移植过来,但前面的折腾已经耗费了太多时间精力,我也实在是没心思继续了,于是只写了几段文字,加上进入我个人主页的按钮,草草了事。由于微信小程序不能直接跳转到网页,所以点击按钮之后只能将网址复制到手机剪贴板,还需要到浏览器地址栏粘贴才能访问。

我对文章正文内的站外链接也做了类似处理,再完善一些界面细节,本次的微信小程序就基本完成了。

小结

通过这次的开发体验,实话说,我对微信小程序总体上是偏向失望的。

诚然,小程序在网页和原生应用的结合方面做得确实不错,我开发 Vary 时也遇到过网页与原生结合的难题,深知其中不易。比如当跳转页面时,可以明显看出是通过原生方式实现的,体验远超网页的链接或 AJAX 跳转;数据和 UI 分别加载的方式大幅提高了加载速度,达到了与原生应用非常接近的用户体验,同时微信扫个二维码或者在聊天窗口里直接点击卡片就能进入小程序,免去了原生应用需要下载的麻烦;更重要的是能够一次开发即可同时投放 iOS 和 Android,虽然必须在微信里运行,但以微信的体量而言,这几乎不是问题。

那为何失望?归结起来就两个字:限制。

微信小程序本身作为一个产品,在规划时需要一定限制,这可以理解;技术层面上的复杂度太高,为了保证可用性做出限制,我也可以理解。不能接受的是人为的、毫无说服力的限制。尤其是对于个人开发者而言,小程序看似有很多功能,但实际开发时往往都是这个不能用、那个不让用。这点我在几年前开发微信公众号机器人的时候就有过类似体验,看来是微信乃至腾讯一脉相承的做法。

我承认要运营这样一个互联网产品不易,个人开发者确实更难监管,但如此草率地“一刀切”歧视所有个人开发者正是对自己的管理能力缺乏信心的表现。不恰当地反观 Apple,在开发和上架一个 iOS app 的过程中就完全感受不到个人与企业名义有任何区别。当然,除去公司之别亦有国情之别,其中微妙不言自喻。

限制二字更多体现在审核环节。对微信小程序审核之严格我早有耳闻,为了确保不触碰任何红线,我在开发前就仔细研读了官网的规范,但在开发完成提交审核时还是以各种含混不清的理由被拒绝了,比如说我的博客含有“文娱资讯”内容,必须要相关资质的企业才能发布等等,如何定义“文娱资讯”?具体哪篇文章违规了?无人告知,申诉无门。上网查了一圈才知道,大家都称之为“薛定谔的审核”,基本上是碰运气,一次过不了随便改点东西再试一次可能就过了。

这时我才知道,原来那个“规范”只是个摆设,能不能发布是审核员说了算的,然而审核员是很不靠谱的,他主观认为你行就行,说你不行就不行。而且我觉得审核员是倾向于拒绝的,因为他要是审核通过之后出了什么问题估计就得自己背锅了,因此宁可错杀一千也不放过一个。

这给了我极大的不安全感,因为自己辛辛苦苦学习、设计、开发和调试的心血之作很有可能因为某个不靠谱的审核员的一念之差而全部付诸东流。这种不安全感让我不敢、也不愿再为它付出更多的时间精力。

最终我的小程序虽然侥幸通过审核,但可能不知哪一天就会触碰到某条看不见、摸不着、说不清、道不明的规定而被强行下架。所以大家趁着现在还能用,赶紧体验吧。

自己动手打造智能物联网设备

10 September 2017 at 15:00

自从前段时间研究了智能家居设备之后,我便迷上了物联网和开源硬件。玩遍了市面上各种常见的物联网产品,总觉得各有不足,于是我突发奇想:干脆自己做一个!

对于我而言,现在市面上的物联网产品最大的问题在于太过封闭,不同厂商的产品都得用自家的 App 才能使用,而且大多数用户体验实在不敢恭维。虽然通过我前一篇博文介绍的方法将它们接入到 HomeAssistant 和 HomeKit 之后使用自由度会高得多,但依然无法满足我自己编写家居智能控制程序的要求。

当我问 Siri 客厅的室温是多少的时候,数据是这样传输的:米家温湿度传感器读取温度数据、通过 ZigBee 信号发送给米家智能网关、通过 Wi-Fi 发送到路由器,路由器再传给树莓派、HA 存储数据、Homebridge 读取和广播数据、手机上的 HomeKit 再通过 Wi-Fi 读取数据……可想而知,数据传输的环节越多,稳定性和数据时效性就越低。比如我想做一个根据电视画面的亮度来调整房间灯光亮度的设备,当电视在播放夜间画面(亮度较低)的时候调暗房间灯光来减轻屏幕反光的影响,反之则调亮灯光方便我吃东西。这时候就需要以百毫秒级的速率来读取光线传感器的数据,一般的商业产品很难满足这种需求。

此外,价格也是很重要的因素:一个硬件成本不超过十元的智能插座零售价高达两百多元;即使选用相对便宜的米家系列传感器和 Sonoff 开关,要实现我心目中真正的智能家居——家中所有电器全部智能化、每个角落都有人体感应器也将是一笔非常可观的开支。

初识 Arduino

于是我开始深入了解开源硬件方面的知识来打造完全合乎自己要求的物联网设备,这时候我发现了 Arduino 这个开源电子原型平台。它本质上是一个单片机,有丰富的针脚接口用于连接各类传感器、伺服器和继电器等等。在电脑上用 Arduino IDE 编写代码后,可以很方便地写入到微控制器上执行。更重要的是由于 Arduino 的软硬件都是完全开源的,让我能以很低的成本获取所需的软件和硬件。

Arduino 有很多种版本,加之以开源的 PCB 图为基础自行生产的第三方产品可谓数不胜数,我选择的是一个可以和我的第三代 Raspberry Pi(树莓派)结合使用的版本——因为 Arduino 本身只是个单片机,并不能像树莓派之类基于 ARM 架构的微型电脑一样连接网络和存储大量数据。虽然市面上 Arduino 也有能实现相关功能的硬件模块,但我依然认为搭配熟悉的树莓派更简单好用。

这个 Arduino 通过串口与树莓派通讯,同时封装了树莓派上的所有 GPIO 针脚,所以需要将它用 USB 线连接到树莓派,然后再将整个 Arduino 都插在它上面。

接入传感器

市面上有非常多的传感器可供选择,且售价大多不过二三十元。Arduino 支持接入模拟和数字两种信号的传感器,我第一个接入的温湿度传感器 DHT11 属于后者,所以要用杜邦线将它插到数字针脚上。

插好之后就可以开始写代码了。Arduino 主要用 C++ 来编程,这是一种我从未接触过的编程语言,还好我学过 Objective-C,所以还算是能读懂;配合万能的 Google、完善的官方文档和传感器厂商提供的实例代码,在开发过程中基本没有遇到什么困难。

虽然厂商提供了已经封装好的代码库,只需调用即可直接读取到传感器数据,不过我对从硬件电路到软件数据的传输和转换过程很感兴趣,于是一探究竟,才知道 Arduino 通过数字接口读取到的是 DHT11 传感器在一段时间内通过电压变化来传输的二进制数值:

按照官方数据表的说明,高电平输出 26 微秒左右表示 0,输出 70 微秒则表示 1,用逻辑分析器即可看到比较直观的效果,这里我偷个懒在网上找了一张已经标注好的图:

这里可以看到传感器总共输出了 40 位的数据,其中前 16 位是湿度,紧接着的 16 位是温度,最后 8 位则用于校验数据有效性,若为温湿度数值之和即为有效;温湿度的 16 位数据中只有前 8 位是有效数据,后 8 位是奇偶校验位,这里全部为 0,可直接忽略。

按照上述规则来解析,图片中的二进制湿度数据为 00011110,温度为 00011001,将它们转换成十进制即可得到最终结果:30% 相对湿度和 25 摄氏度。再计算一下 00011110 + 00011001 = 00110111,即最后 8 个奇偶校验位的数值,证明数据是有效的。

当然实际使用时并不需要自己计算这些,只要引入官方提供的代码库,简单调用一下即可获取传感器数值并通过串口输出,非常方便。

这里要吐槽一下 Arduino IDE 的代码编辑器,功能简陋到基本就是个带语法高亮的记事本,然而它的流畅性和视觉效果甚至还不如记事本……让我不得不在 Sublime Text 里写代码再复制过来编译。

接下来试试光线传感器,它传输的是模拟信号,所以要插在模拟接口的针脚上。软件方面就简单多了,调用 Arduino 内置的 analogRead() 方法即可获取亮度数据。

将数据传送到树莓派

现在已经可以用 Arduino 读取传感器的数据,接下来就要将数据传送到树莓派来做进一步处理。实际上之前所写的代码已经可以让 Arduino 把数据通过串口输出到树莓派上了,所以真正需要做的只是在树莓派上写个程序来读取串口输入的数据,我是用 Python 写的,只需六行代码。

测试成功后我又在 Arduino 上接入了六七种传感器,读取数据的方法都大同小异,这里就不再展开;不过随着数据量的增加,需要对数据进行封装才好解析。这里我选用了 ArduinoJson 库将数据转换为 JSON 格式输出,这样在树莓派上用 Python 读取就方便多了。

在树莓派上用 Python 读取到传感器数据之后,就可以自己写个程序通过我前一篇文章提到过的 HomeAssistant API 来自动控制家里的其他电器了。至此我的智能物联网终端已经初步完成,当然我还会继续研究如何实现更多新奇有趣的用法,包括如何进一步脱离网络传输,完全在本地直接控制电器等等,相信没有做不到,只有想不到。在学习 Arduino 传感器的过程中我还顺便了解到了很多关于电机、伺服器、继电器、ZigBee 通讯协议和无线充电的相关知识,让我很感兴趣,说不定哪天我会一时兴起,给它装上轮子和机械手臂,做成 AI 机器人管家之类的东西,哈哈。

HomeKit 智能家居深入体验

8 August 2017 at 16:55

作为一个从小就爱捣腾数码产品的人,我对智能家居自然有着浓厚的兴趣。最近家里重新装修,便换上了批智能家居设备,我也借此机会对这个新兴领域深入研究了一番。

我觉得智能家居最重要的一点,就是用起来要比传统的控制方式更方便。这听起来像是废话,可实现起来却并不太容易,毕竟绝大多数所谓“智能家居”配套的 App 都非常非常非常难用,比如下图这种画风……即便难得遇到些好用的,想控制不同厂商的设备还得打开不同的 App 也是挺傻的一件事。

还好 Apple 去年发布了 HomeKit 平台,通过这个平台可以将不同厂商的智能家居设备聚合在一起显示和控制,并且深度集成到了 iOS 系统里,在锁屏状态下都可以直接控制所有智能家居设备,完全不需要打开厂商提供的 App;甚至可以不碰手机,直接喊 Siri 帮你控制即可,非常方便(逼格也不知要高到哪里去了);配合 Apple TV 或 iPad 作为控制中枢还能实现人不在家时的远程遥控和自动化控制等等。

然而 HomeKit 虽然已经发布了一整年,硬件的支持情况却并不理想,在国内更是没有得到足够的重视,很多标榜智能家居的厂商甚至不知其为何物。无奈之余,我也本着极客精神,开始研究自行接入的方法。

梳理一下我家的电器设备,大致可分为以下三类:

  1. 原生支持 HomeKit 的设备,如 Philips Hue 系列灯具等;
  2. 完全不支持通过网络控制的传统电器,比如空调、新风系统和电动窗帘;
  3. 可以通过网络控制,却未提供原生 HomeKit 支持的设备,比如米家系列、Sonoff 开关和 Dyson 风扇。

下面我就分享一下我的研究(折腾)经历,看看我是如何把它们统统加进 HomeKit 里的。需要注意的一点是:本文仅为个人经验分享,并不是教程,所以一些细节问题我就不展开了。相信网上已经有了不少相关教程,若有需要可自行搜索,也可以在这里评论与我交流。

原生支持的设备

这里是指生产商官方提供 HomeKit 支持的设备,普通用户就能开箱即用。然而所支持的设备并不多,我手头只有 Philips Hue 系列灯具和 OPSO 的智能插座等。

添加 Hue 灯具的方法非常简单:把 Hue 网关用网线接到路由器上,在官方 app 里根据提示搜索并绑定网关,然后添加灯具即可,完成后扫描一下网关上的序列号,所有灯具都会自动同步到 HomeKit 里。

不得不说,Hue app 的界面设计和用户体验是我所用过的智能家居类应用里最好的。不过把灯都加进 HomeKit 之后,这个 app 也就没太大用处了。

接下来是 OPSO 的智能插座,这是 Apple Store 官网上的唯一一个支持 HomeKit 的国产设备,产品包装很有苹果风,做工也不错,只需在 iOS 自带的 Home app 里扫描设备上的序列号即可使用,非常方便。不足之处是有点不太稳定,使用几天后出现掉线的情况,需要重置设备后重新绑定才能用,希望可以尽快通过固件更新解决。

添加之后默认显示的是插座图标,不过可以手动将它修改成一个灯泡

这就是目前作为普通用户所能体验到的 HomeKit,但我并不会止步于此,下一步将会用些非常规的技术手段把那些并未提供原生支持的设备也加进来。

要实现上述功能,需要借助一个名为 Home Assistant 开源平台(下面简称 HA),它运行在基于 Linux 的系统上,可通过 Web App 和 API 来访问,接入不同的模块即可控制局域网内的各种智能家居设备。

搭建 HomeAssistant

首先需要一个基于 Linux 系统的设备来运行 HA,由于它需要 24 小时不间断运行,所以我选择了超低功耗的第三代 Raspberry Pi(树莓派)。它虽然才一个巴掌大,本质上却是个功能完善的、基于 ARM 架构的电脑,USB、HDMI 和网线等接口一应俱全。

考虑到这台树莓派主要用于运行 HA,我选用了 HA 官方提供的 hassbian 系统。从 HA 官网下载 hassbian 镜像,在电脑上用 Etcher 写入到空白 TF 卡中即可。

安装完成后,将 TF 卡、电源线和网线都插到树莓派上就可以开机了。首次启动时 hassbian 会自动安装和配置所需环境,等待一两分钟后,在局域网内的其他设备上访问 http://hassbian.local:8123 就可以进入 HA 的 Web App 主界面。

这里可以看到 HA 已经自动识别了 Philips Hue 网关和局域网内的两台 Apple TV。这里的 Hue 是可以一键配置的:点击 Configure 再按下 Hue 网关上的实体按钮即可将所有相关灯具都自动添加进来。虽然 Hue 灯具本身已经可以用 HomeKit 控制了,但在 HA 中可以实现更多有趣的玩法,比如自动循环变色等等。

接入传统遥控设备

下一步开始接入那些不能通过网络控制的传统电器,这里需要用到另一个硬件:可以联网的万能遥控器。经过一番比较,我选择了对 HA 支持较好的 BroadLink,它可以发射用于控制空调等电器的红外射频信号和窗帘电机的 FM 433Mhz 信号。

单从硬件角度来看 BroadLink 非常不错,但是它配套的 iOS app 实在是……太!差!了!还好它对 HA 有着良好的支持,所以用这个 app 把它连上 Wi-Fi 之后就可以果断删掉了。

按照官方文档中的说明将 BroadLink 的 IP 和 MAC 地址等信息填入 HA 的配置文件后,在服务列表里就可以看到它了。通过 HA 调用 learn_command 指令,BroadLink 将会进入学习状态。此时用空调遥控器对着它按下开机键,HA 首页便会显示遥控空调开机的指令代码。

接下来要做的就是将这个指令代码按照官方文档的格式复制到 HA 的配置文件里,并给它设置一个名称。用同样的方法配置关机键后,重启 HA 即可在首页看到空调的开关。在 Web App 上点击开关,HA 便会读取这个指令代码并通知 BroadLink 对着空调发送与普通遥控器相同的控制信号,达到通过网络控制电器的目的。

窗帘电机、风扇和空气净化器等其他有遥控器的电器设备也用同样的方法设置之后就都可以通过 HA 来控制了,下一步是将 HA 接入到 HomeKit 上。

连接 HA 与 HomeKit

这里需要用到一个叫做 Homebridge 的工具,它可以在树莓派上调用 iOS 的 HomeKit API 虚拟出一个网关来,然后再用它的 HA 插件将之前添加过的电器都同步到 Homebridge 即可实现操作。安装过程非常简单,参考这几篇官方教程即可:

Homebridge 的 Github 主页
https://github.com/nfarina/homebridge

在树莓派上安装
https://github.com/nfarina/homebridge/wiki/Running-HomeBridge-on-a-Raspberry-Pi

与 HA 建立连接
https://github.com/home-assistant/homebridge-homeassistant

安装完成之后就可以在 iOS 设备上的 Home app 里添加 Homebridge 了,这里与原生支持 HomeKit 的硬件相同的添加步骤完全相同,只不过设备名称和设备序列号都可以在配置文件里自定义,能把自己的名字写到“厂商名”处的感觉还是挺好玩的。

至此,HA 上的所有电器设备就都可以直接通过 HomeKit 来控制了。下一步接入能通过网络控制但未提供原生 HomeKit 支持的电器,比如小米家居(米家)系列。

接入无原生支持的设备

我家的绝大多数单联墙壁开关都是米家的 Aqara 系列,经由米家多功能网关控制。使用 homebridge-aqara 插件即可将小米网关添加到 Homebridge 上。

插件安装完成后,需要在配置文件里填入网关的协议密码和 MAC 地址,获取这些信息需要用到一个小窍门:首先打开米家 App(iOS / Android 版本均可)并绑定网关之后,点击网关界面右上角的 ··· 按钮进入“关于”界面,接着快速连续点击界面底部的空白区域,直到列表中出现“局域网通信协议”和“网关信息”的选项;接下来进入通信协议界面,打开上面的开关即可看到协议密码;MAC 地址则藏在“网关信息”界面里的那堆 JSON 格式的信息中。将这些信息一起复制到插件的配置文件里,保存并重启树莓派即可。由于之前已经将 Homebridge 添加到了 HomeKit 里,现在新加入的米家设备都会自动同步过来。

至此,我已经可以用 HomeKit 控制我家几乎所有的电器了,但这还仅仅是个开始。随着 iOS 11 的发布,HomeKit 平台也得到了进一步加强,同时相关协议也逐渐公开了,相信以后会变得更智能、更好用。借助 HA 和 HomeKit 这两个强大的平台,可以充分发挥想象力来实现一些很有意思的玩法。例如配合人体感应器实现走进房间时自动开灯;通过设置情景模式实现跟 Siri 说“我要去洗澡了”即可自动关窗帘和开浴室灯等等;此外,我还用 Sonoff 智能开关、淘宝二十元买的电磁水阀和五金店买的几截水管 DIY 了一个自动滴灌系统,出门在外时也可以远程操控它给家里的花花草草浇水。

题外话

除了 HomeKit 外,HA 还能配合 IFTTT 和 Workflow 等效率工具以及各种开源项目实现更高级的用法,以及通过 RESTful API 实现与各种编程语言的双向通讯,使自己编写家庭控制程序成为可能,即使你没有 iOS 设备也可以愉快地玩耍。

比如我就随手写了一个 Linux 的 HA 客户端,当我想在 macOS 上关闭卧室空调时只需输入如下命令即可。由于我经常开着 Terminal,所以用这个比打开浏览器再进入 HA 的管理页面控制要方便得多(同时还附带满分装逼效果)。

我还更进一步,用 Python 写了个小程序来实现更智能的自动化功能,它除了有根据实时天气自动调整室内的灯光之类的基础功能外,还可以和我日常生活中的其他方面连接起来,比如当我的网站或者 Vary 的服务器出现异常时,会将我房间的灯全部调成红色以作警示;当手机定位我和家人都不在家,但人体感应器又检测到有人活动的时候(有贼!)就把家里所有的灯都改成阴森昏暗的蓝绿色……由于本文是以 HomeKit 为题,这些高级玩法就不详细展开了,如果大家感兴趣我就找时间再单独写一篇文章谈谈吧。

九个月的努力

28 February 2017 at 15:10

在过去九个月里,我一直在忙着做个小项目。经常关注我动态的朋友们应该已经知道我做的是一个社交 App,但至于它是什么样的,我一直有意保密,所以真正了解细节的人屈指可数。不过随着第一个版本的开发工作即将完成,现在是时候揭开它的神秘面纱了。

在这个信息大爆炸的时代,想在网上和好友分享点东西已经有了太多选择。各种社交网络看起来似乎完全不同,有着不同的功能特色、满足着不同的需求,但实际上它们都有着一个共同点。下图右侧是常见的几款社交类 App 的界面,左侧是我将它们抽象化之后的样子,不难发现其实它们本质上其实都是一样的:上面是你的头像和昵称,然后是文字内容,或许还会有张图片。这些元素组合起来就是一条信息,把很多条这样的信息从上到下叠在一起就成为了我们熟悉的信息流。

界面抽象化之后,其实都是一样的

信息流几乎是所有社交类 UGC 产品的核心。近十年以来,互联网已经发生了翻天覆地的变化,信息流却没有过多少根本性的改变。这种信息流的优势在于可以让你在非常短的时间内快速浏览大量碎片化的信息,起初感觉挺爽的,但现在越来越多人逐渐意识到信息太过碎片化并不是什么好事。至于碎片化究竟是好还是坏,我在这里就不作评判了,不过随着 Twitter 和微博相继放开 140 字符的限制,相信答案还是显而易见的。

限制放开之后,尴尬的情况就出现了:传统的信息流并不适应较长的内容。因为它是一维的,内容只能朝着一个方向扩展,所以内容太长就会打破整体的浏览节奏。对于这个问题,通常的解决方法是在信息流里仅显示少量预览内容,点击之后才展开或跳转到新页面查看全文。当然还有更简单粗暴的解决方法:把文字都拼到长长的渣画质图片里发布,也就是我们熟悉的长微博。这些处理方法虽然能解决问题,却一点也不优雅。在我看来,要完美地解决这个问题,唯有完全彻底地重新设计一个信息流。而这,正是我在过去九个月时间里所做的。

传统信息流是一维的,只能朝一个方向扩展

我发现,信息流的长短是由两个维度决定的,分别是时间轴和内容轴。传统信息流将这两个维度压缩进一维的空间里,扩展能力自然会受到限制。毕竟在它出现的时代,我们与用户界面交互时所用的主要设备还是鼠标,而具体到翻阅网页时,则是鼠标的滚轮——只能上下滚动。虽然在移动触屏设备成为主流的今天,这个问题早已不复存在,无论是上下滑动还是左右滑动都是习以为常的操作,但信息流的设计却依然继承着 PC 时代遗留下来的传统操作习惯,未能充分利用触摸屏幕的优势。

现在,是时候改变一下了。

我发明了一种全新的二维信息流,它可以往水平和垂直两个维度扩展,横向是时间,纵向是内容。这种经过“升维”的设计完全突破了内容长度的限制。用户的发布内容显示在长度可变的卡片里,左右滑动浏览不同的卡片,上下滑动查看单张卡片的内容。

二维信息流可朝两个方向扩展,彻底摆脱单条内容的长度限制

既然有理论上可以承载无限长度内容的信息流,如何充分利用它?这就要说到我的第二大创新——模块化内容。也就是说,卡片里的所有内容都是由模块组成的,每种模块都有特定的功能,比如想发一段文字,就用文字模块;要分享一组照片,则可以用图片模块。我目前已经开发了文字、图片、视频、语音和位置模块,还有更多模块正在设计和开发中。

这些模块可以任意添加、排序及组合。每个模块也有许多可自定义的细节,比如文字的字体、字号和对齐方式,或者图片的形状和排列方式等等;当你分享所在位置时,甚至连地图的缩放比例都可以调节。

不过,要是有些时候懒得调整那么多细节,只想简单快速地分享些东西呢?没问题。我开发了一个自动排版引擎,它会分析每张卡片的内容与模块组合情况,动态计算与调整各个模块的样式、尺寸和间距等几十项参数。无论你输入的是短短几个字,还是包含许多模块的复杂内容,都能自动为你生成一张好看的卡片。

智能的排版渲染配合高度个性化的模块化设计彻底摆脱了固定格式的束缚,让你充分发挥自己的想象力,分享不拘一格。“变化”是这个 App 的核心,所以我给它起的名字就是:Vary。

Vary 卡片效果展示

Vary 是一个定位于在熟识好友间分享生活的社交网络,重点是私密和清净。在这里,没有网站链接,没有广告和软文,没有代购和营销,没有无谓的鸡汤,也没有繁重的人际关系。只是在喧闹纷乱的网络世界里,一个可以安安静静地与你在乎的、真正关心你的人分享和记录自己生活的地方。

如今的互联网世界,已经有了太多助你结识陌生人的社交产品,但专为私密好友间交流的却寥寥可数。在我看来,这很大程度上是因为普遍观点认为这种产品难以大量聚拢用户和维持活跃度,若从商业角度出发,无论是大型互联网公司还是创业公司都不太会优先考虑这种产品。然而 Vary 是我出于兴趣爱好而制作的个人作品,商业利益并不是我的首要考虑,因此在这里你会看到许多与主流互联网产品“反其道而行”的设计:使用纯数字帐号体系,只有知道这个帐号的人才能向你发送好友请求,除此之外无法通过其他途径搜索到你;只有你同意与对方成为好友后,对方才能看到你发布的卡片;不会通过匹配手机或邮箱向你推荐那些你可能认识(但永远不想加为好友)的人;你发出的评论和点赞也只有接收者可见。总而言之,一切尽在你的掌控之中。

此外,在 Vary 上可添加的好友数量是有限的,这个上限数量会随着你的使用情况逐渐增加。如果你想让更多人看到你创作的卡片,可以用内置的分享功能将卡片分享到微博、微信或者其他社交网络,也可以生成分享链接直接发送给别人。这个分享功能是开发过程中的一大技术难题,我优化与调整了无数次,才让卡片内容在不同环境里的显示都能够尽量接近原生 App 里的效果。

Vary 卡片外部分享效果

我是一个极简主义者,相信这点已在 Vary 的设计上体现得淋漓尽致。通常简化设计的思维方式是做减法,即思考现有的东西里哪些是可以去除的;而我则喜欢从零开始做加法:彻底放下成见,从根本上思考每一个元素存在的必要性。这种思维方式在 Vary 的界面、交互和功能上都有所体现,比如注册帐号时,你需要填写的东西精简到了极致:不需要任何个人信息,也不用验证激活,只要设置一个登录密码——没错,只填个密码就能完成注册并开始使用,比第三方帐号登录还方便。

同时我也在不断研究如何通过技术手段来引导交互层面的创新,例如 Vary 的 iOS App 可以检测手指接触屏幕的面积,并以此来调整卡片的滚动速度:用指尖滑动时,一次只会滚动一张卡片;整个手指贴在屏幕上滑动时,则会根据你滑动速度的惯性来连续滚动多张卡片。这听起来也许有些玄乎,却非常容易上手,也很实用。

界面设计方面,自然是我代表性的简约风格,并采用以高冷暗色系为主的纯黑白设计,除极少数 UI 元素外再无任何颜色,层次感由灰度、透明度与模糊效果来体现。我一向注重细节,Vary 的界面细节更是以艺术品的标准来要求的,每一个图标、每一帧动画都由我亲手绘制并在程序中实现,处处细节都经过反复打磨,尽可能追求完美。若从理性的商业角度出发,这种程度的追求几乎是不可理喻的,因为它的投入产出比简直惨不忍睹,但我并不在乎。

Vary 主界面

看过我个人网站的朋友们应该都知道,设计和编程是我的两大爱好,我也一直在探索如何将它们更好地结合起来。过去几年里,我每年都会彻底重新设计和开发一次我的个人主页,以展示我在过去的一年里所学会的新东西。然而 Vary 的开发,则是将过去这些经验全部整合在一起,再闭关修炼了一年多之后,才得以协调使用六种计算机语言和十余种开源框架来实现的。

经过一个多月的测试和细节打磨,目前 Vary 的 iOS 版本已经上架 App Store,同时 Android 版本正在开发中,争取六月份上线。初版仍有些不足之处,我会在后续版本中逐渐完善,希望大家多提些建议。

最后,我开通了一个微信公众号,任何与 Vary 相关的事情都可以在这里和我交流,以及提交建议和 bug 反馈等等,有问必答。我也会将 Vary 的最新动态通过这个公众号推送给大家。如果你是一位 iOS / Android / Web 开发者,有意愿参与 Vary 的开发,也可以和我谈谈。总而言之,如果你对 Vary 感兴趣,就在微信搜索 VaryApp 或者扫描下方的二维码关注一下吧。期待你的反馈。

微信订阅号 VaryApp 的二维码

Workflow 实现签到应用客户端

13 September 2016 at 10:41

Workflow 是 iOS 平台里一个扩展性极强的自动流程 app,可以自定义一系列复杂操作并把它们编排成脚本,之后便可一键快速执行。网上虽然流传着不少 Workflow 脚本,但大多都仅用于简化日常操作或者实现一些扩展功能而已,经过一段时间的体验,我发现它的潜力远远不止于此:只要有些编程基础,完全可以把它当作一个简单的可视化脚本编程工具来用。这就意味着,可以用它实现一些原本需要真正的编程语言才能实现的东西,比如一个记录当前地理位置的应用。

blog-homepage-with-location-highlight-and-workflow-icon

如果你在用电脑阅读这篇博文,你可以在屏幕左侧看到一个显示我当前所在地的模块(如果你看不到就说明我已经回家了,看上面的图片吧)。我每次到达一个新城市后,在手机上“签到”一下就会更新这里显示的所在地,并在我个人主页的旅行足迹地图加上相关信息。此前这个签到功能是通过一个 Web App 实现的:用 Javascript 通过 HTML5 的 Geolocation API 调用手机 GPS 定位获取经纬度,然后使用第三方接口来将经纬度转换为具体的城市,最后再用 AJAX 提交到我的服务器保存。我每次签到都需要在浏览器中打开签到页面,输入密码登录之后才能“一键签到”。这个过程虽然算不上繁琐,却也并不简便,当然,如果用 Swift 写一个原生 app 来实现,用起来会更方便一些,然而为了“更方便点”专门写个 app 似乎又不太值得……于是这事就不了了之了,直到我用上 Workflow 才真正解决这个问题。这听起来很神奇,实现起来却并不复杂,在这里我会尽量用通俗易懂语言和大家分享一下我的实现方法。

获取定位信息

首先添加一个 Get Current Location 动作,并将返回的结果保存为一个变量,我这里将它命名为 location。

img_1217.jpg

接下来读取这个变量,并通过 Get Details of Locations 动作来获取城市名,然后将结果保存为一个名叫 city 的变量。当然只有城市名是不够的,所以之后还要重复几次这个流程,来获取省份、国家名称和经纬度等信息,并将它们分别保存到变量里。

img_1218.jpg

发送数据到服务器

现在我们已经获取到了所需的信息,接下来需要把它们发送给我服务器的签到接口。这个接口接受 HTTP GET 请求,所以简单拼接 URL 并访问即可发送数据,就像这样:

https://api.dandyweng.com/.../checkin?lat=纬度&lng=经度&city=城市名&state=省份名&country=国家名

这里我先用变量中的信息拼接好 URL,之后用 Get Contents of URL 来发送请求,最后用 Show Notification 来显示服务器返回的内容。

img_1219.jpg

运行测试

大致功能已经实现,现在把它保存成脚本,添加进通知中心的 Widgets 里试试看。

img_1223.jpg

点击之后脚本就开始在后台执行,不需要打开任何 app,几秒钟就执行完毕并显示服务器端返回的成功提示。

这时在服务器端查询数据库,已经可以看到新的签到记录,我个人主页上的旅行足迹地图和这个博客上的所在地信息也已经同步更新。

提升安全性

虽然已经实现了签到功能,但在正式投入使用前还有一个非常重要的问题要解决,那就是安全性。按照目前的实现方式,任何一个知道上面的 URL 和参数的人都可以通过这个接口来提交数据,有非常大的安全隐患。解决的方式是加个密码,但密码通过 GET 方式明文传输依然有可能被截获和盗用,所以并没有什么意义,因此这里要用复杂些的对称密钥加密的方法来进行校验。

首先,生成一个九位数的随机数字,保存为 rand 变量;之后再设置一个密码,保存为 key 变量;最后把随机数和密码拼接在一起,进行 MD5 加密,然后将密文保存为 token 变量。

img_1489.jpg img_1489.jpg img_1490.jpg

最后把 token 的密文和 rand 的明文放在 HTTP 请求里一起发送。服务器收到 rand 参数后,使用相同的步骤和预置的密码 key 来重新生成另一个 token,并检测与 Workflow 里生成的 token 是否一致,若是就表明前一步中填写的密码是正确的,否则就拒绝签到请求。这里的 token 每次都会变,只要密码 key 和随机数 rand 相同,在服务器端生成的 token 就必然是相同的,这里密码本身并不会被传输,从而避免被截获和盗用的风险。

至此这个“地理位置签到应用 iOS 客户端”就已经开发完成,根据我最近几天的使用体验,总结起来就一个字:爽!大家有兴趣也可以自己试试看,有任何问题都可以在这里留下评论,我会尽量解答。

闭关修炼

10 August 2016 at 15:39

最近有不少朋友来询问我的近况,好奇为什么我不像以前那么活跃了。确实,我已经足足八个月没有写博文,我的摄影网站 Camarts 最近半年都没有更新摄影作品,社交网站的动态也是寥寥可数。所以我觉得应该写篇博文,说说我最近都做了些什么,在想些什么,还有 Camarts 暂停更新的原因。

静待蜕变

五六年前我开发 Camarts 的时候,制定了现在这种以多张照片为一辑的发布方式。这些照片通常在同一个地方拍摄,或是表达相同的主题,配上一些说明文字,颇有讲故事的感觉。优点不言自喻,缺点也渐渐显现:后期需要处理大量的照片,费时费力;展示方式受到局限,不便索引;相似的照片较多,易审美疲劳等等。近一两年我的拍摄方式有了些变化,我称之为“精品化”,即每个主题仅拍摄一两张有代表性的作品,传统的发布和展示方式已不再适合。

按照原先的计划,Camarts 在连载完《西北行》系列,即 2014 年秋季新疆之行的摄影作品后,紧接着就开始发布 2015 年春季在美国旅行期间拍摄的作品。这时我才发现,Camarts 上的摄影作品以在中国拍摄的为主,在索引结构等方面对于国外的情况考虑得并不充分。这是第二大问题。

从技术角度来看,Camarts 的架构已经落后了。毕竟当初开发的时候,我只是一个 15 岁的初学者,目标仅仅是做一个类似于博客的网站,能发照片就行。虽然后来不断地迭代更新,丰富了功能,但本质依然是个基于 WordPress 的博客类网站。作为个人网站,这架构倒也不差,但用作 App 和微信机器人的后端就显得有些捉襟见肘了,扩展性也十分有限。此外还有一系列历史遗留问题,包括图片分辨率太低、EXIF 信息条遮挡照片、Logo 水印太大等等。

要想一次性彻底解决这些问题,最好的方法就是重构。具体地说,就是全部推倒重来。

那么,这么长时间过去了,为什么还迟迟不见新版的踪影呢?

因为,我现在还有更重要的事情。

一个小项目

我做个人网站的初衷,是为了综合运用和展示我的兴趣爱好。比如 Camarts 就把旅行、摄影、设计和编程很好地结合在一起。然而随着不断地学习进步,我已经不再满足于此。换句话说,就是我现在学的东西,仅用个人网站的规模已经很难体现出来了。于是我决定更上一个台阶,做一款大家每天都想用的、具有商业价值的产品。

至于做什么,我曾有过不少想法。经过长时间的思考、调查和分析,最终决定开发一个轻量化的精品社交网络,定位于熟识好友间相对私密的生活分享与交流。没有繁重的好友关系,没有无谓的广告和鸡汤文,只是一个很安静的地方,让你可以轻松地分享自己的生活给真正关心你的人,也不再错过他们的点点滴滴。

为了打造一种前所未有的使用体验,我在功能、界面和交互等方面,运用了一些在同类产品中未曾出现过的设计,希望能给大家带来一丝惊喜。至于具体是什么,我就暂时先卖个关子,目前这个项目的服务器端和 iOS app 都在开发过程中,争取在年内发布第一个版本让大家上手体验。(更新:现在已经做好,详情见文末)

虚一而静

三年前,我的学习经历和个人网站开始受到大家的关注,自此收获了许多溢美之词,颇有几分少年得志的感觉,久而久之便有些飘飘然,不知不觉就把自己放到一个很高的位置上,下不来了。尤其是刚步入二十岁时,我迫切地用过高的标准来要求自己,平添了太多压力。在这种压力的驱使下,我走了一些小弯路,经历了一些小挫折。然而,正是这些挫折才让我能够正视自己头上那些所谓的光环,放低自己,淡然虚荣,褪去轻狂和高傲,发现不足,并静下心来弥补;也不再过于刻意地追求一些暂时还不属于自己的东西,而是做好力所能及的事情,之后顺其自然。

我是个完美主义者,事事追求完美,不放过任何细节。一直以来我都引此为豪,后来发现这会让我活得很累,于是我便有意识地强迫自己去接受一些不完美,我做到了。可是有些事情确实得追求完美才能有好的效果啊!这似乎是个两难的问题,我迷茫了很久才明白——其实最重要的,并不仅仅是去接受不完美,而是一种可以判断什么时候该接受,什么时候不该的能力。知行合一,才是正解。

调整好心态,自然就有了时间、精力和心境来踏踏实实地提高自己,去发现和弥补那些很重要却被我忽略的事物,于是开始闭关修炼,首先是大量阅读,补充了一些历史、哲学、心理学、经济学、商业管理和各种与创业相关的知识;同时也更注意自己的身心健康,开始去健身房了;还接触了一些曾经不屑一顾的事物,深入体验之后才发现其实挺好的;最难能可贵的是与同龄人真正敞开心扉地交流,感受人与人之间那份简单的快乐。

在过去的六年里,我的个人主页每年都会彻底地重新设计和开发一次,今年或许会是个例外,不过我的各项兴趣爱好依然不会放弃。比如旅行,中国的每个省份都已经留下了足迹,逛得差不多了,接下来的旅行会以国外为主,尤其是北美地区;摄影方面,在作品趋向精品化的同时,开始学习和实践人像拍摄;设计方面则以用户界面和交互设计为主,保持极简风格,同时向商业项目的标准发展;编程方面,除了继续学习 iOS 开发和研究 Web 前端的新技术外,服务器端架构方面的知识也是新的挑战。

最近收到一些朋友的留言和 email,他们讲述了自己的经历和所思所想,以及了解我的故事后的感悟和改变,让我很欣慰。我才知道,原来我能给其他人带来这么大的影响。这种影响,足以让他们打破束缚,在人生的交叉路口上选择一条不同的路。虽然更多的朋友们都因为种种顾虑无法随心而行,但无论如何,我希望你们有一天也能在自己的人生之路上找到精彩,去追寻自己所想所爱。

最后附上一张去年在西藏墨脱的照片,我觉得还算切题吧,嘿嘿。
更新:上面提到的小项目即 Vary,现在已经上线,详情见《九个月的努力》

photographing-at-motuo-in-a-rainy-day
❌
❌