1. 简介

“甩出”效果是当我们快速拖动container并松开后,container继续朝原方向运动,但是渐渐减速直到停止的效果。

ScrollView的onTouchEnded方法会设置Timer,间隔0、延迟0、无限次数,回调函数是deaccelerateScrolling方法。说明触摸结束时,当该方法不被unschedule时将每帧执行一次。

2. setContentSize

先看一个和“甩出”有关的方法setContentSize:

ScrollView的setContentSize重写了Node的方法,设置的是container的尺寸。

注意,还调用了updateInset方法,执行:

  _maxInset = this->maxContainerOffset();
  _maxInset.set(_maxInset.x + _viewSize.width * INSET_RATIO, _maxInset.y + _viewSize.height * INSET_RATIO);
  _minInset = this->minContainerOffset();
  _minInset.set(_minInset.x - _viewSize.width * INSET_RATIO, _minInset.y - _viewSize.height * INSET_RATIO);

设置了_maxInset:maxContainerOffset的值加部分可视范围。_minInset:minContainerOffset减部分可视范围。

INSET_RATIO为0.2,可以自行修改。

_maxInset和_minInset相当于扩大了偏移范围,并且只在有回弹效果时才有用处。

3. deaccelerateScrolling

情况一:开启回弹,不执行setContentSize

deaccelerateScrolling方法中会判断ScrollView是否设置了回弹效果。

我们先看有回弹的情况,如果不对ScrollView执行setContentSize,这样的话maxInset和minInset均为0。

    if (_bounceable) //有回弹则true
{
maxInset = _maxInset;
minInset = _minInset;
}

接下来设置container位置,代码不再粘贴。

然后,该if语句第二第三个条件永远满足,那么将会执行unschedule,说明了deaccelerateScrolling将会只执行这一次。之后,当container此时超出范围时,再通过relocateContainer方法设置回弹效果,不再赘述。

if (
  (fabsf(_scrollDistance.x) <= SCROLL_DEACCEL_DIST && fabsf(_scrollDistance.y) <= SCROLL_DEACCEL_DIST) ||
  ((_direction == Direction::BOTH || _direction == Direction::VERTICAL) && (newY >= maxInset.y || newY <= minInset.y)) ||
  ((_direction == Direction::BOTH || _direction == Direction::HORIZONTAL) && (newX >= maxInset.x || newX <= minInset.x))
)
{
  this->unschedule(CC_SCHEDULE_SELECTOR(ScrollView::deaccelerateScrolling));
  this->relocateContainer(true);
}

因为deaccelerateScrolling仅执行一次,仅在设置一次container的位置,所以没有“甩出”效果。

情况二:没有回弹

当没有回弹时,maxInset和minInset被设置成container偏移范围的界限。

if (_bounceable) //false
{
//...
}
else
{
maxInset = this->maxContainerOffset();
minInset = this->minContainerOffset();
}

那么,如果拖动在范围之内,接下来if判断的3个条件中后两个将为false,我们看第一个条件:

  (fabsf(_scrollDistance.x) <= SCROLL_DEACCEL_DIST && fabsf(_scrollDistance.y) <= SCROLL_DEACCEL_DIST)

_scrollDistance在if之前执行了:

  _scrollDistance = _scrollDistance * SCROLL_DEACCEL_RATE;

SCROLL_DEACCEL_DIST是一个界限,当_scrollDistance绝对值小于它时,使得if第一个条件满足,执行unschedule,deaccelerateScrolling将不会在下一帧执行。

onTouchMoved结束时保存了_scrollDistance,该向量是两次Moved之差。在触摸结束后,每帧执行deaccelerateScrolling时,_scrollDistance都会乘以小于0的系数SCROLL_DEACCEL_RATE,使得_scrollDistance渐渐变小。

并且,每次deaccelerateScrolling方法开始会根据当前_scrollDistance设置container的新位置:

  _container->setPosition(_container->getPosition() + _scrollDistance);

到这里,显而易见,虽然触摸结束了,但是deaccelerateScrolling将会在触摸结束后每帧执行,设置container的新位置,而每帧位置的增长都渐渐变小,实现了“甩动甩出”的效果。

当_scrollDistance小于界限值时,将会unschedule销毁Timer,deaccelerateScrolling不会在下一阵执行,我们看到的“甩出”效果就结束了。

还有一个问题,在无回弹情况下,如果“甩出”时container到了边界是如何处理的?

看deaccelerateScrolling部分代码:

    _container->setPosition(_container->getPosition() + _scrollDistance); //假设此时设置位置后越界

    if (_bounceable) //false
{
//...
}
else
{
maxInset = this->maxContainerOffset();
minInset = this->minContainerOffset();
} newX = _container->getPosition().x;
newY = _container->getPosition().y; _scrollDistance = _scrollDistance * SCROLL_DEACCEL_RATE;
this->setContentOffset(Vec2(newX,newY)); //看

container在setPosition后又用setContentOffset方法设置了一次位置。

看setContentOffset部分代码:

        if (!_bounceable)
{
const Vec2 minOffset = this->minContainerOffset();
const Vec2 maxOffset = this->maxContainerOffset(); offset.x = MAX(minOffset.x, MIN(maxOffset.x, offset.x));
offset.y = MAX(minOffset.y, MIN(maxOffset.y, offset.y));
} _container->setPosition(offset); if (_delegate != nullptr)
{
_delegate->scrollViewDidScroll(this);
}

当没有越界时,确实是执行了两次参数一样的setPosition。

而越界时,位置会被修正为边界位置setPosition。

同时,因为修正前的坐标已经越界,deaccelerateScrolling方法最后会触发unschedule,“甩出”效果终止。

情况三:开启回弹,执行setContentSize

因为执行了setContentSize,maxInset和minInset不是0,而是比maxContainerOffset()和minContainerOffset()计算的值扩大了部分可视范围。

本情况中偏移范围扩大了,是为了当container“甩出”时,允许在一定程度内超出maxContainerOffset()和minContainerOffset()范围。当“甩出”过了一定程度,会触发if三条件中的越界条件,从而执行unschedule,再执行relocateContainer设置回弹效果。

4. 总结

onTouchEnded设置了一个在触摸结束后每帧执行的Timer。

当有“甩出”效果时,对“甩出”时每帧之间container的距离间隔设为比上一帧缩小,实现了每帧移动的距离慢慢减小,直到到达临界点停止。

当没有“甩出”效果时,deaccelerateScrolling仅执行一次,设置一次container的位置。

‎Cocos2d-x 学习笔记(21.1) ScrollView “甩出”效果与 deaccelerateScrolling 方法的更多相关文章

  1. Ext.Net学习笔记21:Ext.Net FormPanel 字段验证(validation)

    Ext.Net学习笔记21:Ext.Net FormPanel 字段验证(validation) 作为表单,字段验证当然是不能少的,今天我们来一起看看Ext.Net FormPanel的字段验证功能. ...

  2. SQL反模式学习笔记21 SQL注入

    目标:编写SQL动态查询,防止SQL注入 通常所说的“SQL动态查询”是指将程序中的变量和基本SQL语句拼接成一个完整的查询语句. 反模式:将未经验证的输入作为代码执行 当向SQL查询的字符串中插入别 ...

  3. GIS案例学习笔记-ArcGIS整图大图出图实例教程

    GIS案例学习笔记-ArcGIS整图大图出图实例教程 联系方式:谢老师,135-4855-4328,xiexiaokui#qq.com 1. 通过出图比例尺(1:2000),地图范围测算图纸大小. 图 ...

  4. C#数字图像处理算法学习笔记(一)--C#图像处理的3中方法

    C#数字图像处理算法学习笔记(一)--C#图像处理的3中方法 Bitmap类:此类封装了GDI+中的一个位图,次位图有图形图像及其属性的像素数据组成.因此此类是用于处理像素数据定义的图形的对象.该类的 ...

  5. ‎Cocos2d-x 学习笔记(21) ScrollView (CCScrollView)

    1. 简介 CCScrollView.cpp文件内的滚动视图ScrollView直接继承了Layer+ActionTweenDelegate. 滚动视图能在屏幕区域内,用户通过触摸拖动屏幕,实现大于屏 ...

  6. ArcGIS API for JavaScript 4.2学习笔记[21] 对3D场景上的3D要素进行点击查询【Query类学习】

    有人问我怎么这个系列没有写自己做的东西呢? 大哥大姐,这是"学习笔记"啊!当然主要以解读和笔记为主咯. 也有人找我要实例代码(不是示例),我表示AJS尚未成熟,现在数据编辑功能才简 ...

  7. [原创]java WEB学习笔记21:MVC案例完整实践(part 2)---DAO层设计

    本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...

  8. JNI学习笔记_Java调用C —— 非Android中使用的方法

    一.学习笔记 1.java源码中的JNI函数本机方法声明必须使用native修饰. 2.相对反编译 Java 的 class 字节码文件来说,反汇编.so动态库来分析程序的逻辑要复杂得多,为了应用的安 ...

  9. Dynamic CRM 2013学习笔记(十七)JS读写各种类型字段方法及技巧

    我们经常要对表单里各种类型的字段进行读取或赋值,下面列出各种类型的读写方法及注意事项: 1. lookup 类型 清空值 var state = Xrm.Page.getAttribute(" ...

随机推荐

  1. 关于设置tomcat端口为80的事

    今天有人要求tomcat服务器的访问地址不能带端口访问, 也就是说只能用80端口访问网站. 那么问题来了, Ubuntu系统禁止root用户以外的用户访问1024以下的商品, 因此tomcat 默认为 ...

  2. 松软科技课堂:SQL--RIGHTJOIN关键字

    发布时间:2019/3/15 9:27:31 SQL RIGHT JOIN 关键字 RIGHT JOIN 关键字会右表 (table_name2) 那里返回所有的行,即使在左表 (table_name ...

  3. 网络编程之socket模块

    一.TCP协议 TCP是可靠的.面向连接的协议(eg:打电话).传输效率低全双工通信(发送缓存&接收缓存).面向字节流.使用TCP的应用:Web浏览器:电子邮件.文件传输程序. 二.基于TCP ...

  4. 01:***VideoToolbox硬编码H.264

    最近接触了一些视频流H264的编解码知识,之前项目使用的是FFMpeg多媒体库,利用CPU做视频的编码和解码,俗称为软编软解.该方法比较通用,但是占用CPU资源,编解码效率不高.一般系统都会提供GPU ...

  5. [Leetcode] 第309题 最佳买卖股票时机含冷冻期

    一.题目描述 给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 .​ 设计一个算法计算出最大利润.在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票): 你不能同时参与 ...

  6. webstorm中关闭烦人Eslint语法检查

    打开许久没打开的webstrom,以前关闭的配置不知道怎么又乱了,react项目到处报错,真是没法忍. 关闭eslint位置:File-->Setting-->Languages& ...

  7. jenkins自动化部署项目1--下载安装启动(windows)

    年初以来断断续续研究jenkins自动化部署项目,前些天终于搞定了,接下来一点点把做的时候遇到的坑以及自己的心得写下来,方便以后复用. 我的jenkins服务是是部署在windows上的 一.下载安装 ...

  8. linux系统下开发环境安装与配置

    安装系统环境 CentOS 6.8 64位 jdk版本 7u80 64位 Tomcat版本 Tomcat7 maven版本 Apache Maven 3.6.0 vsftpd版本 vsftpd-2.2 ...

  9. TypeScript中使用getElementXXX()

    如果只是看解决方法,可以直接跳到第二小节 简述 Angular 1.x版本是用JavaScript编写的,我们在百度Angular经常会搜索到AngularJS,并不是JavaScript的什么衍生版 ...

  10. springboot 集成Redis单机

    1.redis服务搭建 centos7 搭建redis服务 2.接入相关 pom文件依赖引入 <dependencies> <dependency> <groupId&g ...