小球飞入购物车动画及购物车列表弹出,收起

5,154 阅读5分钟

人生中第一次写博客,写的不好,各位大佬多多包涵。。。 话不多说,效果图如下:

思路:

我是用一个游离的view标签来做的,这个view采用fixed定位,默认top: 0; left: 0;

注:文中的游离view均指动画播放发生运动的view,即下面的图片中红色箭头所指的元素

当点击加号的时候,会发生以下操作:

  1. 获取当前点击加号的位置信息,计算出top值和left值,并将这个游离的view定位到当前点击的位置,这是动画开始位置

  2. 计算出左下角购物车距离顶端的left, top值,这是动画结束位置

  3. 然后使用css3动画,使其移动从开始位置移动到结束位置,left, top, opacity同时发生变化

Tips: 至于为什么不用小程序自带的wx.createAnimation呢?是因为使用这个播放一次动画之后,不能清除样式,第二次加入购物车游离view不能正确设置top, left值,好苦恼。。。

这个飞入效果是css3实现的,所以其它的也可以用,不局限于小程序,就是获取top, left的地方各有不同

接下来分布介绍:

步骤一: 获取当前点击加号的top, left值

获取当前点击加号的top, left值

加入购物车按钮绑定的点击事件为selectGoods

selectGoods(e) {
    let that = this;
    // 获取当前点击位置距离顶部的高度,即图中标出来的,红色字体写的top值,left值
    // top值和left值分别减去40, 是为了使游离的view定位到当前点击位置的左上方,有一种先弹上去,再飞入购物车的感觉,这里懒就不用动画了,哈哈
    let top = e.detail.y - 40;
    let left = e.detail.x - 40;
    that.setData({
      style: `top: ${top}px;left: ${left}px;`
    })
    that.playAnimation(e, left, top); // 播放动画
},

游离的view样式如下:

<view class="animat" style="{{style}}">
    <image class="icon" src="/resources/images/icon_add01.png"></image>
</view>
.animat{
  position: fixed;
  top: 0px;
  left: 0px;
}

点击加号的时候动态设置style值设置top, left

这一步结束后的效果图如下:

步骤二: 计算出左下角购物车距离顶端的left, top值

小程序有提供获取元素位置的方法,我大概封装了只需要传id的一个方法,方便使用,如下:

quertElementSize(id, callback) {
    let query = wx.createSelectorQuery();
    query.select('#' + id).boundingClientRect((rect) => {
      callback && callback(rect);
    }).exec()
}
/**
 * 获取左下角购物车图标top, left值
*/
quertShoppingCarSize() {
    let that = this;
    this.quertElementSize('shoppingCar', function (rect) {
      that.setData({
        'shoppingCarSize.top': rect.top + (rect.height / 2),
        'shoppingCarSize.left': rect.left + (rect.width / 2)
      })
    });
},

如下图:

获取元素中心点距离顶部top值,和距离左边left值

由于这个图标的位置的固定的,所以这个位置信息可以提前获取,存储到data中,不用每次加入购物车的时候再算

步骤三: 使用css3动画,使其移动到左下角购物车的位置,left, top, opacity同时发生变化

步骤一和二已经拿到了动画初始位置的top,left值,和结束位置的top, left值

现在只需要用animation,将这个游离view从初始位置移动到结束位置就可以了,代码如下:

/**
 * 小球飞入购物车动画
*/
playAnimation(e, left, top) {
    let that = this;
    this.aniTimer = setTimeout(function () {
      that.setData({
        style: `--startLeft: ${left}px;--startTop: ${top}px;
        --endLeft: ${that.data.shoppingCarSize.left}px;
        --endTop: ${that.data.shoppingCarSize.top}px;
        animation: runTop .3s cubic-bezier(.66,.1,1,.41), runLeft .3s linear;`
      })
    }, 5);
    that.setDataAddShoppingCar(e);
},

这里解释一下:

  1. --startLeft: ${left}px; 这里使用了css变量, startLeft, startTop, endLeft, endTop均为变量,在wxss中的css代码用var(--startTop)方式使用变量
  2. animation: runTop .3s cubic-bezier(.66,.1,1,.41), runLeft .3s linear; 这里的top变化和left变化分别是两个不用的动画,并且这两个动画的速度不一致,可以使运行轨迹为曲线
  3. 这里加定时器的原因是,是保证这个动画是最后执行的,就比如说经典面试题
setTimeout(function(){
    console.log(1);
})
console.log(2);
// 输入结果为 2 1

CSS代码: 动态获取开始位置top, left值和结束位置top,left值

@keyframes runTop {
  0%{
    top:  var(--startTop);
      opacity: 1;
  }
  90%{
      opacity: 1;
  }
  100%{
    top: var(--endTop);
    opacity: 0;
  }
  }
@keyframes runLeft {
  0%{
    left: var(--startLeft);
      opacity: 1;
  }
  90%{
      opacity: 1;
  }
  100%{
    left: var(--endLeft);
          opacity: 0;
  }
}
  /**
   * 更新数据
   */
  setDataAddShoppingCar(e) {
    let that = this;
    let index = e.currentTarget.dataset.index;
    let data = that.data.nowShowData[index];
    data.isSelect = true;

    let nowSelectData = that.data.nowSelectData;
    let tag = 'nowShowData[' + index + '].isSelect';

    nowSelectData.push(data);
    that.setData({
      [tag]: true,
      nowSelectData: nowSelectData
    });
  },

到这里,飞入购物车效果就完成了,鼓掌👏

接下来就是购物车列表弹出和收起##

这里就使用的是wx.createAnimation

代码如下:

/**
   * 购物车列表弹起
   */
  transShoppingCar() {
    let that = this;
    if (this.data.nowSelectData.length === 0) {
      wx.showToast({
        title: '购物车内没有商品哦~',
        icon: 'none',
        duration: 1500
      })
      return;
    }
    let shoppingCarIsShow = that.data.shoppingCarIsShow;
    shoppingCarIsShow = !shoppingCarIsShow;

    var animation = wx.createAnimation({
      duration: 500,//动画的持续时间 默认400ms   数值越大,动画越慢   数值越小,动画越快
      timingFunction: 'ease',//动画的效果 默认值是linear
    });
    this.animation = animation;
    if (shoppingCarIsShow) {
      this.setData({
        shoppingCarIsShow: shoppingCarIsShow
      });
      that.fadeIn();//调用显示动画

    } else {

      that.fadeDown();//调用隐藏动画   
      let time = setTimeout(function () {
        this.setData({
          shoppingCarIsShow: shoppingCarIsShow
        });
        clearTimeout(time);
      }.bind(this), 500)//先执行下滑动画,再隐藏模块
    }
  },
  //动画集
  // 进入
  fadeIn: function () {
    this.animation.translateY(0).opacity(1).step()
    this.setData({
      animationData: this.animation.export()
    })
  },
  fadeDown: function () {
    this.animation.translateY(300).opacity(0).step()
    this.setData({
      animationData: this.animation.export(),
    })
  }, 

WXML代码

<view class="goodsList" animation="{{animationData}}"></view>

思路:

利用translateY实现,初始列表translateY(300),弹出动画使translateY变为0,隐藏时translateY又变为300

所以样式中要进行初始位置translateY(300),即:

.goodsList {
    background-color: #fff;
    transform: translateY(300px);
}

代码github自取: github.com/orangleLi/o…

demo文件夹, 下载下来开发者工具直接导入就可以了