移动UI系列 - 简单地使用半衰期算法来预测手势的滑动方向与速度
前言
有一个问题, 给定一个物体的运动轨迹, 包含时间和坐标的数组, 如何使用这个数据来预测物体未来的运动走势??
本文提供了一个很简单的方式去实现这个算法. 效果够用, 又简单, 有一定的准确程度.
缘由
以前做过的一些手机应用, 没有做动画的 , 也没做手势. 这个做起来挺麻烦的.
最近开始了新的手机项目 (微信项目模板) , 基于 Blazor server side , 其组件化的方式可以很方便地把各种常用的通用的东西封装成 组件/控件
于是, 就打算让这段时间辛苦一些, 一次过把动画的部分补上, 让以后的项目更加牛逼, 意思是让客户更加乐意地掏钱.
问题
手势的一个问题是力度/速度判断, 划得快, 划得慢, 先快后慢, 先慢后快, 都得有一个合适的算法来得到一个最后速度.
这个算法一定要满足基本的需求后, 要尽量简单, 要目测, 理论上没有bug .
这个时候, 半衰期算法就派上用场了. (不知道有没有半衰期算法这玩意, 应该说是使用半衰期的原理)
原理
这是一个很简单的权重计算, 有很多个不同时间点的坐标, 每个相邻时间的两个数据, 有距离差, 有时间差
关键是解决先慢后快, 先快后慢的数据的处理问题, 那么我们只需要
把新的数据, 乘以更大的权重, 把老的数据, 乘以更小的权重 , 最后得到这个距离权重的合成值, 就OK了.
预览效果:
注意, 因为gif的帧数不够, 要更好效果还是复制代码运行一遍.

测试代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Half Life</title>
<style>
html, body, canvas {width: 100%;height: 100%;margin: 0;padding: 0;box-sizing: border-box;overflow: hidden;}
</style>
</head>
<body> <canvas></canvas> <script type="text/javascript"> var CONST_HALF_LIFE_SPAN = 50; //半衰期 , 设置得越小 , 就越看淡以前的速度
var CONST_MAX_SAMPLES = 15; //保留最后15个点的数据 减少无意义的运算量, 主要看浏览器触发 onmousemove 的频率来调整. var pts = [];
document.onmousemove = function (e) { //储存数据
pts.push({ time: Date.now(), x: e.clientX, y: e.clientY });
if (pts.length > CONST_MAX_SAMPLES) pts.shift(); //计算 var xs = 0, ys = 0; //走过的路的长度.
var previtem = pts[0];
for (var index = 1; index < pts.length; index++) {
var newitem = pts[index];
var t = newitem.time - previtem.time; //让这个数据衰减一次, 衰减程度由时间差决定.
var halflifefactor = Math.pow(0.5, t / CONST_HALF_LIFE_SPAN); //注意, 这里没有计算速度, 而是直接用距离来预测将来要走过的距离. // 走过的距离每一次都要衰减, 每一段的路程都多次乘以各时间差的factor,
// 原理是, 0.5 ^ (t1 + t2 + t3...) 等于 0.5 ^ t1 * 0.5 ^ t2 * 0.5 ^ t3 * ...
xs = xs * halflifefactor + newitem.x - previtem.x;
ys = ys * halflifefactor + newitem.y - previtem.y; previtem = newitem;
} //画图 var CONST_EFFECT_FACTOR = 2; //乘以一个因素来画图用, 这里数值可以充当'摩擦系数'大小的效果.
xs = Math.floor(xs * CONST_EFFECT_FACTOR);
ys = Math.floor(ys * CONST_EFFECT_FACTOR); var x0 = e.clientX, y0 = e.clientY; var canvas = document.querySelector("canvas");
var ctx = canvas.getContext("2d");
var w = canvas.width = canvas.offsetWidth;
var h = canvas.height = canvas.offsetHeight;
ctx.clearRect(0, 0, w, h);
ctx.lineWidth = 5;
ctx.strokeStyle = "blue";
ctx.beginPath();
ctx.moveTo(x0, y0);
ctx.lineTo(x0 + xs, y0 + ys);
ctx.closePath();
ctx.stroke(); console.log(xs, ys)
}
</script> </body> </html>
简单讲解
可以看出 , 代码量非常少. 与其说这是一种"算法" , 不然说是一种"思路"
代码先是记录每一点的数据, 然后放进 pts 数组
在鼠标移动后, 使用 pts 数组, 计算出每一点的 x , y 的移动量, 让每一段的移动量都使用 半衰期 的方式进行调整.
最后得出的 xs , ys , 是理论上 "最近移动的距离."
使用这个数值, 作为 "参考值" , 就可以用于更多的判断.
例如我最近做的一个简单的 swipe 组件 ,
手指滑动了 1/4 个屏幕, 然后再加上理论的参考值, 一共超过了 1/2 个屏幕, 就切换到下一个panel
这个参考值很重要. 如果手指只是很慢地滑动, 那么参考值就很小, 就不应该切换. 如果手指很快地滑动, 那么就应该切换panel
可改良的方案
上面方案, 是计算 '移动距离' 的, 它有一个弊端, 无论划得多快, 移动的总数是有上限的.
这段代码完全可以 除以 t , 得到一个 速度值,
这更加合理, 但是注意速度的合成方式不能普通地累加.
这就留给有兴趣的网友自己尝试了. 也不难. 毕竟本文传播的是 半衰期 的思路. 不宜说太细.
扩展思路
其实这种算法 , 一直都有人用. 很奇怪的就是, 没有看到什么人专门写文章介绍?
半衰期除了计算运动轨迹, 还可以很好地去统计热度.
例如一篇文章 , 一个视频, 有不同时段的点击数量. 每一天都不一样.
怎样使用最少的储存方式, 去储存一个合理的热度参考值?
可行的方法是 , 储存两个数值 (就够了) :
articleRateValue 用于储存热度值
articleRateTime 用于储存热度时间
每一次点击, 都使用这个公式储存 :
articleRateValue = newclickcount + articleRateValue * POW(0.5, (NOW - articleRateTime) / HALF_LIFE_SPAN )
articleRateTime = NOW
排序的时候麻烦点, 要实时的计算 articleRateValue * POW(0.5, (NOW - articleRateTime) / HALF_LIFE_SPAN ) 来得到每个文章的热度值.
(对于排序的问题, 还有一个变通的做法, 如果以后有时间写一篇文章细说这个热度方案, 再详细解说)
注意这里有一个 HALF_LIFE_SPAN 的概念. 如果 HALF_LIFE_SPAN 是一天, 那么昨天的热度就占 1/2 权重, 前天的就占 1/4 权重 , 大前天的占 1/8
这样按天算也有个弊端 , 我想按月算那怎么办 ?
再存两个数值 :
monthlyRateValue
monthlyRateTime
如此类推.
这样做的最大好处是 , 无需详细地纪录每一次的点击数据. 非常节省空间.
缺点是 , 每一组数据, 只适合一个半衰期时段的数值, 要储存多个参考值得准备多组数据.
最后
时间过得真快. 这段时间挖的坑太多, 在业余时间内根本没有时间填坑..
5月初的时候实现了BlazorCefApp , 到现在开了几个有意义的坑, Blazor微信项目模板 算是一个.
但是由于没有时间写博客, 有时只是偶尔把测试的视频放到 B站 : https://space.bilibili.com/540073960
有兴趣用 Blazor 来做微信项目或手机网页项目的, 可以去了解一下. 当项目成熟后, 会发布到github上.
----
移动UI系列 - 简单地使用半衰期算法来预测手势的滑动方向与速度的更多相关文章
- Android UI系列-----时间、日期、Toasts和进度条Dialog
		
您可以通过点击 右下角 的按钮 来对文章内容作出评价, 也可以通过左下方的 关注按钮 来关注我的博客的最新动态. 如果文章内容对您有帮助, 不要忘记点击右下角的 推荐按钮 来支持一下哦 如果您对文章内 ...
 - 【转】Android UI系列-----时间、日期、Toasts和进度条Dialog
		
原文网址:http://www.cnblogs.com/xiaoluo501395377/p/3421727.html 您可以通过点击 右下角 的按钮 来对文章内容作出评价, 也可以通过左下方的 关注 ...
 - 分布式理论系列(二)一致性算法:2PC 到 3PC 到 Paxos 到 Raft 到 Zab
		
分布式理论系列(二)一致性算法:2PC 到 3PC 到 Paxos 到 Raft 到 Zab 本文介绍一致性算法: 2PC 到 3PC 到 Paxos 到 Raft 到 Zab 两类一致性算法(操作原 ...
 - WeX5的简单介绍及UI的简单讲解
		
WeX5的简单介绍及UI的简单讲解 (2016-01-13 14:49:05) 标签: it 分类: WeX5的初步自学 一.WeX5的简单讲解 1.WeX5是前端快速开发框架,可开发跨端运行应用.是 ...
 - iOS开发UI篇—简单的浏览器查看程序
		
iOS开发UI篇—简单的浏览器查看程序 一.程序实现要求 1.要求 2. 界面分析 (1) 需要读取或修改属性的控件需要设置属性 序号标签 图片 图片描述 左边按钮 右边按钮 (2) 需要监听响应事件 ...
 - iOS开发UI篇—简单介绍静态单元格的使用
		
iOS开发UI篇—简单介绍静态单元格的使用 一.实现效果与说明 说明:观察上面的展示效果,可以发现整个界面是由一个tableview来展示的,上面的数据都是固定的,且几乎不会改变. 要完成上面的效果, ...
 - 简单易学的机器学习算法——EM算法
		
简单易学的机器学习算法——EM算法 一.机器学习中的参数估计问题 在前面的博文中,如“简单易学的机器学习算法——Logistic回归”中,采用了极大似然函数对其模型中的参数进行估计,简单来讲即对于一系 ...
 - Lance老师UI系列教程第八课->新浪新闻SlidingMenu界面的实现
		
UI系列教程第八课:Lance老师UI系列教程第八课->新浪新闻SlidingMenu界面的实现 今天蓝老师要讲的是关于新浪新闻侧滑界面的实现.先看看原图: 如图所示,这种侧滑效果以另一种方式替 ...
 - 简单的理解deflate算法
		
简单的理解deflate算法 最近做压缩算法. 用到了deflate压缩算法, 找了很多资料, 这篇文章算是讲的比较易懂的, 这篇文章不长,但却浅显易懂, 基本上涵盖了我想要知道的所有要点. 翻译 ...
 
随机推荐
- Java集合类: Set、List、Map
			
Set.List.Map都是集合接口 set --其中的值不允许重复,无序的数据结构 list --其中的值允许重复,因为其为有序的数据结构 map--成对的数据结构,健值必须具有唯一 ...
 - Centos7安装jupyter notebook
			
安装python3 查看当前python版本 [root@iz1i4qd6oynml0z /]# python -V Python 2.7.5 安装python3以及检查python3的版本 yum ...
 - 5.3 Go 匿名函数
			
5.3 Go 匿名函数 Go支持匿名函数,顾名思义就是没名字的函数. 匿名函数一般用在,函数只运行一次,也可以多次调用. 匿名函数可以像普通变量一样被调用. 匿名函数由不带函数名字的函数声明与函数体组 ...
 - nginx配置之状态模块和压力测试功能
			
状态模块功能 nginx.conf中的http{}中的server{}中: location /status { #开启nginx状态功能 stub_status on; } 直接在网页中请求stat ...
 - 第一章 Python 基础
			
1. 为什么学习 Python? 答题路线:a.python的优点,b.python的应用领域广 具体: 优点 1.python语法非常优雅,简单易学 2.免费开源 3.跨平台,可以自由移植 4.可扩 ...
 - 一个导致JVM物理内存消耗大的Bug
			
概述 最近我们公司在帮一个客户查一个JVM的问题(JDK1.8.0_191-b12),发现一个系统老是被OS Kill掉,是内存泄露导致的.在查的过程中,阴差阳错地发现了JVM另外的一个Bug.这个B ...
 - hdu4746莫比乌斯反演进阶题
			
Mophues Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 327670/327670 K (Java/Others)Total S ...
 - GYM100962A ABBA
			
题目链接:https://vjudge.net/problem/Gym-100962A 题目大意: 给出一个 \(h \times w\) 的目标矩阵.定义一种 \(h \times w\) 的矩阵, ...
 - SpringBoot2.x快速入门指南(一)
			
SpringBoot2.x快速入门指南(一) 准备工作 IDE: IntelliJ IDEA 2020.3 Java环境 jdk1.8 在官网快速创建SpringBoot项目 下面开始进入正题: 进入 ...
 - 花费一周刷完两份面试pdf(含答案)轻松拿下了抖音、头条、京东、小米等大厂的offer,成功度过程序员的寒冬。
			
整理出一篇Java进阶架构师之路的核心知识,同时也是面试时面试官必问的知识点,篇章也是包括了很多知识点,其中包括了有基础知识.Java集合.JVM.多线程并发.spring原理.微服务.Netty 与 ...