微信小程序使用wxs实现购物车贝塞尔曲线

基本实现思路

小程序要实现点击加购时绘制一个抛物线动画,这个抛物线动画是计算出来的贝塞尔曲线上每个点的坐标,然后通过`setData`动态给元素设置样式,从而实现动画的效果,但是频繁的调用`setData`会造成一定的性能问题,这里采用微信官方提供的`wxs`

wxs实现

<!-- 自定义动画 -->
<wxs module="customAnimate">
var curIndex = 0
var ballIns
var Ins
var curCoordIdx
var wxsFunction = function(event, ownerInstance) {
  var ins = ownerInstance;
  var topPoint = { x: 0, y: 0 }
    // 起点
  var finger = { x: event.detail.x, y: event.detail.y }
  var cartIconInstance = ownerInstance.selectComponent('.cart-icon')
    // 终点
  var busPos = { x: cartIconInstance.getBoundingClientRect().x, y: cartIconInstance.getBoundingClientRect().y }

  console.log('开始动画')

  if (finger['y'] < busPos['y']) {
      topPoint['y'] = finger['y'] - 80;
  } else {
      topPoint['y'] = busPos['y'] - 80;
  }

  topPoint['x'] = Math.abs(finger['x'] - busPos['x']) / 2;

  if (finger['x'] > busPos['x']) {
      topPoint['x'] = (finger['x'] - busPos['x']) / 2 + busPos['x'];
  } else {
      topPoint['x'] = (busPos['x'] - finger['x']) / 2 + finger['x'];
  }
  var linePos = bezier([busPos, topPoint, finger], 30)
  coordArr = linePos.bezier_points
  executeCartAnimation()

  function executeCartAnimation () {
    curCoordIdx = coordArr.length - 1
    // 使用 requestAnimationFrame 代替定时器
    ins.requestAnimationFrame(setStyleByFrame)
  }

  function setStyleByFrame() {
    if (curCoordIdx >= 0) {
      var scale = 0.6
      if (curCoordIdx > 20) {
        scale = curCoordIdx / coordArr.length
      }
      ins.selectComponent('.good_box').setStyle({
        display: 'block',
        left: coordArr[curCoordIdx].x + 'px', 
        top: coordArr[curCoordIdx].y + 'px',
        transform: 'scale('+ scale +')'
      })
      console.log(curCoordIdx, coordArr.length)
      curCoordIdx -= 1
      ins.requestAnimationFrame(setStyleByFrame)
    } else {
      ins.selectComponent('.good_box').setStyle({
        display: 'none'
      })
    }
  }
}


function bezier(points, times) {
  // 0、以3个控制点为例,点A,B,C,AB上设置点D,BC上设置点E,DE连线上设置点F,则最终的贝塞尔曲线是点F的坐标轨迹。
  // 1、计算相邻控制点间距。
  // 2、根据完成时间,计算每次执行时D在AB方向上移动的距离,E在BC方向上移动的距离。
  // 3、时间每递增100ms,则D,E在指定方向上发生位移, F在DE上的位移则可通过AD/AB = DF/DE得出。
  // 4、根据DE的正余弦值和DE的值计算出F的坐标。
  // 邻控制AB点间距
  var bezier_points = [];
  var points_D = [];
  var points_E = [];
  var DIST_AB = Math.sqrt(Math.pow(points[1]['x'] - points[0]['x'], 2) + Math.pow(points[1]['y'] - points[0]['y'], 2));
  // 邻控制BC点间距
  var DIST_BC = Math.sqrt(Math.pow(points[2]['x'] - points[1]['x'], 2) + Math.pow(points[2]['y'] - points[1]['y'], 2));
  // D每次在AB方向上移动的距离
  var EACH_MOVE_AD = DIST_AB / times;
  // E每次在BC方向上移动的距离
  var EACH_MOVE_BE = DIST_BC / times;
  // 点AB的正切
  var TAN_AB = (points[1]['y'] - points[0]['y']) / (points[1]['x'] - points[0]['x']);
  // 点BC的正切
  var TAN_BC = (points[2]['y'] - points[1]['y']) / (points[2]['x'] - points[1]['x']);
  // 点AB的弧度值
  var RADIUS_AB = Math.atan(TAN_AB);
  // 点BC的弧度值
  var RADIUS_BC = Math.atan(TAN_BC);
  // 每次执行
  for (var i = 1; i <= times; i++) {
      // AD的距离
      var dist_AD = EACH_MOVE_AD * i;
      // BE的距离
      var dist_BE = EACH_MOVE_BE * i;
      // D点的坐标
      var point_D = {};
      point_D['x'] = dist_AD * Math.cos(RADIUS_AB) + points[0]['x'];
      point_D['y'] = dist_AD * Math.sin(RADIUS_AB) + points[0]['y'];
      points_D.push(point_D);
      // E点的坐标
      var point_E = {};
      point_E['x'] = dist_BE * Math.cos(RADIUS_BC) + points[1]['x'];
      point_E['y'] = dist_BE * Math.sin(RADIUS_BC) + points[1]['y'];
      points_E.push(point_E);
      // 此时线段DE的正切值
      var tan_DE = (point_E['y'] - point_D['y']) / (point_E['x'] - point_D['x']);
      // tan_DE的弧度值
      var radius_DE = Math.atan(tan_DE);
      // 地市DE的间距
      var dist_DE = Math.sqrt(Math.pow((point_E['x'] - point_D['x']), 2) + Math.pow((point_E['y'] - point_D['y']), 2));
      // 此时DF的距离
      var dist_DF = (dist_AD / DIST_AB) * dist_DE;
      // 此时DF点的坐标
      var point_F = {};
      point_F['x'] = dist_DF * Math.cos(radius_DE) + point_D['x'];
      point_F['y'] = dist_DF * Math.sin(radius_DE) + point_D['y'];
      bezier_points.push(point_F);
  }
  return {
      'bezier_points': bezier_points
  };
}
module.exports = {
  wxsFunction: wxsFunction
}
</wxs>
<view catchtap="{{customAnimate.wxsFunction}}"></view>

与基本实现思路一致,不同的这里用`wxs`的高性能优势替代`setData`, 需要注意的是:wxs并不支持es6,不支持`setInterval`和`setTimeOut`,这里使用`requestAnimationFrame`这个api替代

最终测试测试在安卓端运行非常流畅,ios端表现不如安卓

最后附上微信官方文档 小程序框架 / 视图层 / 事件系统 / WXS 响应事件 (qq.com)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值