最近在做的一个小程序项目,需要一个加入购物车动画,倒腾了一下总算实现了想要的效果。
代码如下(用的是wepy2.x语法):
//wxml
//通过点击事件对象 获取到点击的位置
<image
src="{{imgDomain}}mini/ic_btn_add.png"
class="img"
@tap.stop="onCartAmountChange(item, index, $event)"
/>
//红点
<view
class="good_box"
animation="{{animation}}"
style="left: {{bus_x}}px; top: {{bus_y}}px; opacity: 0"
>
</view>
//wxss
.good_box {
width: 30rpx;
height: 30rpx;
position: absolute;
border-radius: 50%;
overflow: hidden;
left: 50%;
top: 50%;
z-index: 9999;
background: #fe3c53;
}
//js
data: {
linePos: null,
hh: '',
animation: null,
bus_x: '',
bus_y: '',
},
onLoad() {
this.getContentHeight()
},
onShow() {
//首先计算购物车的位置
this.busPos = {};
this.busPos['x'] = 240; //x轴坐标是固定的
this.busPos['y'] = this.hh - 19;
},
methods: {
getContentHeight() {
let systemInfo = wx.getSystemInfoSync();
this.hh = systemInfo.windowHeight;
},
onCartAmountChange(goods, index, event) {
this.tapEvent = event;
//把事件对象先保存起来,后期加入购物车成功后再用来实现动画
}
//加入购物车成功诗调用
this.touchOnGoods(this.tapEvent)
/*加入购物车动效*/
touchOnGoods: function(e) {
this.finger = {};
var topPoint = {};
this.finger['x'] = e.touches['0'].clientX; //点击的位置
this.finger['y'] = e.touches['0'].clientY;
if (this.finger['y'] < this.busPos['y']) {
topPoint['y'] = this.finger['y'] - 150;
} else {
topPoint['y'] = this.busPos['y'] - 150;
}
topPoint['x'] = Math.abs(this.finger['x'] - this.busPos['x']) / 2;
if (this.finger['x'] > this.busPos['x']) {
topPoint['x'] =
(this.finger['x'] - this.busPos['x']) / 2 + this.busPos['x'];
} else {
//
topPoint['x'] =
(this.busPos['x'] - this.finger['x']) / 2 + this.finger['x'];
}
this.linePos = this.bezier([this.busPos, topPoint, this.finger], 30);
this.startAnimation(e);
},
startAnimation: function(e) {
var index = 0,
that = this,
bezier_points = that.linePos['bezier_points'];
that.hide_good_box = false;
that.bus_x = that.finger['x'];
that.bus_y = that.finger['y'];
var len = bezier_points.length;
index = len;
let animation = wx.createAnimation({
duration: 33,
timingFunction: 'ease-out'
});
animation.opacity(1).step();
for (let i = index - 1; i > -1; i--) {
let deltX = bezier_points[i]['x'] - that.finger['x'];
let deltY = bezier_points[i]['y'] - that.finger['y'];
animation.translate(deltX, deltY).step();
}
animation.opacity(0).step();
this.animation = animation.export();
},
//获得了从点击到购物车之间轨迹的点的位置
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;
radian = Math.atan(tan);
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.原文中实现动画原理:通过不断去改变fixed定位红点的 top和left值,其中用了setTimeout和setInterval定时器,我拿来用的时候,在安卓中是正常的,IOS没有效果。所以我采用了微信小程序自带的动画API。
2.原文中红点的显示隐藏是通过标签的hidden属性来控制。我用小程序动画API的时候好像有点冲突,所以换成了opacity的值来显示隐藏红点。