Browse Source

增加购物车动画

lilei 8 months ago
parent
commit
4e378064d3

+ 171 - 0
components/uni-cart-animation/uni-cart-animation.vue

@@ -0,0 +1,171 @@
+<template>
+	<!-- 加入购物车动画 -->
+	<view>
+		<view class="good_box" v-show="!hide_good_box" :style="[{left:bus_x+'px', top:bus_y+'px', background:PrimaryColor}]"></view>
+	</view>
+</template>
+
+<script>
+    export default {
+        data() {
+            return {
+                PrimaryColor: '#fe461d', // 背景色
+                hide_good_box: true,
+                bus_x:0,
+                bus_y:0,
+            }
+        },
+        created(e) {
+            // 默认掉落坐标
+            this.busPos = {};
+            this.busPos['x'] = uni.getSystemInfoSync().windowWidth - 140;
+            this.busPos['y'] = uni.getSystemInfoSync().windowHeight + 100;
+        },
+        methods:{
+            //点击商品触发的事件
+            touchOnGoods: function(e,busPos) {
+                console.log(e, '进入动画')
+                if(busPos){//是否自定义掉落坐标
+                    this.busPos = busPos
+                }
+                // 如果good_box正在运动
+                if (!this.hide_good_box) return;
+                this.finger = {};
+                var topPoint = {};
+                var flag = false; //false:倒序,true:正序
+                this.finger["x"] = e.detail.clientX || e.touches[0].clientX; //点击的位置
+                this.finger["y"] = e.detail.clientY || e.touches[0].clientY;
+    
+                if (this.finger['y'] < this.busPos['y']) {
+                    topPoint['y'] = this.finger['y'] - 150;
+                } else {
+                    topPoint['y'] = this.busPos['y'] - 150;
+                }
+                topPoint['x'] = Math.abs(this.finger['x'] - this.busPos['x']) / 2;
+    
+                if (this.finger['x'] > this.busPos['x']) {
+                    topPoint['x'] = (this.finger['x'] - this.busPos['x']) / 2 + this.busPos['x'];
+                    console.log("0123")
+                    this.linePos = this.bezier([this.busPos, topPoint, this.finger], 30);
+                    flag = false
+                } else { //
+                    topPoint['x'] = (this.busPos['x'] - this.finger['x']) / 2 + this.finger['x'];
+                    console.log("123")
+                    this.linePos = this.bezier([this.finger, topPoint, this.busPos], 30);
+                    flag = true
+                }
+                this.startAnimation(flag);
+            },
+            //开始动画
+            startAnimation: function(flag) {
+                // uni.vibrateShort(); //震动方法
+                var that = this,
+                    bezier_points = that.linePos['bezier_points'],
+                    index = bezier_points.length;
+                console.log(bezier_points, 'bezier_points')
+                this.hide_good_box = false,
+                this.bus_x = that.finger['x']
+                this.bus_y = that.finger['y']
+                if (!flag) {
+                    index = bezier_points.length;
+                    this.timer = setInterval(function() {
+                        index--;
+                        that.bus_x = bezier_points[index]['x']
+                        that.bus_y = bezier_points[index]['y']
+                        if (index <= 1) {
+                            clearInterval(that.timer);
+                            that.hide_good_box = true
+							that.$emit('animationfinish')
+                        }
+                    }, 22);
+                } else {
+                    index = 0;
+                    this.timer = setInterval(function() {
+                        index++;
+                        that.bus_x = bezier_points[index]['x']
+                        that.bus_y = bezier_points[index]['y']
+                        if (index >= 28) {
+                            clearInterval(that.timer);
+                            that.hide_good_box = true
+							that.$emit('animationfinish')
+                        }
+                    }, 22);
+                }
+            },
+            bezier: function(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 = [];
+                const DIST_AB = Math.sqrt(Math.pow(points[1]['x'] - points[0]['x'], 2) + Math.pow(points[1]['y'] - points[0]['y'], 2));
+                // 邻控制BC点间距
+                const DIST_BC = Math.sqrt(Math.pow(points[2]['x'] - points[1]['x'], 2) + Math.pow(points[2]['y'] - points[1]['y'], 2));
+                // D每次在AB方向上移动的距离
+                const EACH_MOVE_AD = DIST_AB / times;
+                // E每次在BC方向上移动的距离 
+                const EACH_MOVE_BE = DIST_BC / times;
+                // 点AB的正切
+                const TAN_AB = (points[1]['y'] - points[0]['y']) / (points[1]['x'] - points[0]['x']);
+                // 点BC的正切
+                const TAN_BC = (points[2]['y'] - points[1]['y']) / (points[2]['x'] - points[1]['x']);
+                // 点AB的弧度值
+                const RADIUS_AB = Math.atan(TAN_AB);
+                // 点BC的弧度值
+                const 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
+                };
+            },
+        }
+    }
+</script>
+
+<style lang="scss" scoped>
+.good_box {
+    width: 20rpx;
+    height: 20rpx;
+    position: fixed;
+    border-radius: 50%;
+    overflow: hidden;
+    left: 50%;
+    top: 50%;
+    z-index: +99;
+    border: 1px solid #fff;
+    background: red;
+}
+</style>

+ 13 - 4
pagesA/digitalShelf/choosePart.vue

@@ -227,6 +227,8 @@
 				</view>
 			</view>
 		</uni-popup>
+		<!-- 购物车动画 -->
+		<uni-cart-animation ref="cartAnimation"></uni-cart-animation>
 	</view>
 </template>
 
@@ -277,7 +279,11 @@
 				showCarPrice: false, // 显示车主价
 				showCostPrice: false, // 显示成本价
 				tempData: null ,// 临时数据
-				safeAreaBottom:0
+				safeAreaBottom:0,
+				busPos:{
+				    x: 10,
+				    y:uni.getSystemInfoSync().windowHeight - 30
+				}
 			}
 		},
 		computed:{
@@ -578,7 +584,8 @@
 				} 
 			},
 			// 添加到购物车
-			addPart(item,type){
+			addPart(event,item,type){
+				this.$refs.cartAnimation.touchOnGoods(event,this.busPos);
 				if(type == 'vin'){
 					this.updateListData(item.productSn,'vinPartList',1,true)
 				}else{
@@ -604,6 +611,7 @@
 			},
 			// 更新配件列表数量
 			updatePartList(data,type){
+				console.log(data,type)
 				const index = this.partListData.findIndex(item => item.productSn == data.index)
 				// 删除
 				if(!data.value){
@@ -624,15 +632,16 @@
 					// 更新页面数据
 					this.updateListData(data.index,type,data.value,true)
 				}
+				if(data.event){
+					this.$refs.cartAnimation.touchOnGoods(data.event,this.busPos);
+				}
 			},
 			// 修改适配配件上数量
 			updateVinNums(data){
-				console.log(data)
 				this.updatePartList(data,'vinPartList')
 			},
 			// 修改其它配件上的数量
 			updateNums(data){
-				console.log(data)
 				this.updatePartList(data,'partList')
 			},
 			// 点击购物车修改数量

+ 17 - 16
pagesA/digitalShelf/components/otherPartItem.vue

@@ -39,22 +39,22 @@
 							<view class="item-detail-text flex align_center" v-if="item.currentInven">
 								<view 
 								class="qty-btn flex align_center justify_center"
-								@click="addPart(item)" 
+								@click="e=>{addPart(e,item)}" 
 								v-if="!item.checked"
 								>+</view>
 								<view class="flex align_center qty-box" v-else>
-									<view class="qty-btn" @click="changeQty(item.qty-1,item,1)" :class="(item.qty <= 0)?'qty-disabled':''">-</view>
-									<view class="qty-num" :style="{width:Number(item.qty).toString().length*8+8+'px'}"><input type="number" @blur="e=>changeQty(Number(e.detail.value),item,0)" v-model="item.qty"/></view>
-									<view class="qty-btn" @click="changeQty(item.qty+1,item,1)" :class="(item.qty >= item.currentInven)?'qty-disabled':''">+</view>
+									<view class="qty-btn" @click="e=>{changeQty(null,item.qty-1,item,1)}" :class="(item.qty <= 0)?'qty-disabled':''">-</view>
+									<view class="qty-num" :style="{width:Number(item.qty).toString().length*8+8+'px'}"><input type="number" @blur="e=>changeQty(null,Number(e.detail.value),item,0)" v-model="item.qty"/></view>
+									<view class="qty-btn" @click="e=>{changeQty(e,item.qty+1,item,1)}" :class="(item.qty >= item.currentInven)?'qty-disabled':''">+</view>
 								</view>
 								<!-- <uni-number-box v-else :hideInput="true" @change="e=>updateNums(e,item.productSn)" v-model="item.qty" :min="0" :max="item.currentInven"></uni-number-box> -->
 							</view>
 							<view class="item-detail-text flex align_center" v-else>
-								<text @click="addPart(item)" v-if="!item.checked" class="phtxt">我要急送</text>
+								<text @click="e=>{addPart(e,item)}" v-if="!item.checked" class="phtxt">我要急送</text>
 								<view class="flex align_center qty-box" v-else>
-									<view class="qty-btn" @click="changeQty(item.qty-1,item,1)" :class="(item.qty <= 0)?'qty-disabled':''">-</view>
-									<view class="qty-num" :style="{width:Number(item.qty).toString().length*8+8+'px'}"><input type="number" @blur="e=>changeQty(Number(e.detail.value),item,0)" v-model="item.qty"/></view>
-									<view class="qty-btn" @click="changeQty(item.qty+1,item,1)" :class="(item.qty >= 999)?'qty-disabled':''">+</view>
+									<view class="qty-btn" @click="e=>{changeQty(null,item.qty-1,item,1)}" :class="(item.qty <= 0)?'qty-disabled':''">-</view>
+									<view class="qty-num" :style="{width:Number(item.qty).toString().length*8+8+'px'}"><input type="number" @blur="e=>changeQty(null,Number(e.detail.value),item,0)" v-model="item.qty"/></view>
+									<view class="qty-btn" @click="e=>{changeQty(e,item.qty+1,item,1)}" :class="(item.qty >= 999)?'qty-disabled':''">+</view>
 								</view>
 								<!-- <uni-number-box v-else :hideInput="true" @change="e=>updateNums(e,item.productSn)" v-model="item.qty" :min="0"></uni-number-box> -->
 							</view>
@@ -109,25 +109,26 @@
 			openPriceModal(item, type, show) {
 				this.$emit('openPrice', item, type, show)
 			},
-			addPart(item) {
-				this.$emit('addPart', item, 'other')
+			addPart(e, item) {
+				this.$emit('addPart', e, item, 'other')
 			},
-			changeQty(value,item,type) {
+			changeQty(event,value,item,type) {
 				const max = item.currentInven || 999
 				const v = value > max ? max : value
 				if(value > max && type == 0){
-					this.updateNums(value,item.productSn)
+					this.updateNums(event,value,item.productSn)
 					this.$nextTick(()=>{
-						this.updateNums(v,item.productSn)
+						this.updateNums(event,v,item.productSn)
 					})
 				}else{
-					this.updateNums(v,item.productSn)
+					this.updateNums(value > max ? null : event,v,item.productSn)
 				}
 			},
-			updateNums(value, id) {
+			updateNums(event,value, id) {
 				this.$emit('updateNums', {
 					value: Number(value),
-					index: id
+					index: id,
+					event: event
 				})
 			},
 			viewImg(item) {

+ 17 - 16
pagesA/digitalShelf/components/vinPartItem.vue

@@ -43,22 +43,22 @@
 							<view class="item-detail-text" v-if="item.currentInven">
 								<view
 								class="qty-btn flex align_center justify_center"
-								@click="addPart(item)" 
+								@click="e=>{addPart(e,item)}" 
 								v-if="!item.checked"
 								>+</view>
 								<view class="flex align_center qty-box" v-else>
-									<view class="qty-btn" @click="changeQty(item.qty-1,item,1)" :class="(item.qty <= 0)?'qty-disabled':''">-</view>
-									<view class="qty-num" :style="{width:Number(item.qty).toString().length*8+8+'px'}"><input type="number" @blur="e=>changeQty(Number(e.detail.value),item,0)" :value="item.qty"/></view>
-									<view class="qty-btn" @click="changeQty(item.qty+1,item,1)" :class="(item.qty >= item.currentInven)?'qty-disabled':''">+</view>
+									<view class="qty-btn" @click="e=>{changeQty(null,item.qty-1,item,1)}" :class="(item.qty <= 0)?'qty-disabled':''">-</view>
+									<view class="qty-num" :style="{width:Number(item.qty).toString().length*8+8+'px'}"><input type="number" @blur="e=>changeQty(null,Number(e.detail.value),item,0)" :value="item.qty"/></view>
+									<view class="qty-btn" @click="e=>{changeQty(e,item.qty+1,item,1)}" :class="(item.qty >= item.currentInven)?'qty-disabled':''">+</view>
 								</view>
 								<!-- <uni-number-box v-else :hideInput="true" @change="e=>updateVinNums(e,item.productSn)" v-model="item.qty" :min="0" :max="item.currentInven"></uni-number-box> -->
 							</view>
 							<view class="item-detail-text" v-if="item.affiliation=='NON_SHELF'||!item.currentInven">
-								<text v-if="!item.checked" @click="addPart(item)" class="phtxt">我要急送</text>
+								<text v-if="!item.checked" @click="e=>{addPart(e,item)}" class="phtxt">我要急送</text>
 								<view class="flex align_center qty-box" v-else>
-									<view class="qty-btn" @click="changeQty(item.qty-1,item,1)" :class="(item.qty <= 0)?'qty-disabled':''">-</view>
-									<view class="qty-num" :style="{width:Number(item.qty).toString().length*8+8+'px'}"><input type="number" @blur="e=>changeQty(Number(e.detail.value),item,0)" :value="item.qty"/></view>
-									<view class="qty-btn" @click="changeQty(item.qty+1,item,1)" :class="(item.qty >= 999)?'qty-disabled':''">+</view>
+									<view class="qty-btn" @click="e=>{changeQty(null,item.qty-1,item,1)}" :class="(item.qty <= 0)?'qty-disabled':''">-</view>
+									<view class="qty-num" :style="{width:Number(item.qty).toString().length*8+8+'px'}"><input type="number" @blur="e=>changeQty(null,Number(e.detail.value),item,0)" :value="item.qty"/></view>
+									<view class="qty-btn" @click="e=>{changeQty(e,item.qty+1,item,1)}" :class="(item.qty >= 999)?'qty-disabled':''">+</view>
 								</view>
 								<!-- <uni-number-box v-else :hideInput="true" @change="e=>updateVinNums(e,item.productSn)" v-model="item.qty" :min="0"></uni-number-box> -->
 							</view>
@@ -120,25 +120,26 @@
 			openPriceModal(item, type, show) {
 				this.$emit('openPrice', item, type, show)
 			},
-			addPart(item) {
-				this.$emit('addPart', item, 'vin')
+			addPart(e,item) {
+				this.$emit('addPart', e, item, 'vin')
 			},
-			changeQty(value,item,type) {
+			changeQty(event,value,item,type) {
 				const max = item.currentInven || 999
 				const v = value > max ? max : value
 				if(value > max && type == 0){
-					this.updateNums(value,item.productSn)
+					this.updateNums(event,value,item.productSn)
 					this.$nextTick(()=>{
-						this.updateNums(v,item.productSn)
+						this.updateNums(event,v,item.productSn)
 					})
 				}else{
-					this.updateNums(v,item.productSn)
+					this.updateNums(value > max ? null : event,v,item.productSn)
 				}
 			},
-			updateNums(value, id) {
+			updateNums(event,value, id) {
 				this.$emit('updateVinNums', {
 					value: Number(value),
-					index: id
+					index: id,
+					event:event
 				})
 			},
 			viewImg(item){

+ 6 - 1
pagesB/cart/productitem.vue

@@ -312,13 +312,18 @@
 				height: 70px;
 				margin-left: 20px;
 			}
+			.choose-product-item-info .choose-product-item-left-info-guige{
+				margin: 10rpx 0 0 0;
+			}
 			.choose-product-item-info .choose-product-item-left-info-name{
 				font-size: 24rpx;
 				.rebate-tag{
-					margin: 0 5px 0 0;
+					margin: 0 5px 0 2px;
 					background: #ff0000;
 					color: #fff;
 					width: 30px;
+					display: flex;
+					justify-content: center;
 				}
 			}
 		}

+ 26 - 10
pagesB/shopiing/productDetail.vue

@@ -79,7 +79,7 @@
 					</view>
 				</view>
 				<view class="footer-right">
-					<view class="footer-right-item" @click="addCart">
+					<view class="footer-right-item" @click="addCart($event)">
 						<text>加入购物车</text>
 					</view>
 					<view class="footer-right-item" @click="toBuy">
@@ -89,6 +89,8 @@
 			</view>
 			<view :style="{height:safeAreaBottom+'px'}"></view>
 		</view>
+		<!-- 购物车动画 -->
+		<uni-cart-animation ref="cartAnimation" @animationfinish="animationfinish"></uni-cart-animation>
 		<!-- 立即购买 -->
 		<page-container
 		:show="showPopu" 
@@ -138,7 +140,7 @@
 							<view class="flex align_center rebate-tag">
 								赠品
 							</view>
-							<text class="ellipsis-one">{{detail.productName}}</text>
+							<text class="ellipsis-two">{{detail.productName}}</text>
 						</view>
 						<view class="popu-content-box justify_between">
 							<view></view>
@@ -169,6 +171,7 @@
 	export default {
 		data() {
 			return {
+				loading: false,
 				showPopu: false, // 弹框
 				statusBarHeight: 0, // 状态栏高度
 				safeAreaBottom: 0, // 底部安全区域高度
@@ -176,7 +179,11 @@
 				shopProductSn: null,
 				detail: null,
 				qty: 1,// 数量
-				isShare: false // 是否从分享打开的
+				isShare: false ,// 是否从分享打开的
+				busPos:{
+				    x: 10,
+				    y:uni.getSystemInfoSync().windowHeight - 30
+				}
 			}
 		},
 		onLoad(option) {
@@ -254,17 +261,21 @@
 					uni.hideLoading()
 				})
 			},
+			animationfinish(){
+				this.loading = false
+			},
 			// 加入购物车
-			addCart(){
+			addCart(event){
 				if(this.hasLogin){
 					// 游客未认证
 					if(this.userInfo.sysUserFlag == '0'){
 						toAuthStore()
 					}else{
-						uni.showLoading({
-							title: '加入中...',
-							mask: true
-						})
+						if(this.loading){
+							return
+						}
+						this.loading = true
+						this.$refs.cartAnimation.touchOnGoods(event,this.busPos);
 						// 加入购物车
 						addCart({
 							qty: 1,
@@ -273,7 +284,6 @@
 						}).then(res => {
 							if(res.status == 200){
 								uni.$emit('refashCart')
-								uni.hideLoading()
 							}
 						})
 					}
@@ -663,11 +673,17 @@
 						font-size: 12px;
 						color: #666;
 					}
+					.popu-content-box{
+						margin-top: 6px;
+					}
 					.rebate-tag{
 						margin: 0 5px 0 0;
 						background: #ff0000;
 						color: #fff;
-						width: 30px;
+						width: 50px;
+						flex-grow: 1;
+						display: flex;
+						justify-content: center;
 					}
 				}
 				.popu-content-box{