当前位置: 首页 > Web前端 > JavaScript

微信小程序购物车小球动画

时间:2023-03-27 15:35:43 JavaScript

需求在订购商品时,涉及到商品加入购物车时小球以抛物线形式落下的显示动画研究。在接触这个需求之前,我也有研究过小程序的动画,但基本上都是针对线性变化,球类动画的抛物线形式。在网上很容易找到关于抛物线的动画。原理是贝塞尔曲线在网上找了一篇大文章。已经有抛物球、传送门的相关实现原理,但效果不是我喜欢的。所以我重写了一部分。抛物线球实现的基本原理:选中点(关键帧),定时器会依次显示选中的点。选点所谓选点,就是在手指点击的地方和购物车之间形成的抛物线中选出几个坐标点,即关键帧。首先我们确定三个点,起点(手指点击的位置)、终点(购物车的位置)和控制点。控制点的代码如下:if(this.finger['y']busPos['x']){topPoint['x']=(this.finger['x']-busPos['x'])/2+busPos['x'];}else{//topPoint['x']=(busPos['x']-this.finger['x'])/2+this.finger['x'];}这里,二阶贝塞尔曲线用于选择点。贝塞尔曲线我不懂,不过有一个代码可以直接使用,如下:/***获取小球运动的轨迹点*@param{Object}pots三个控制点*@param{Number}amount选择的点数*@returns*/bezier:function(pots,amount){varpot;可变线;varret=[];变量点;对于(vari=0;i<=amount;i++){points=pots.切片(0);线=[];while(pot=points.shift()){if(points.length){lines.push(pointLine([pot,points[0]],i/amount));}elseif(lines.length>1){点=线;线=[];}else{休息;}}ret.push(行[0]);}functionpointLine(points,rate){varpointA,pointB,pointDistance,xDistance,yDistance,tan,radian,tmpPointDistance;varret=[];点A=点[0];//点击pointB=points[1];//中间xDistance=pointB.x-pointA.x;yDistance=pointB.y-pointA.y;pointDistance=Math.pow(Math.pow(xDistance,2)+Math.pow(yDistance,2),1/2);tan=yDistance/xDistance;radian=Math.atan(tan);tmpPointDistance=pointDistance*rate;ret={x:pointA.x+tmpPointDistance*Math.cos(radian),y:pointA.y+tmpPointDistance*Math.sin(radian)};返还;}return{'bezier_points':ret};},动画显示点选中点后,我们需要显示这些点。和大哥不同的是,大佬是用定时器来显示选中的点,但是我要做的是小程序的动画,所以我直接选择小程序的动画界面创建一个小球组件,并且组件的方法是执行动画画图,这里设置hide_good_box字段控制小球的渲染this.setData({hide_good_box:false})this.animate(`#good_box-${this.properties.ballIndex}`,keyFrames,150,function(){this.setData({hide_good_box:true})//回调this.triggerEvent("endAnimation",this.properties.ballIndex)//清除good_box动画this.clearAnimation(`#good_box-${this.properties.ballIndex}`)}.bind(this))将选中的点格式化成每一帧的效果,然后调用小球组件的动画函数/***fingerclick*@param{Object}e*/tapAdd(e){//简单判断一下手指点击的位置是不是最后一次点击的位置?如果是,直接使用最后计算的关键帧数组if(Math.abs(this.data.bus_y-e.touches["0"].clientY)>20){this.data.keyFrames=[]this.data.bus_y=e.touches["0"].clientYletpoints=ballFallAnimation.touchOnGoods({x:e.touches["0"].clientX-10,y:e.touches["0"].clientY-50},this.busPos,80)varindex=0,bezier_points=points['bezier_points'];varlen=bezier_points.length;index=len//放置关键帧(让我=指数-1;我>-1;i--){this.data.keyFrames.push({左:bezier_points[i]['x']+'px',上:bezier_points[i]['y']+'px',不透明度:i===0?0:1,offset:0.4})}}this.startAnimation()},/***开始动画*/startAnimation:function(){this.ballComponent.startAnimation(this.data.keyFrames)},在这一步,我做了一个小优化,就是把第一次手指点击的位置和选中点的位置存储起来,下次点击的位置偏移量不大这种情况下,跳过选点这一步,直接开始动画。经过以上步骤,你已经可以得到一个完整的小球动画了。但是,一个问题出现了:如果用户连续点击,用户该怎么办?用户连续点击,理论上应该会出现连续的小球,可惜,下面是来自小程序官方的介绍:调用animateAPI后,会在节点上添加一些样式属性,覆盖原来对应的样式。如果需要清除这些样式,可以在该节点上的所有动画执行完毕后,使用this.clearAnimation清除这些属性。然后,如果连续点击,会出现第一次小球还没有落到购物车里,第二次点击,从手指点击的地方又开始往下掉,第三次,第四次……都是这样。连续球动画关于这一点,有一个很简单的方法。既然一个小球只能显示一个动画,那么如果我设置多个小球,会形成一个连续的动画吗?/***循环遍历所有球节点*/for(leti=0;i