β

使用web模拟手Q水滴下拉刷新效果

兴趣部落页面中,为了加强下拉刷新的体验效果,我们用web模拟了ios下手Q下拉刷新时的水滴效果,实现效果如下。

2

页面开发已经很久,今天写这篇来总结一下吧。

实现原理

这里分为几个部分

实现的关键在于水滴的绘制部分,考虑到尺寸特别小,这里我们使用canvas来进行绘制

为什么是canvas

首先用CSS3来做的话会非常麻烦,很多东西不能直接控制,并且涉及到动画,所以基本不用考虑,SVG的话也是不错的选择,就是控制也不是很方便,Canvas就性能和控制性上都比较高,所以最终选择了Canvas来做关键帧的绘制。

水滴的绘制

如果仔细观查水滴是由三部分组成,两个半圆加中间连接线,如果两个半圆的端点用直线连起来的话,看起来过渡会很生硬,如下所示

所以我们要选择一个好一点的曲线来连接两个半圆。但实际上,水滴的绘制可以是完全用贝塞尔曲线绘制的

鉴于canvas使用三次贝塞尔曲线绘制比较复杂一点(其实也还好,三次贝塞尔曲线要求两个控制点,控制点选择坚直就好了,具体没有尝试,这可能是一个优化点),所以考虑用二次的贝塞尔曲线来连接两个半圆,所以问题变成是如何选择二次贝塞尔曲线的控制点

二次贝塞尔曲线

二次贝塞尔曲线的canvas Api大家可以在网上搜到

JavaScript 语法:

Crayon Syntax Highlighter v2.7.1
1
context.quadraticCurveTo(cpx,cpy,x,y);
[Format Time: 0.0017 seconds]

canvas

下面有一个动画图更能反映这个曲线的绘制过程

1426130734_8_w474_h593

控制点的选择其实也有很多种,这里我选用了两个连接点连线的垂直平分线上的点,如下图所示,两条蓝线是两条连线的垂直平分线,在这上面的点,会使弧线过渡很均匀

1426065675_6_w990_h876

这样做出来,在图像很小的情况下也很漂亮,只是连接点的不是连续的(左导 !=右导),但还可以接受。

所以按这种方法绘制水滴,用canvas这样绘制

Crayon Syntax Highlighter v2.7.1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
ctx.beginPath();
// 画顶圆
ctx.arc(topRound[0], topRound[1], topR, - Math.PI, 0);
// 计算控制点
// 先计算垂分线
// 找出垂分线上 向水滴里面的 一点做控制点
// 使用控制点画贝塞尔曲线到 底部圆连接点
ctx.quadraticCurveTo(point[0], point[1], bottomRound[0] + bottomR, bottomRound[1]);
// ...另一侧也是这样画
// 闭合
ctx.closePath();
// 填充
ctx.fill();
//画边
ctx.stroke();
[Format Time: 0.0021 seconds]

其中可能要计算垂直平分线,计算的原理比较简单,先计算出两个连接点p0, p1的斜率k,然后,计算出两点的中点pm,垂分线方程就是y – y pm =-1/k * (x – x pm ),然后再取靠中点向里一点的值x,计算y就可以了,这样控制点坐标就计算出来了

这里要特别说明的是,对于高清屏,canvas的绘制要用两倍宽高去绘,用css把canvas缩放回来

其他的一些参数如下

Crayon Syntax Highlighter v2.7.1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
canvas.width = WIDTH;
canvas.height = HEIGHT;
// 为高清屏缩放
canvas.style.width = ~~ (WIDTH / 2) + "px";
canvas.style.height = ~~ (HEIGHT / 2) + "px";
// 画笔和填充颜色的配置
ctx.fillStyle = "#b1b1b1";
ctx.strokeStyle = "rgb(118,113,108)";
// 为了让效果更好 设置上角的一点点高光
ctx.shadowColor = "#ccc";
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 2;
ctx.shadowBlur = 1;
// 画笔宽度为1
ctx.lineWidth = 1;
[Format Time: 0.0026 seconds]

画出来在手机上的效果如下
1426128335_58_w671_h581

左为原生,右为web实现

更细节的对比

1426128809_32_w592_h882

上为原生,下为web实

ICON贴图

中间的ICON就没有用canvas去画在上面,直接是DOM覆盖在canvas上

动画的实现

在IOS上,因为弹性滚动,某个DOM可以被拉下,其scrollTop可以为负值(-),我们就跟据被拉下元素的scrollTop的负值的大小,依次绘制出此时的水滴即可。

具体的关系,某个scrollTop值 对就一个 -dis,如果dis<A, 水滴是被拉下的过程,一旦dis超过A, 水滴开始变形,上圆和下圆的中心点开始存在距离d, 上圆半径R和下圆半径r都在随d增大减小,只不过下圆r随d减小的速度更快。 下面只是一种猜测的关系

Crayon Syntax Highlighter v2.7.1
1
2
topR = TOP_R - d * 0.1;
bottomR = BOTTOM_R - d * 0.3;
[Format Time: 0.0007 seconds]

同时,注意还要跟据这个关系变换那个刷新的ICON,不过这个就是DOM变换了,相对比较简单

Crayon Syntax Highlighter v2.7.1
1
reloadIcon.style.webkitTransform = "scale(" + (topR / TOP_R) + ")";
[Format Time: 0.0006 seconds]

拉到一定值,水滴消失,出现loading菊花,这就是普通的DOM操作 最后的回弹

注意到,如果仅仅是这样子,在放手那一刻还没来得及出刷新成' ;&,"vIE' ;&,"会让scrollTop为负值的DOM弹上去了,这还不能满足我们的需要

这就要放手那一刻,迅速给负值的DOM元素做CSS translate变换,使当前DOM保持在被拉下的位置

Crayon Syntax Highlighter v2.7.1
1
2
3
4
// 放手那时
// 监听touchend即可
// 不要拉在哪就停在哪 稍微弹回一点
$(scrollDom).css("-webkit-transition", "all ease-out 0.5s").css("-webkit-transform", "translateY(" + (currDis - 40) + "px)");
[Format Time: 0.0009 seconds]

直到刷新成功了,让它弹上去

Crayon Syntax Highlighter v2.7.1
1
2
// 弹回去吧 (上面代码已设置过transition属性)
$(scrollDom).css("-webkit-transform", "translateY(" + (0) + "px)");
[Format Time: 0.0006 seconds]

至此,代码的核心部分已经完成了,然后再做一些接口的封装就算彻底完工了。

关于Android

Android鉴于无法拉下负的scrollTop,而且Android的下拉样式不是这个样子的,所以这个方案就只用在ios上了。

作者:Web前端 腾讯AlloyTeam Blog | 愿景: 成为地球卓越的Web团队!
腾讯全端 AlloyTeam 团队 Blog
原文地址:使用web模拟手Q水滴下拉刷新效果, 感谢原作者分享。

发表评论