ustbhuangyi / better-scroll

:scroll: inspired by iscroll, and it supports more features and has a better scroll perfermance
https://better-scroll.github.io/docs/en-US/
MIT License
16.47k stars 2.61k forks source link

使用rem导致非整数itemHeight带来的误差 #206

Closed TotooriaHyperion closed 7 years ago

TotooriaHyperion commented 7 years ago

https://codepen.io/torihyper/pen/wqbYZN

移动端用户大多使用rem来设置高度,经常产生非整数的itemHeight, 应该根据scrollerHeight和items.length采用先乘后除的方式来消除误差。

https://codepen.io/torihyper/pen/vJodEO

这个是iscroll就一直有的bug,由于绝大多数场景使用的是transition,所以一般不会遇到。 开始一个新的animation之前,要清除旧的animation,否则如果旧animation的结束时间早于新animation则会导致最终定位为旧animation,并且画面闪动。 比如,在index-list的demo中,如果强制关闭transition(useTransition: false)那么在动画结束之前,点击右侧index的操作将被动画效果覆盖。

关联pr:https://github.com/ustbhuangyi/better-scroll/pull/184

ustbhuangyi commented 7 years ago

animation 这个确实可以先清除一下 关于itemHeight,最终渲染到页面上都是整数像素的,有一个取整策略,具体可以参考:http://trac.webkit.org/wiki/LayoutUnit,而且你给的 demo 地址我也没看出问题,可以适当给 demo 加一些样式方便看出问题。

TotooriaHyperion commented 7 years ago

实际上我所描述的问题和渲染误差并无关系,而是计算误差。

虽然渲染到页面上的都是整数像素,但是他们并不是一致的, 在snap里,这种情况其实是得到了处理的: 比如10.25px的item,会生成 10px 10px 10px 11px 10px 10px 10px 11px的page,正因如此,snap并不会出现误差问题。 而在wheel中,这种情况并没有得到处理(统一使用唯一的itemHeight),所以才会出现误差。 至于demo的情况,在item非常多的时候,是会有很大误差的: image

TotooriaHyperion commented 7 years ago

如上图,在90的时候,已经错了整整1个itemHeight 在下图中,错位约半个itemHeight,截断了上面的43 image

TotooriaHyperion commented 7 years ago

image 如图,这是将官方picker demo修改为36.25px高度时的情况。 实际的clientHeight是不同的,取items[0].clientHeight显然是不合理的。 考虑到浮点数精度问题,使用scroller.clientHeight / items.length也是不精确的(在极端情况下) 这里要么使用nearestSnap来获得wheel对应的正确index,要么使用先乘后除的方式来消除误差。

nearestSnap显然会更精确,恩那么让我们改成nearestSnap吧。 同时发现了另一个bug:maxScrollY的计算,在wheel时,使用itemHeight显然也会计算出错误的值。

emmm好像不是很好改。。。snap会触发flick导致wheel失效。。。

那么问题来了, 1.snap的page是否应该作为一个公共的属性以便其它功能的调用? 2.snap是否应该拥有默认的flick事件,如果有,是否应该允许用户取消默认的flick事件? 3.在wheel中,如果用户注册了flick事件,则wheel会失效,因此,wheel是否应该注册自己的flick事件?

初步思考的建议是,snap和wheel不带默认flick事件,让用户自行注册flick事件。 这样,在用户不注册flick事件时,snap和wheel事件都是绝对正常并且结果可预测的。 flick事件本身会return掉_end,以避免和wheel、momentum产生冲突,而如果用户需要flick事件,那么肯定是会专门进行处理的,也应该将这一操作的具体行为交由用户选择。在flick事件中告知用户flick的方向即可。

我认为默认事件不应该修改原本的行为,应该只是触发新的事件(如pullup、pulldown)或同步ui(如scrollbar)。

ghost commented 7 years ago

@TotooriaHyperion 是的我也遇到这样问题,rem适配,item越多,误差越大

TotooriaHyperion commented 7 years ago

@1657413883 你可以先参照我上面先乘后除的方法临时处理一下。也可以使用平面滚动 + snap(实际上wheel是比较消耗性能的。因为目前的实现中,所有的item都会有rotate行为。) 对于这个问题,我还是觉得,pages不应该是snap独享,应该拿出来给大家使用。 而flick不应该修改原本行为,应该由用户来处理。并且应该作为单独的事件存在,不应该和momentum的判断标准混淆在一起。

ustbhuangyi commented 7 years ago

恩,rem 布局看来确实有问题