小程序-贝塞尔曲线 加入购物车动画

3,964 阅读2分钟

开篇

作为一款2C下单小程序,产品老总说要搞一个加入购物车的动画,第一习惯是网上找解决方案:贝塞尔算法+setInterval=>setData。

正文

小坑

上线一段时间就出现了加入购物车总价计算负数情况,以及计算不准,嗯。包括动画频繁setData期间丢失关键动作setData,以及添加重复商品等bug。

来自小程序官方动画支持

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了,因为很少搞动画,缺乏经验。有同行仁兄多多指交。