整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

打造丝般顺滑的 H5 翻页库

随着近几年移动营销页的火爆,催生了一个中国式的名词「H5」。而 H5 最常见的形态就是类似幻灯片翻页效果。

我们需要制作 H5 的时候,最快的办法就是使用一些滑动插件库,如 iDangero.us 出品的 Swiper,百度 BE-FE 出品的 iSlider。通过这些翻页库提供的强大的配置功能,我们就能实现很酷炫的翻页效果。当然,这些库还支持自动播放,点击切换和当前页面指示等配置,所以还能用在网页上,实现一些 web carousel 的效果。

百度 H5 也先后使用了 Swiper 和 iSlider 作为 H5 运行时的翻页框架,随着用户越来越多,也遇到了一些问题:

  1. H5 平台和这些库不能很好贴合,有些配置项用不上,而有些必要的功能需要「Hack」的方式实现。
  2. 一些 H5 的元素多、动画多,在低端机型上翻页时,翻页时会有「卡顿感」和「粘滞感」,用户体验不好。

而我们希望的 H5 翻页库能和平台本身的功能完美贴合,在保持体积小的同时,在翻页的时候能做到「丝般顺滑」。于是我们就开始了研(zao)究(lun)之(zi)旅。

开始

H5 滑屏框架的开发,第一个问题就是:页面是否跟随手指滑动?这也是腾讯 ISUX 团队的《滑屏 H5 开发实践九问》的第一问(这篇文章原文出处现在是 404 ,大家可以在其他的转载网站看到),这里用这篇文章的图片说明一下这个问题。

上图:不跟随手指滑动,下图:跟随手指滑动。

左边的是不跟随手指滑动,只需要关注手指触碰开始和离开两个时间点,中间过程不用考虑。所以实现起来比较简单。但是用户的操作没有实时的反馈,体验不够好。因此,尽管实现起来更复杂,我们仍然决定实现前一种「跟随手指滑动」的效果。

起步

下图是跟随手指滑动的 H5 最直观的版本,所有的「页面」依次从上到下,首尾相接。需要说明一下,这里的「页面」打引号,是因为实际上他们都是 div,后文说的页面都指这些 div。同时,我们这里以最常见的竖直方向滑动为例,水平方向同理。

基本原理图

这些 div 的宽度和高度都是 100% 的容器高度,可视区域是中间的部分,我们监听 touchstart, touchmove, touchend 事件,跟鼠标拖拽的原理类似:

  1. touchstart 时,记下起点位置;
  2. touchmove 实时计算滑动的距离,让所有页面一起沿着 Y 轴 translate 这段距离。
  3. touchend 时,能得到最终的滑动距离,跟设定的阈值比较。进入到页面自动控制阶段:大于阈值则让页面滑动到下一页,小于阈值则恢复到起始位置。

深入探究

简单的版本在上一部分很容易就实现了,如果其他需求不多,页面上元素和动画比较少,基本上就够用了。但是本文要探究的是如何能做到「丝般顺滑」,其实就是两个字:性能。

性能的瓶颈是什么呢?

我们的目标是:在「三多一低」(页面多、元素多、动画多,配置低)的情况下,滑动翻页时,尽可能不产生卡顿。

我们分成两部分来看这问题:手指离开屏幕前和手指离开屏幕后。

手指离开屏幕前

此时比较耗费性能的操作是:当 touchmove 触发时,计算出了要移动的距离,所有的页面都需要沿着 Y 轴移动相同的距离。此时必然免不了进行 DOM 操作,而 DOM 操作是非常「昂贵」的,再加上 touchmove 事件的频繁触发,性能处理不够好的话,很容易出现卡顿。

为了优化性能,我们很自然的想到一个策略:减少 DOM 操作

这里面包含两部分:减少 DOM 操作的元素和减少 DOM 操作的属性。前者比如,看不到的页面不参与动画。后者比如,只改变元素的 css 属性的一个或几个。

减少 DOM 操作的元素

最开始简易的版本的例子中,touchmove 触发时,所有的页面都沿着 Y 轴移动。其实没有必要,因为相当一部分页面是看不见的。那一般情况下,我们最少需要操作几个页面呢?答案是两个。可以回想一下,我们滑动的时候,最多能同时看到两个页面。这个方法相对于所有的页面一起移动,成倍地提升性能。

减少 DOM 操作的属性

这个方法的主要意思是,只需要操作一次 DOM 能达到的效果,绝不用两次。实际上,对于 slide 动画,我们只需要改变页面的 transform的值,其他的 DOM 操作(增加 class,修改元素的 innerHTML)等能不做就不做。

我们得到了一个初步的方案:初始化时,所有的页面一次性全部置入 container,除了我们用到的两页,display 属性都设置为 none。touchmove 的时候,只有这两页的 transform 属性有变化。

touchmove 的过程,我们可以写成数学表达式:

s=f(x),x∈[0,sideLength]

s=f(x),x∈[0,sideLength]

x

x 表示手指滑动的距离,s

s 表示页面滑动距离,sideLength

sideLength 是当前滑动边的长度,如果是沿 y 轴滑动,则是页面高度。写到这里,就跟时下很流行的「数据驱动」的概念很类似了。我们要实现的就只有一个 render 函数,输入是用户的交互数据,输出是页面表现。

手指离开屏幕后

当手指离开屏幕时,我们就已经知道了这次滑动的结果(向上还是向下?翻页还是回弹?),要实现的只是动画效果,我们有两个选择:

方案一:复用 touchmove 的 render 逻辑,按照手指滑动的速度,使用 requestAnimationFrame 控制动画;

方案二:使用 css3 transition 动画;

方案一的优点在于:可以在手指滑动和动画过程使用同样的 render 函数,最大限度复用了代码,逻辑统一;同时可以精确控制动画的每一帧,动画曲线会比较流畅。 缺点就是可能存在的性能问题。方案二跟方案一刚好相反。其实说到底还是 js 动画 vs css 动画的问题。

动画性能实验

为了比较两个方案在 H5 翻页动画上的性能优劣,我们取一个稍微复杂点的例子:

H5:百度无人车招聘的 H5

动画:从第 1 页翻到第 2 页

CPU: 6 * slowdown

浏览器:Chrome 61.0.3163.100(64 位)

js 动画方案:点击这里

css 动画方案:点击这里

js 翻页动画方案,Profile 结果

css 翻页动画方案,Profile 结果

通过实验我们可以看到,js 的动画过程中,帧率大多维持在 30 fps 上下。而 css 动画,基本都在 60 fps 上下。而且在动画过程中,明显感觉 js 动画有卡顿。这种情况在一些 CPU 和显卡配置相对低的 Android 机型上尤为明显。对于这个问题有兴趣的同学,可以看一下 swiper 库的 raf 分支,这是本次对比测试所用到的 js 。

所以,尽管 js 的动画方案看起来比较「优雅」,能用「数据驱动」的理念,统一解决滑动过程和动画过程的问题。实际上性能有瓶颈,我们只能在手指离开屏幕后,采用 css 的动画方案以保证性能。正应了一句话「能用 css 做的,绝对不要用 js 解决」。

实施方案

下图形象地展示了我们实施的基本思路,只有两页:

currentPage :当前页面

activePage:即将要翻到的下一页

其余的页面都是初始化的时候加载进 DOM 结构,但是 display 为 none 并且 z-index 都是 0。这里展示「层叠」的状态是为了更形象的展示。

swiper 原理图

为了方便获取页面,我们采用双向链表保存页面结构。每个 page 具有 prev 和 next 分别指向上一个和下一个 page。

我们重点要关注的是,怎么样确定 activePage ?即下一个要去到的页面。答案很简单,其实,当用户开始触碰屏幕,并且滑动的时候,就能确定了:

  1. 滑动距离 x < 0,表示页面向上滑动,此时 activepage = currentPage.next
  2. 滑动距离 x > 0,表示页面向下滑动,此时 activepage = currentPage.prev

扩展

翻页效果

我们举的例子中的翻页效果是最普通的滑动效果。怎么样扩展支持立方体、翻转等效果呢?可以回头看看「手指离开屏幕前」部分,我们提出了 s=f(x)

s=f(x),x

x 是用户滑动距离,s

s 是页面滑动距离。我们把 s

s 扩展一下,变成「页面翻转角度」或「页面缩放比率」,就可以支持其他的效果了。

事实上,我们在滑动的时候,本身就是使用 css3 的 transform 属性,将其中的 translate, rotate, scale 适当的组合就能做出千变万化的翻页效果了。

更令人愉悦的动画

这里指的是 animation-timing-function,拿最简单的滑动效果举例。如果是线性的函数,用户滑动的速度始终等于页面滑动速度。而「感觉上」更流畅、更灵敏的应该是:刚开始页面滑动速度大于用户滑动速度,随着翻页的进行,两者趋于相同,过了某个点后,单位时间内,页面滑动速度开始逐渐小于用户滑动速度,将速度表示为距离,就可以得到 x

x 和 s

s 之间的关系如下图:

x

x 和 s

s 的关系图(横轴为 x

x,纵轴为 s

s)

在这里,不得不再提起两种动画方案: js 动画和 css 动画。

js 动画方案的一个优点是,可以精确控制动画的进程,而 css 无法做到。比如用户在 x = 0.8 的时候手指离开屏幕,因为采用的同一个 render,js 可以知道手指离开屏幕的瞬间 x 处于 0.8 的位置,接下来的动画由 requestAnimationFrame 完成,整个过程流畅且完整。

而 css 动画则不同,css 动画只有在动画开始之前设定 animation-timing-function,当用户在 x = 0.8 手指离开屏幕时,原本的 js 控制滑动过程中断,由 css 来完成剩余的动画,css 无法根据手指离开屏幕的瞬间动态计算 animation-timing-function ,所以在衔接的那个点,两者速度不匹配,会影响整体动画效果。

但遗憾的是,js 的动画方案有性能问题,我们在用户手指离开屏幕后的那一部分只能采取 css 动画方案。这个「更令人愉悦的动画」也只能用在手指滑动期间。

总结和展望

本文讲述了一个「丝般顺滑」的 H5 翻页库的开发过程中遇到的一些问题和对应的解决方法。基本的滑动翻页模型建立之后,重点关注了性能的问题,分为手指离开屏幕前手指离开屏幕后两个阶段。前一阶段主要聚焦于减少 DOM 操作。后一阶段聚焦于动画的性能,并且对比了 js 动画和 css 动画的性能数据,最后得出了在手指离开屏幕后使用 css 动画的结论。此外,我们还基于「数据驱动」的思想,在翻页效果动画函数两部分进行了扩展,增强了翻页库的功能,也丰富了 H5 的展现效果。

本文中尝试用「数据驱动」的思想去解释整个过程,但是因为性能问题只能暂时放弃,希望在未来能找到更好的方案。由于水平所限,文中难免会出现纰漏,欢迎大家批评指正,共同学习进步。感谢 Swiper 和 islider 翻页库的启发,特别感谢和 @Ronny 的热烈讨论。

本文所述的 swiper 库地址:https://github.com/fex-team/swiper。master 分支所用的代码是目前百度 H5 线上使用的。raf 分支是文中提到的使用 js 动画方案。

希望本文能帮助到您!

点赞+转发,让更多的人也能看到这篇内容(收藏不点赞,都是耍流氓-_-)

关注 {我},享受文章首发体验!

每周重点攻克一个前端技术难点。更多精彩前端内容私信 我 回复“教程”

原文链接:http://fex.baidu.com/blog/2017/10/build-a-silky-smooth-slide-library/

作者:zhangbobell

篇给大家介绍了,控件测试、界面及易用性、可靠性测试,今天我们一起来看看翻页控件、输入框、搜索框、增删改功能试。

通用测试点——翻页控件

上一页、下一页、尾页。   

2、总页数,当前页数   

3、指定跳转页   

4、指定每页显示条数   

当然,有一些是少于多少页,全部以数字的形式显示,多于多少页后,才出现下一页的控件。本文暂且用以上四点来作为通用的用例来设计吧。   

对于1翻页链接或按钮的测试,主要要检查的测试点有:   

1、有无数据时控件的显示情况   

2、在首页时,首页和上一页是否能点击   

3、在尾页时,下一页和尾页是否能点击   

4、在非首页和非尾页时,四个按钮的功能是否正确   

5、翻页后,列表中的记录是否按照指定的排序列进行了排序   

对于2总页数,当前页数,主要要检查的测试点有:   

1、总页数是否等于总的记录数/指定每页条数   

2、当前页数是否正确   

对于3指定跳转页,主要要检查的测试点有:   

1、是否能正常跳转到指定的页数   

2、输入地跳转页数非法时的处理   

对于4指定每页显示条数,主要要检查的测试点有:   

1、是否有默认地指定每页显示条数   

2、指定每页的条数后,列表显示的记录数,页数是否正确   

3、输入的每页条数非法时的处理   

分析完上面的测试点,应该可以进行用例的设计了。   

step 1: 列表无记录   

expect:

1、四个翻页控件变灰不可点击   

2、列表有相应的无数据信息提示   

3、不可指定页数   

4、不可指定跳转页   

5、总页数显示为0   

6、当前页数显示为0   

step 2: 列表的记录数<=指定的每页显示条数   

expect:

1、四个翻页控件变灰不可点击   

2、总页数显示为1   

3、当前页数显示为1   

step 3: 列表的记录数>指定的每页显示条数   

expect:

1、默认在首页,当前页数为1   

2、列表的数据按照指定的排序列正确排序   

3、记录数与数据库相符   

4、总页数=记录数/指定的每页显示条数   

step 4: 列表的记录数>指定的每页显示条数,在首页   

expect: 1、首页变灰不可点击   

2、上一页变灰不可点击   

3、下一页可点击,从(每页指定条数+1)条记录开始显示,当前页数+1   

4、尾页可点击,显示最后页的记录   

step 5: 列表的记录数>指定的每页显示条数,在中间的某页   

expect: 1、首页可点击,显示1到每页指定条数的记录   

2、上一页可点击,显示上一页的记录   

3、下一页可点击,从后一页的记录   

4、尾页可点击,显示最后页的记录   

5、列表的数据按照指定的排序列正确排序   

6、当前页数为所在页   

step 6:列表的记录数>指定的每页显示条数,在尾页   

expect: 1、首页可点击,显示1到每页指定条数的记录   

2、上一页可点击,显示上一页的记录   

3、下一页变灰不可点击   

4、尾页变灰不可点击   

5、列表的数据按照指定的排序列正确排序   

6、当前页数为最后一页的页数   

step 7:输入每页显示条数为正整数   

expect: 1、每页显示条数更新成指定的条数   

2、超过指定的条数的记录分页显示   

3、总页数更新成列表的记录数/每页显示条数   

step 8:输入每页显示条数为0   

expect: 1、提示“每页显示条数必须为大于1的整数”   

2、提示后每页显示条数恢复为上次生效的条数   

step 9:输入每页显示条数为负数   

expect: 1、提示每页显示条数必须为大于1的整数   

2、提示后每页显示条数恢复为上次生效的条数   

step 10:输入每页显示条数长度超过数据库指定的长度<<>>   

expect: 1、提示每页显示条数不能超过<<>>位   

2、提示后每页显示条数恢复为上次生效的条数   

step 11:输入每页显示条数为字符串,如中文翻页数   

expect: 1、提示每页显示条数必须为大于1的整数   

2、提示后每页显示条数恢复为上次生效的条数   

step 12:输入每页显示条数为特殊字符,如%   

expect: 1、提示每页显示条数必须为大于1的整数   

2、提示后每页显示条数恢复为上次生效的条数   

step 13:输入每页显示条数为html字符串,如   

expect: 1、提示每页显示条数必须为大于1的整数   

2、提示后每页显示条数恢复为上次生效的条数   

step 14:输入跳转的页数为存在的页数   

expect: 1、正确跳转到指定的页数   

step 15:输入跳转的页数不存在或非法值   

expect: 1、跳转的页数值置为1,显示第一页的数据   

通用测试点——输入框

必填项

1:必填项未填写时系统会如何处理,是否有提示信息

2:必填项是否有“*”注释

3:对必填项提示返回后,光标是否自动定位到必填项

输入光标

1:在光标停留的地方输入信息,光标和所输入的信息是否有跳转

输入内容

1:输入各种标点符号,系统如何处理

2:特殊字符,html标签,sql注入,系统如何处理

3:输入一个或多个空格

4:输入的关键字中含有一个或多个空格(前空格,中空格,后空格)

5:输入超长字符,系统如何处理

6:输入纯中文,纯英文,纯数字,组合输入

7:输入信息和输出信息是否一致

8:大小写混合的情况

9:字体颜色是否合适

易用性

1:有输入或修改限制的,是否有明确的提示

2:对于约束条件的,当在满足条件后,状态应及时改变

3:是否支持复制黏贴

通用测试点——搜索框

常见需求:

用户通过搜索框搜索自己想要看的内容,前端进行数据展示

输入格式:任意内容

二、手机号输入框

常见需求

注册账号、添加手机号码、反馈、绑定手机号等模块

输入格式:只允许输入数字/任意内容

三、图片上传

常见需求

用户上传本地的图片

常见格式:jpg、png、jpeg、gif、bmp

四、表单

常见需求

包含了单选框、复选框、全选框、下拉框

因为上述控件常见且在表单里也经常有,所以都归属到表单控件里,但可单独提出来复用

通用测试点——增删改功能

增删改功能测试点如下:

一般检查

1:添加的数据项合理检查,DB中是否添加了相应的数据

2:不能保存时,检查DB中是否多数据或有数据是否被修改

3:添加项是必填,修改也应为必填项

4:添加项为int,修改也应为int类型

重复性

1:是否区分大小写

2:输入内容前后有空格的处理

3:把不能重名的改成已存在的内容,系统如何处理

4:提交后,返回再提交,系统如何处理

删除

1:删除DB中的数据,应弹框提示确认删除信息,确认后,给出删除成功的信息,且页面不存在这条数据,建议此处的删除为逻辑删除。

2:不选择任何项,点击删除,看是否有错误提示,且DB中无数据被删除

3:某项删除成功后,如果页面没刷新,继续删除,看系统如何处理

4:删除一个已被使用的记录,应不允许删除,给于提示,且DB中未删除

5:删除一个有关联的记录

6:删除一个有权限控制的记录

希望本文对你有所帮助~~如果对软件测试、接口测试、自动化测试、面试经验交流感兴趣可以私聊我或关注公众号“特斯汀软件测试”。免费领取最新软件测试大厂面试资料和Python自动化、接口、框架搭建学习资料!技术大牛解惑答疑,同行一起交流。

杰报表web端展示的时候,表下方工具条里有翻页按钮。如果报表只有一页,想让工具条上的翻页按钮不显示,该如何实现呢?

这个需求可以根据皕杰报表提供的api写自定义工具条按钮来实现。

① 参考帮助文档-开发指南-javadoc-bios.report.api.factory-ToolbarButtonFactory这个类重写replacePaginateArea这个方法。

② 将这个自定义工具条按钮类打成jar包放到皕杰报表环境web应用下/WEB-INF/lib文件夹下(或者如果是class文件放到web应用下/WEB-INF/classes)。

③ 然后修改皕杰报表环境web应用下WEB-INF\resources\report-config.xml里的〈toolbarButtonFactory〉〈/toolbarButtonFactory〉节点值,节点值是包名.类名(package.class,demo.factory.MyToolbarBtnFactory )

〈!-- 用户自定义接口配置,包括工厂、事件等 --〉

〈userDef〉

〈!-- 〈reportFactory〉〈/reportFactory〉 --〉 〈!-- 报表工厂,用于自定义报表加载和计算等 --〉

〈!-- 〈dataSourceFactory〉〈/dataSourceFactory〉--〉 〈!-- 自定义数据源工厂,用于通过自定义的方式获取报表数据源 --〉

〈!-- 〈toolbarFactory〉〈/toolbarFactory〉 --〉 〈!-- 工具条工厂,用于自定义html工具条 --〉

〈toolbarButtonFactory〉demo.factory.MyToolbarBtnFactory〈/toolbarButtonFactory〉 〈!-- 工具条按钮工厂,用于替换html工具条上的一些按钮 --〉

〈!-- 〈printEventListener〉〈/printEventListener〉 --〉 〈!-- 打印事件监听器 --〉

〈!-- 〈fillinEventListener〉〈/fillinEventListener〉 --〉 〈!-- 填报事件监听器 --〉

〈!-- 〈exportEventListener〉〈/exportEventListener〉 --〉 〈!-- 报表导出事件监听器 --〉

〈!-- 〈reportLogHandler〉〈/reportLogHandler〉 --〉 〈!-- 自定义报表日志输出处理器,在报表系统日志的基础上,增加其他日志记录方式 --〉

〈/userDef〉

1、代码示例

2、示例效果