小程序仿饿了么弹窗式购物车

效果图

包含以下模块

  1. 选购页实现切换分类与数量加减
  2. 购物车动画与同步数量加减
  3. 购物车弹窗自适应商品条数

一、 点击分类项,切换右边的食品,并高亮自身

这个实现比较简单,给一个states数组,用于记录每一项分类的状态,点击设为true,wxml渲染时三目运算判断即可。

categoryStates = categoryStates.map(function (item, i) {
	if (index == i) {
		item = true;
	} else {
		item = false;
	}
	return item;
});

相应的wxml文件,class="{{categoryStates[index] ? 'category-item-active' : ''}}"

二、 加减按钮

  1. 初始只有一个加号
  2. 点击加号后,相应商品数量+1,并出现减号
  3. 减至0时,减号消失,连同数量值

设计数组结构

cartData: {},它的键是Food表的objectId,值是数量。

以下是js代码实现

add: function (e) {
	// 所点商品id
	var foodId = e.currentTarget.dataset.foodId;
	console.log(foodId);
	// 读取目前购物车数据
	var cartData = that.data.cartData;
	// 获取当前商品数量
	var foodCount = cartData[foodId] ? cartData[foodId] : 0;
	// 自增1后存回
	cartData[foodId] = ++foodCount;
	// 设值到data数据中
	that.setData({
		cartData: cartData
	});
}

在wxml文件中绑定数据如下

<view class="stepper">
	<!-- 减号 -->
	<view class="symbol subtract" wx:if="{{cartData[item.objectId]}}">-</view>
	<!-- 数量 -->
	<view class="value">{{cartData[item.objectId]}}</view>
	<!-- 加号 -->
	<view class="symbol add" bindtap="add" data-food-id="{{item.objectId}}">+</view>
</view>

上述代码中,通过wx:if判断当前商品的数量是否存在,无则不显示减号按钮;而在加号按钮旁要显示的数量就是{{cartData[item.objectId]}};点击事件传递的foodId就是{{item.objectId}}

减法按钮类似

subtract: function (e) {
	// 所点商品id
	var foodId = e.currentTarget.dataset.foodId;
	// 读取目前购物车数据
	var cartData = that.data.cartData;
	// 获取当前商品数量
	var foodCount = cartData[foodId];
	// 自减1
	--foodCount;
	// 减到零了就直接移除
	if (foodCount == 0) {
		delete cartData[foodId]
	} else {
		cartData[foodId] = foodCount;
	}
	// 设值到data数据中
	that.setData({
		cartData: cartData
	});
}

三、购物车动画

cascadeToggle: function () {
        //切换购物车开与关
	if (that.data.maskVisual == 'show') {
		that.cascadeDismiss();
	} else {
		that.cascadePopup();
	}
},
cascadePopup: function () {
	// 购物车打开动画
	var animation = wx.createAnimation({
		duration: 500,
		timingFunction: 'ease-in-out',
	});
	this.animation = animation;
	animation.translateY(-285).step();
	this.setData({
		animationData: this.animation.export(),
		maskVisual: 'show'
	});
},
cascadeDismiss: function () {
        // 购物车关闭动画
	this.animation.translateY(285).step();
	this.setData({
		animationData: this.animation.export(),
		maskVisual: 'hidden'
	});
}

通过点击控制显示与隐藏,<view class="ft" bindtap="cascadeToggle">

而<view>层级通过z-index来解决,其中底部购物车.ft区别权重最高,设为999,其次是弹窗主体.modal-content,其余默认不设定。

四、购物车加减

首先要读取购物车数据,即cartData,它是以foodId为key,数量为value的object,所以需要转换为array,才能很好地被遍历。

cartToArray: function (foodId) {
	// 需要判断购物车数据中是否已经包含了原商品,从而决定新添加还是仅修改它的数量
	var cartData = that.data.cartData;
	var cartObjects = that.data.cartObjects;
    var query = new Bmob.Query('Food');
    // 查询对象
    query.get(foodId).then(function (food) {
    	// 从数组找到该商品,并修改它的数量
    	for (var i = 0; i < cartObjects.length; i++) {
    		if (cartObjects[i].food.id == foodId) {
    			// 如果是undefined,那么就是通过点减号被删完了
    			if (cartData[foodId] == undefined) {
    				delete cartObjects[i];
    			} else {
        			cartObjects[i].quantity = cartData[foodId];
    			}
    			that.setData({
    				cartObjects: cartObjects
    			});
    			// 成功找到直接返回,不再执行添加
    			return ;
    		}
    	}
    	// 添加商品到数组
        var cart = {};
        cart.food = food;
        cart.quantity = cartData[foodId];
    	cartObjects.push(cart);
    	that.setData({
    		cartObjects: cartObjects
    	});
    });
}

然后在add/subtract方法末尾中调用它就可以购物车键值对转换成对象数组。

那接下来就顺理成章了,直接购物车小弹窗里将cartObjects渲染就可以了。

<view class="modal-body">
	<view class="item" wx:for="{{cartObjects}}">
		<view class="title">{{item.food.title}}</view>
		<view class="fee">{{item.food.price * item.quantity}}</view>
		<view class="stepper">
			<!-- 减号 -->
			<view class="symbol subtract" bindtap="subtract" wx:if="{{cartData[item.food.objectId]}}" data-food-id="{{item.food.objectId}}">-</view>
			<!-- 数量 -->
			<view class="value">{{cartData[item.food.objectId]}}</view>
			<!-- 加号 -->
			<view class="symbol add" bindtap="add" data-food-id="{{item.food.objectId}}">+</view>
		</view>
	</view>
</view>

得益于MVVM数据绑定,因此在购物车里点加减也实时地同步了商品列表中显示的数量

汇总

amount: function() {
	var cartObjects = that.data.cartObjects;
	var amount = 0;
	cartObjects.forEach(function (item, index) {
		amount += item.quantity * item.food.get('price');
	});
	that.setData({
		amount: amount
	});
}

这里多请求了一次网络,由于请求是异步的,所以我将汇总代码丢在网络请求里,于cartToArray方法内。

减法与加法基本类似,值得一提的是,减法要判断非负的合法性,所以将自减至零时,直接将元素通过delete操作移除,省去后续提交购物车遍历汇总的非零判断的烦琐。

五、购物车高度自适应

购物车列表用的是view作为容器,这样列表是不能在弹窗里滚动的,因此要将购物车弹窗改为scroll-view显示,并且高度自适应行数,直至一个max-height高度

改造

<view class="modal-body">替换成<scroll-view class="modal-body" scroll-y="true">,同时在样式表中为它增加一个高度height值。

bug

过程出现了一个bug,外部的商品列表上拉时再触发购物车弹窗会出现有白屏区块。

在wxml面板查看,发现是modal-mask层随着页面滚动而上移了

果断修改样式.modal-mask {position: absolute;}.modal-mask {position: fixed;}

问题迎刃而解。

进一步改造

将高亮动态化,由wxss样式转移到.js代码中控制

1、先注释掉wxss中硬编码的height样式

/*弹窗主体*/
.modal-content {
	position: fixed;
	bottom: -235px;
	left: 0;
	width: 100%;
	/*height: 235px;*/
	margin-top: 5px;
	background: #fff;
	z-index: 99;
}
/*内容区域*/
.modal-body {
	font-size: 14px;
	/*height: 145px;*/
	max-height: 295px;
}

2、在js文件声明成员变量

// 最大行数
var max_row_height = 5;
// 行高
var cart_footer_offset = 90;
// 底部栏偏移量
var food_row_height = 49;

设定最大行数,是因为这个弹窗不能无限伸展,否则都盖过整个屏幕了;在最大行数以内,都应该自动适应高度。

3、计算高度值

// scrollHeight为商品列表本身的高度
var scrollHeight = (that.data.cartObjects.length <= max_row_height ? that.data.cartObjects.length : max_row_height) * food_row_height;
// cartHeight为整个购物车的高度,也就是包含了标题栏与底部栏的高度
var cartHeight = scrollHeight + cart_offset;
animation.translateY(- cartHeight).step();
that.setData({
	scrollHeight: scrollHeight,
	cartHeight: cartHeight
});

4、wxml绑定数据

<view animation="{{animationData}}" class="modal-content" style="height: {{cartHeight}}px; bottom: -{{cartHeight}}px;">
...
        <scroll-view class="modal-body" scroll-y="true" style="height: {{scrollHeight}}px;">

到此不论购物车内有几条数据,都能很好的自适应;超过5条,开始滚动视图,大功告成

源码下载:http://git.oschina.net/laeser/dinner,或者关注公众号【黄秀杰】,回复112。

转载于:https://my.oschina.net/huangxiujie/blog/1476662

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值