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

## 实现

### 选点

``````if (this.finger['y'] < busPos['y']) {
topPoint['y'] = this.finger['y'] - 150;
} else {
topPoint['y'] = busPos['y'] - 150;
}
topPoint['x'] = Math.abs(this.finger['x'] - busPos['x']) / 2;

if (this.finger['x'] > 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) {
var pot;
var lines;
var ret = [];
var points;
for (var i = 0; i <= amount; i++) {
points = pots.slice(0);
lines = [];
while (pot = points.shift()) {
if (points.length) {
lines.push(pointLine([pot, points[0]], i / amount));
} else if (lines.length > 1) {
points = lines;
lines = [];
} else {
break;
}
}
ret.push(lines[0]);
}

function pointLine(points, rate) {
var pointA, pointB, pointDistance, xDistance, yDistance, tan, radian, tmpPointDistance;
var ret = [];
pointA = points[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;
tmpPointDistance = pointDistance * rate;
ret = {
x: pointA.x + tmpPointDistance * Math.cos(radian),
y: pointA.y + tmpPointDistance * Math.sin(radian)
};
return ret;
}
return {
'bezier_points': ret
};
},``````

### 动画显示点

1. 创建一个小球组件，组件的方法是执行动画,其中设置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))``````
2. 将选中的点格式化成每一帧的效果，然后调用小球组件的动画函数

``````/**
* 手指点击
* @param {Object} e
*/
// 简单判断手指点击位置是否是上次点击的位置，若是，直接是用上一次计算的关键帧数组
if (Math.abs(this.data.bus_y - e.touches["0"].clientY) > 20) {
this.data.keyFrames = []
this.data.bus_y = e.touches["0"].clientY

let points = ballFallAnimation.touchOnGoods({
x: e.touches["0"].clientX - 10,
y: e.touches["0"].clientY - 50
}, this.busPos, 80)
var index = 0,
bezier_points = points['bezier_points'];

var len = bezier_points.length;
index = len

// 放入关键帧
for (let i = index - 1; i > -1; i--) {
this.data.keyFrames.push({
left: bezier_points[i]['x'] + 'px',
top: bezier_points[i]['y'] + 'px',
opacity: i === 0 ? 0 : 1,
offset: 0.4
})
}
}
this.startAnimation()
},

/**
* 开始动画
*/
startAnimation: function() {
this.ballComponent.startAnimation(this.data.keyFrames)
},``````

1. 连续小球动画
关于这点，有一个很简单的做法，既然一个小球只能显示一个动画，那我设置多个小球，不就形成了连续动画吗？

``````/**
* 循环遍历所有小球的节点
*/
for (let i = 0; i < this.data.ballAnimationArray.length; i++) {
// 获取小球节点信息
this.ballAnimation = this.selectComponent(`#ball-\${i}`)
// 将小球信息存储
this.ballComponent.push(this.ballAnimation)
}

/**
* 开始动画
*/
startAnimation: function() {
// 数组循环，每次开启动画弹出一个数组里面，完成动画之后重新排队
let id = this.data.ballAnimationArray.pop()
this.ballComponent[id].startAnimation(this.data.keyFrames)
},

/**
* 动画结束
* @param {Object} e
*/
endAnimation(e) {
// 入队
this.data.ballAnimationArray.unshift(e.detail)
// 开启购物车动画
this.startShopCartAnimation()
// 处理事件逻辑
// Tip: 后续事件逻辑最好少使用setData,不然在低端机上表现起来会很不流畅
},``````

这时动画效果为：

1 声望
0 粉丝
0 条评论