导数,微积分,牛顿运动学制作创意地图

大前端时代下,面向组件,面向数据编程已家常便饭,但是作为一个合格的程序员,优秀的逻辑思维和抽象能力是其最基本的素质。代码只是实现过程和编程功底的体现,更重要的是分析问题,建模实力,解决问题,才能实现技术创新,引领业务,改变世界

点击查看本文的第一个案例

一个优秀的程序员应该做到:

1. 观察归纳需求,抽象需求

2. 设计算法将需求数学化,建立合理的数学模型

3. 扎实的编程功底实现模型

我们首先归纳的应该有以下的细节:

1. 整个页面的动效交互只有滚动条触发

2. 两点之间是任意曲线,曲线的运动学是整个曲线方程的子集轨迹,也就是局部曲线

3. 由于滚动条有上下滚动两个方向,所以曲线的运动轨迹的长度随滚动条的高度成正比,即向下滚动,轨迹变长,向上滚动,轨迹变短,注意一点,轨迹长度的变化方向,和原曲线的方向一致

4. 飞机沿着曲线的运动轨迹而做曲线运动,注意一点,由于飞机是svg图片,而其运动轨迹是任意曲线,那么飞机的方向也必须跟随轨迹运动而实时变化,这一点非常重要!

5. 飞机在两点之间运动时,在该曲线的起点到1/2长度之间,飞机逐渐变大,在运动到超过1/2长度和自身长度之间,飞机又逐渐变小

6. 在曲线运动过程中,整个地图也发生运动,且地图的运动轨迹和当前时刻曲线的运动方向一致

7. 在滚动过程中,当左侧图文面板里出现图片,那么以飞机为三角形定点,左侧图片的上下两个点为三角形底边,作一个三角形光束照射在图片上,并且随着底边点的位置改变定点角度也随着变化

以上细节,是一个程序员最基本的观察力和对需求的归纳能力,那么接下来,开始动手编程?那是新手,接下来要做的应该是逐一分析技术算法细节,也是培养我们的建模能力

下面开始算法原理的分析:

1. 前端技术栈很明显,使用原生js和canvas技术就可以实现,这一点没什么可以说的

2. 曲线的制作可以利用三次贝塞尔方程(一切的计算都源于此方程)

三次贝塞尔曲线的参数方程

通过此方程,我们可以方便的求其偏导数以及任意时刻t对应的x和y值

3. 两点间一曲线,把一条曲线和4个点(绘制三次贝塞尔)抽象成一个类,这非常容易想到,而这个类里应封装基本的绘制方法,包含曲线、圆形点、飞机图形、三角形等,还应封装计算方法,包含根据任意时刻t计算相应的x和y值、根据任意x或y值反解出曲线对应的t值、求x和y的偏导数、求曲线上任意一点的斜率、求曲线的总长度

以上的分析依然很简单,只不过是oop和canvas的api的使用,接下来实际说明为什么需要封装这些计算方法

4. 绘制局部贝塞尔曲线:

我们知道使用canvas的api里bezierCurveTo(p0,p1,p2,p3)很容易画出一条完整的贝塞尔曲线,但是我们需要模拟的运动学,是沿着曲线做曲线运动,也就是需要绘制局部曲线,而canvas是没有给我们提供绘制任意局部曲线的api的,所以单纯依赖api是走不通的,我们需要从本质原理出发推理

我们知道两点可以组成一条直线,那么曲线呢?而任意一条线上有多少个点?不妨假设有n个点,那么如果这n个点的坐标不是线性的,那么将这n个点连接起来,就是一条完整的曲线,如果这n个点的坐标满足贝塞尔方程,那么组合起来就是一条完整的贝塞尔曲线

分析到这里,我们可以抽象出我们只要选择m个点(0=<m<=n),将这m个点连接起来,就构成了局部贝塞尔曲线(请思考为什么不只选取运动起点和运动终点来连线)

而新问题紧接着产生,我们设滚动条滚动的距离为a,怎么去计算出a所对应的方程的x和y?

因为我们浏览器的屏幕是笛卡尔坐标系,我们绘制任意一个点需要知道点的x坐标和y坐标,所以我们很容易想到不妨用a去映射方程终点p3对应的x,那么我们只需要求出p3的y值即可完成终点的绘制。观察方程,我们不能直接利用x去求y,需要先求出p3.x对应的t值,才能用t去计算p3.y

已知任意时刻t的方程,P0,P1,P2,P3为四个点,其中P0和P3是起点和终点,P1和P2是控制点

曲线方程

我们已知曲线终点的Px,求Py,那么两边除以t的三次方,整理得:

化简一

设 m = (1 - t) / t,即可以整理出:

化简二

我们知道这是一个标准的三次方程,利用三次求根公式可以解出m进而求出t,那么带入t和另外三个点的y值就可以求出p3的y值

但是这并不是一个非常好的做法,因为考虑我们绘制的是任意曲线,方程图像如果存在以下这种情况

一对多的情况

那么我们很难控制曲线到底应该画到第一个y值,还是第二个y值,所以综上分析,a去映射x求t来解y,会存在多解影响笛卡尔坐标绘制,故而正确的做法应该是用a去映射t,用t来解出x和y

那么问题继而转化为,a怎么去映射t?

我们可以容易的想到如果已知曲线的总长度L,用a和L的比率去计算局部dl,我们假设长度为L的曲线对应可视高度为H的值(当盒子完全被滚动条卷入,也就证明线走完了),可以得到dl = L / H * a,则此时此刻的t = dl / L (t 属于[0,1])

那么问题就转化为需要先求出任意曲线的长度L?

我们知道路程 = 速度 * 时间,基于微积分的思想,我们假设某个很小的时刻dt,存在速度dv,那么ds = dv * dt,最后我们对所有dt对应的ds进行求和,可以逼近曲线的总长度L(我们假设将曲线S划分为N段)

问题即又转化为怎么求速度?(这里指的是合速度)

很明显,我们需要对曲线方程对t进行求导,即得到其速度的方程式

求导过程非常简单(请复习高中复合函数求导)

速度方程

我们带入t求出两个方向的速度,很显然,一个是沿着x方向的速度vx,另一个是沿着y方向的速度vy,我们需要计算出二者产生的合力的速度v即可

根据平行四边形法则,我们知道v = (vx * vx + vy * vy) ^ 1/2,现在我们终于可以计算出曲线的长度L = ∑ v * dt,而a和H已知(单纯的网页dom计算,不题),我们就可以得到dl = L / H * a,最后得到t = dl / L,完成了a和t的映射关系

现在我们用a映射出t,并且可以保证t在0到1之间,根据t解出真正的x和y值,至此,我们终于可以画出局部曲线了

5. 局部曲线长度的动效变化:

当我们建立如何画局部曲线后,将要解决随着滚动条向上或向下滚动时候,局部曲线的长度变化过程。现在我们已经得到任一点的dt和该dt对应的x和y值,很显然想到利用canvas的LineTo(x, y)进行绘制连线即可

但是问题出现了,如果我们直接一步LineTo(x,y)到终点,会出现以下情况:

绘制成直线了

显然,我们不能一步就绘制起点和终点

那么问题又回到了绘制局部曲线的理论,我们将起点(dt = 0)和终点( dt = t)之间构造出N个dt,利用微分的思想绘制可以很方便的绘制出这段曲线

绘制真正的曲线

这段代码请自行感悟

绘制曲线的流程

至此我们只要知道a对应的dt,利用微分思想,就可以绘制任意方向的局部曲线动效

6. 飞机做曲线运动,且随着曲线方向实时旋转方向:

我们飞机是svg矢量图,利用canvas的drawImage非常容易绘制出飞机,但是问题是计算机是不知道飞机的机头要跟随其运动的方向旋绕,这是我们的所常识决定的,所以我们要利用计算机仿真这个常识

单纯的绘制飞机svg(不符合常识)


我们观察很容易发现飞机的旋转是沿着运动轨迹的切线方向,其正负值由运动方向决定,如果我们知道任意时刻dt的曲线切线与水平轴的夹角d,那么利用canvas的rotate(d)就可以改变飞机的旋转角度,并且保证其旋转的角度和曲线相切,就可以模仿这个常识

问题就抽象出计算曲线任意时刻dt的切线与水平轴的夹角?

计算切线,其实就是求导,而我们之前就已经计算好dt对应的两个方向的偏导数方程式,这里只需要计算两个运动方向的夹角即可,我们利用arctan vy / vx 就可以计算出该时刻切线与水平轴的夹角

计算切线角度后实时旋转

7. 飞机在运动过程中的大小变化

这一步就非常简单了,我们已知飞机的运动过程由t决定,而t在[0,1]之间,我们利用canvas的scale让飞机运动在[0,1/2 * L]的时候从0到1放大,在运动的[1/2 * L,L]的时候从1到0缩小即可

那么问题就抽象成构造一个分段函数去计算scale的值

利用初中数学很容易构造出该分段函数

分段函数计算缩放

至此我们就可以用[0,1]去映射[0,1]变化到[1,0],完成飞机运动的缩放

8. 剩下基本是canvas的知识运用了,比如save和restore的结合,lineTo和moveTo的结合,渐变填充createLinearGradient等,都非常简单的纯api调用,这里就不题了

现在,需求归纳和技术原理,实现难点,数学模型都已经推理完,接下来的才到最后一步,编程实现,而这一步单纯考验读者原生JS,闭包,异步编程的功底,相比之前的算法分析,显得简单的太多了

最后我们可以从这个案例总结出几点:

1. 一个优秀的程序员可以抽象出整个世界

2. 优秀的数学功底,很多本质上的原理,都是数学原理的实际运用

3. 算法永远是编程的灵魂,编程只是实现,算法是先驱,也是核心大脑

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容