React、Vue实现购物车小球抛物线效果

大家对下面这个动画效果想必都并不陌生了:

这里写图片描述

点击增加商品的按钮,会有一个小球做抛物线掉入购物车中,对这个动画一直很感兴趣,所以稍稍探究了一下


抛物线轨迹方程

实现此动画效果的难点在于,如何控制抛物小球在每一帧的坐标,因为只要控制好小球的每帧坐标,则小球的运动轨迹也就确定了。

一般情况下,这种抛物小球的运动轨迹都是抛物线,或者近似抛物线(例如本文开头的那个饿了么图片,小球运动感觉并不是真正的抛物线运动),道理其实都是一样的,这里先设定小球就是抛物线运动。

这里写图片描述

抛物线的一般方程为:

y=ax^2+bx+c

其中, x y为坐标,所以此方程中只要确定 a b c这 3个参数的值,就能得到一条抛物线轨迹方程式。

3个参数需要 3个条件才能完全求解,也就是说只要知道抛物线轨迹上随意 3个点的坐标即可。

不过,因为这里的坐标轴是我们自己虚构的,我们是在解决实际问题,而不是解决数学问题,所以不需要太复杂,怎么简单怎么来,如果假定抛物线轨迹的起始点坐标为 (0,0),那么根据上述抛物线方程可以得到 c=0,抛物线方程就变成了:

y=ax^2+bx

另外, 参数 a实际上是一个可控的常量,通过设定 a的值来确定抛物线在每一点处的曲率,所以 a可以看做是已知的

所以,只需要求出 b即可:

b=(y+ ax^2) / x

只要再知道抛物线轨迹上任意一点坐标即可,而小球所在的抛物线轨迹上,除了起始点坐标外,还有一点坐标肯定是已知的,那就是抛物线末尾坐标,即购物车元素的坐标,到此,抛物线方程就搞定了。

小球抛物线动画的关键就是这个抛物线轨迹方程,只要得到了此方程公式,剩下的就很好解决了,关于抛物线方程更多详细可见 张鑫旭


在 React中实现抛物小球动画

搞定了抛物线方程之后,接下来就是如何处理抛物小球的问题了。

每一次点击增加按钮,就会出现一个沿着抛物线轨迹抛向购物车的小球,每个小球因为出现的坐标以及时间不同,抛物线轨迹并不一定完全重合,所以需要对这些小球进行单独控制。

可以将每个抛物小球封装成一个组件,每多处一个小球就相当于是在页面上多增加了一个此组件,需要给组件传入的关键 props数据有如下几个。

  • 小球的起始坐标
    根据上述关于抛物线方程的推导可知,想要确定此方程,需要小球的起始坐标以及末尾坐标,设定其实坐标如下表示:
// 起始点坐标
position = {x0, y0}
// 末尾点坐标
target = {x1, y1}
  • 曲率 curvature
    在上述推导的抛物线方程中,参数a是可控的,由我们自己来定,通过这个参数控制抛物线在各点的曲率

  • 速度 speed
    小球的运动速度也是可以控制的,通过此参数来控制小球的运动速度

  • 清除函数以及小球id
    小球在进入到购物车后会自动消失,所以这里需要处理一下,页面中可能同时存在很多个小球,根据 id来确定到底哪个小球需要清除掉。

根据以上推断,小球组件可以是下面这个样子:

<Ball
   position={position}
   target={target}
   id={id}
   curvature={curvature}
   speed={speed}
   changeFlyBallCount={changeFlyBallCount}/>

设定一个 state作为页面上的小球组件数据来源:

balls: []

balls数组中的每一项代表着一个需要显示的小球,当需要在页面上显示小球时,则向 balls中追加一个小球数据项,此项中包含小球的起始点坐标以及 一个独一无二的小球id(可以使用时间戳代替),如下:

balls: [
    {id, position: {x, y}}
]

当小球进入到购物车中需要被清除掉时,只需要根据 id查找出小球的数据项,然后将此项从 balls中删掉即可,如下:

for (let i=0; i<balls.length; i++) {
 if(balls[i].id === id) {
    // 清除小球
    balls.splice(i, 1)
    break
  }
}

效果如下:

这里写图片描述


在 Vue中实现抛物小球动画

上述 react实现的动画,如果应用在实际项目中,有的时候可能会感觉抛物线有点别扭,我们可能是希望抛物线轨迹与小球抛出点的水平方向相切,即小球抛出点坐标是抛物线的顶点,看起来会比较顺滑,类似下面这种:

这里写图片描述

如果是这样的话,那么抛物线方程就要重新设定一下了。

由于小球抛出点为顶点,顶点已知,那么可根据抛物线顶点式进行推导:

// 抛物线顶点式,(h,k)为顶点坐标
y=a(x-h)^2+k(a0)

设定小球起始点坐标为 (0, 0),则只需要再已知一点坐标,得到参数 a即可,而这一点坐标显而易见可以是 抛物线末尾点坐标(x1,y1)上式中的参数 a为:

a = y1/(x1^2)

抛物线方程搞定。

这里可以使用 Vue动画钩子函数来完成小球抛物动画,其余的与上述 react大体相同,设定一个数组数据源 balls,当需要显示小球时,就向里面追加一个数据项,有一点需要注意,由于Vue动画本身的逻辑,当需要清除小球的时候,就不能像上面 react实现的那样直接将对应的数据项从 balls数组里面删除了,可以多设定一个标志变量 show,当需要显示的时候,将 show设为 true,当需要小球消失时,将对应的小球数据项的 show设为 false,如下:

// x,y为小球起始点坐标
balls: [
  {id, show, x, y}
]

每个小球都看作是一个 transiton动画,为每个动画显式声明 appearafterAppear这两个钩子函数,在 appear函数中实现小球运动动画,在 afterAppear函数中清除小球。

效果如下:

这里写图片描述


动画的主体部分大概就是这样了,至于其他的一些小动画,例如小球掉入购物车时,购物车发生抖动等就很简单了,不多说。
具体代码放在 Github上了,有兴趣可以看一下,顺便 star哦~

猜你喜欢

转载自blog.csdn.net/deeplies/article/details/78191999
今日推荐