开篇
作为一款2C下单小程序,产品老总说要搞一个加入购物车的动画,第一习惯是网上找解决方案:贝塞尔算法+setInterval=>setData。
正文
小坑
上线一段时间就出现了加入购物车总价计算负数情况,以及计算不准,嗯。包括动画频繁setData期间丢失关键动作setData,以及添加重复商品等bug。
来自小程序官方动画支持
- 简易的界面动画:
- 使用 CSS 渐变 和 CSS 动画 来创建简易的界面动画。
- wx.createAnimation 接口来动态创建简易的动画效果
- 高级动画
- WXS 响应事件 (备注:基础库要求2.4.4以上)
WXS响应事件的基础库要求有些高,直接放弃了。
网络社区
贝塞尔曲线算法 + setInterval=>setData
function bezier(points, part) {
let sx = points[0]['x'];
let sy = points[0]['y'];
let cx = points[1]['x'];
let cy = points[1]['y'];
let ex = points[2]['x'];
let ey = points[2]['y'];
var bezier_points = [];
// 起始点到控制点的x和y每次的增量
var changeX1 = (cx - sx) / part;
var changeY1 = (cy - sy) / part;
// 控制点到结束点的x和y每次的增量
var changeX2 = (ex - cx) / part;
var changeY2 = (ey - cy) / part;
//循环计算
for (var i = 0; i <= part; i++) {
// 计算两个动点的坐标
var qx1 = sx + changeX1 * i;
var qy1 = sy + changeY1 * i;
var qx2 = cx + changeX2 * i;
var qy2 = cy + changeY2 * i;
// 计算得到此时的一个贝塞尔曲线上的点
var lastX = qx1 + ((qx2 - qx1) * i) / part;
var lastY = qy1 + ((qy2 - qy1) * i) / part;
// 保存点坐标
var point = {};
point['x'] = lastX;
point['y'] = lastY;
bezier_points.push(point);
}
//console.log(bezier_points)
return {
bezier_points
};
}
然后 setInterval (坑)
优化方案
综合贝塞尔曲线和wx.createAnimation
method:{
touchOnGoods(e) {
// 如果good_box正在运动
if (!this.data.hide_good_box) return;
const { touches } = e;
const topPoint = {};
this.finger = {
x: touches[0].clientX,
y: touches[0].clientY
};
topPoint['y'] =
this.finger['y'] < this.busPos['y']
? this.finger['y'] - 150
: this.busPos['y'] - 150;
topPoint['x'] =
this.finger['x'] > this.busPos['x']
? (this.finger['x'] - this.busPos['x']) / 2 + this.busPos['x']
: (this.busPos['x'] - this.finger['x']) / 2 + this.finger['x'];
const result = bezier([this.finger, topPoint, this.busPos], 25);
this.startAnimation(result);
},
startAnimation(linePos) {
const { bezier_points } = linePos;
const len = bezier_points.length - 1;
const first = bezier_points.shift();
//防止动画不生效
//同一个按钮动画,动画开始前先归位元素位置
this.setData(
{
hide_good_box: false,
initLeft:first.x,
initTop:first.y,
animationData: this.animation.export()
},
() => {
bezier_points.forEach((i, idx) => {
this.animation
.left(i.x)
.top(i.y)
.rotate(50)
.step({ duration: 25 });
});
this.setData({
animationData: this.animation.export()
});
}
);
//有个bug,就是连续点击,动画不出来
setTimeout(() => {
this.setData({ hide_good_box: true });
}, len * 20 + 100);
}
},
ready() {
this.busPos = { x: 50, y: 601 };
//调用同步接口 略有问题
this.busPos.y = util.getSysInfo().windowHeight - 66;
this.animation = wx.createAnimation({
timingFunction: 'ease-in',
delay: 0,
duration: 1000
});
}
//有点懒,直接贴源代码了
<view class="good_box" hidden="{{hide_good_box}}" style="left:{{initLeft}}px;top:{{initTop}}px" animation="{{animationData}}"></view>
<view bindtap="touchOnGoods">
<slot></slot>
</view>
结尾
其实好的解决方案,应该是排除低端手机,尤其是安卓机子来启用动画。 暂时没有其他好的idea了,因为很少搞动画,缺乏经验。有同行仁兄多多指交。