【Harmony】在鸿蒙Next上实现仿真翻页有多难?——上手鸿蒙的记录与吐槽

4,343 阅读6分钟

作为一名不专业的小说阅读爱好者,如果不能在鸿蒙Next设备上来上一句:阅读,启动!那将是一件天大的憾事。

所幸的是,现在确实有在开发的鸿蒙阅读项目,比如说:legado-Harmony

不过关于核心的阅读功能,目前好像并没有相关的项目或者示例,那么这次就来尝试下?

前言

首先还是先展示下目前实现的效果:

录屏2024-06-28 15.39.35.gif

其性能说实话,确实意外的很不错,在虚拟机上,基本只需要耗时3ms?

image.png

现在如果让我来评价下就这个小功能的实现过程,可以套用一个梗:

阅读器是这样的,用户只要随便划拉下手指就可以操作了,可是背后的开发者就需要考虑的事情就很多了。

提醒:目前基于的鸿蒙API版本为12,在11这块提供的能力太初级了,是真没想出有什么方式能实现,因此鉴于保密条例,这次仅仅分享记录下思路,源码是不会公开的。

踩坑之路

实现这个仿真翻页,除了基本的翻页动画外,其实还有一个隐藏的需求点:

如何实现GIF图开头的长按菜单?

由于目前鸿蒙的canvas并不支持绘制组件,无法直接把控件展示到canvas上,另外经过测试,无论是OffScreenCanvas还是通过componentSnapshot来获取的组件截图,其性能都比较差,差不多接近16ms的耗时,这种直接占了一帧的耗时是肯定不能接受的

为了实现这个点,我采用的方案是:

手势拖动期间使用canvas绘制,在没有拖动手势的时候,使用普通的Text组件;

这样的话就会引出这么两个注意点:

  1. 需要保证Text组件和canvas组件的无缝切换,不能有明显的白屏等。
  2. 需要保证Text组件和canvas的绘制效果要对应一致,不能出现文字位置、大小等方面的差异。

Canvas和Text的无缝切换

这块原来采用的方案是通过RenderNode的自定义绘制功能来实现。后来发现RenderNode的切换总带一帧初始状态的绘制,不能做到无缝切换(大佬们有更好的方案么?),后来通过canvas+DrawingRenderingContext来实现的。

绘制效果对应一致的问题

绘制一致性方面的解决方案只能一个个的实验,由于看不到源码实现,所以只能结合fontMetrics方面的知识来进行猜测。

说实话,背了那么多面试题,这次是真的第一回有用上的地方

现在记录下实验结果:

canvas绘制文字所需要的基线位置=Math.round(lineHeight * (Math.abs(fontMetrics.ascent) / (fontMetrics.descent - fontMetrics.ascent)))

手势与绘制部分的处理

手势部分需要的数据除了手势位置点,还需要速度等参数来做惯性计算。这块我选择的是PanGesture,其所带的参数中包含了我所需要的参数。

至于剩余的贝塞尔曲线等部分,这块的api能力挺全的,因此这里的计算方式没啥好说的,等同于以前做flutter和android的小说阅读器的计算方式,直接翻译过来即可用,都不需要适配修改。

唯一比较纠结的是由于DrawingRenderingContext使用的是绘制模块的canvas,这个canvas不同于canvas控件,不支持生成渐变色,所以阴影的实现就成了一个大问题。现在我使用的方案是给pen和brush设置MaskFilter,通过其BlurType的配合,实现的模拟阴影,现在来说,效果还算可以?

就是这个阴影的实现需要换实验方式,于是乎需要结合数学计算来生成辅助点和辅助线,这个数学计算虽不难,但确实也让人不爽。

鸿蒙开发之我见

在实现了这个翻页动画之后,我也是对开发鸿蒙自认为有了一定的了解,先说我自认的优点部分:

  1. 作为一个初版,鸿蒙的性能意外的还挺不错的,至少在虚拟机上确实表现还真不错;唯一纳闷的部分是有时候挂机时间长了,在虚拟机上手势的响应速度会变的很慢,当时我还以为代码写的有问题导致了卡顿问题…………后来重启了下虚拟机,这个卡顿问题就解决了…………

  2. 文档确实全面,甚至过于全面了,论坛虽然不活跃,但是足够解决大部分的问题了。

说完了优点,自然也有要吐的槽点:

  1. 作为一位老android和flutter,接触这种闭源项目的开发确实有一定的别扭,至少源码学习方面是别想了,话说老ios开发都怎么学习源码的实现方式的…………

  2. 功能初级且缺失,这回真的需要具有一定客户端开发经验的人才能快速上手开发,很多地方如果有android的开发经验,至少能通过寻找android底层技术点的关键字方式快速找到对应的API,来自己实现功能;如果没有android开发经验,可能需要翻全了API才能找到,至少在分页计算这块我是这么感觉的…………

  3. 令人懵逼的单位转换,在鸿蒙中存在五种距离单位,除了px,还有vp、fp、lpx;这个可以很明显的看出对应的是android的px、dp、sp等,混排不是太大问题,但是由于typeScript的特性,而且这些单位并不支持在配置项中自动转换,所以经常出错后才发现是遗漏了单位方面的手动转换处理。

最重要的暂时就这些,说实话,总体来说虽然不及flutter和android,但是考虑到才是初版,我认为是及格的。

虽然称不上遥遥领先,但是也是真正的未来可期。

目前还存在的待解决问题

  1. 由于canvas绘制的方式,那么如果对应的原页面中出现花里胡哨的复杂控件,在canvas上实现这块的绘制就是比较恶心的部分;

    获取真就需要componentSnapshot来获取到这个复杂组件的截图,然后绘制到canvas上,这块对性能有一定的影响,但是如果控件大小不大的话,影响应该也比较有限。

  2. 仅仅实现了lineHeight、textSize的对应处理,而且这种方式的同步成本比较高,需要数据源中较精准的记录,比如说现在就需要记录每行多少字了,而在原来的分页计算方案中,我仅仅计算了每段的展示高度,这就意味着我原先的甚至都写了一半说明文章的分页计算方案要重做了……

待跟进的部分

其实也可以看出,主要问题就是canvas不支持绘制组件,如果canvas能支持高性能的直接绘制组件,上面的一半问题都自然能得到解决,因此后续可以跟进canvas的能力跟进。