<template>
<div>
<div class="shopCart">
<div class="left">
<div class="cart">
<img src="./img/cart.png" alt="" />
<div class="num" v-show="totalCount>0">{{totalCount}}</div>
</div>
<p>¥{{totalPrice}}</p>
</div>
<div class="ball-container">
<div v-for="ball in balls">
<transition name="drop" @before-enter="beforeDrop" @enter="dropping" @after-enter="afterDrop">
<div class="ball" v-show="ball.show">
<div class="inner inner-hook"></div>
</div>
</transition>
</div>
</div>
<!--小球动画-->
<div class="right">
去结算
</div>
</div>
</div>
</template>
//底部购物车
<buycart :selectFoods="selectFoods"></buycart>
<!--底部购物车-->
</div>
</template>
<script>
import BScroll from 'better-scroll'
import buycart from './buyCart'
//载入与放置
props: {
selectFoods: {
type: Array,
default() {
return [
{
price: 10,
count: 1
}
];
}
},
},
//计算与获取 相关数据 比如价格啥的 从父组件那拿值 拿数组 后面会写
totalPrice() {
let total = 0;
this.selectFoods.forEach((food) => {
total += food.price * food.count;
});
return total;
},
//计算总值
totalCount() {
let count = 0;
this.selectFoods.forEach((food) => {
count += food.count;
});
return count;
},
//计算总数
}
//computed里的属性计算 selectfoods使用的是从上个页面传过来的值 后面会写出来
<div class="left">
<div class="cart">
<img src="./img/cart.png" alt="" />
<div class="num" v-show="totalCount>0">{{totalCount}}</div>
</div>
<p>¥{{totalPrice}}</p>
</div>
//将值放入 购物车页结束。。 selectsfoods与food.cound 后面会写入
<template>
<div class="cartcontrol">
<transition name="move">
<div class="cart-decrease">
<div class="inner" @click.stop.prevent="decreaseCart" v-show="food.count > 0">-</div>
</div>
</transition>
<div class="cart-count" v-show="food.count > 0">{{food.count}}</div>
<div class="cart-add" @click.stop.prevent="addCart">+</div>
</div>
</template>
//购物车控制页
<li style="display: flex;padding: 0.5rem 0;border-bottom: 1px solid #ccc;" v-for="items in item.shop_goods">
<img v-lazy='"http://jujiewang.oss-cn-hangzhou.aliyuncs.com/"+items.main_image' alt="" />
<div class="content">
<p class="name">{{items.goodname}}</p>
<p class="price">{{items.price}}/份</p>
<p class="tree">送{{items.orange}}颗桔树</p>
<div class="cartcontrol-wrapper">
<cartcontrol :food="items" @add="addFood"></cartcontrol>
</div>
<!--购物车+-按钮-->
</div>
</li>
//将foods值传入 传入的是循环的自己的数组
computed: {
totalPrice() {
let total = 0;
this.selectFoods.forEach((food) => {
total += food.price * food.count;
});
return total;
},
//计算总值
totalCount() {
let count = 0;
this.selectFoods.forEach((food) => {
count += food.count;
});
return count;
},
//计算总数
}
};
//点击+1-1 因为用了betterscroll 要先在上层允许点击 click: true 即用到betterscroll的区域
selectFoods() {
let foods = [];
if(this.goods.respond){
this.goods.respond.forEach((good) => {
good.shop_goods.forEach((food) => {
if (food.count) {
foods.push(food);
}
});
});
return foods;
//加个判断防止forEach报错
}
}
// 放入计算属性 给购物车循环出来的 上面写到的
<buycart :selectFoods="selectFoods" ref="shopcart" ></buycart>
//将selectFoods的值传入 结束 购物车与 + - 计算
data() {
return {
balls: [ // 5个小球 解决在前一个小球没有下落后又点击的问题
{
show: false
},
{
show: false
},
{
show: false
},
{
show: false
},
{
show: false
}
],
dropBalls: [],
fold: true // 默认折叠
};
},
//小球动画
//传给动画值
<div class="ball-container">
<div v-for="ball in balls">
<transition name="drop" @before-enter="beforeDrop" @enter="dropping" @after-enter="afterDrop">
<div class="ball" v-show="ball.show">
<div class="inner inner-hook"></div>
</div>
</transition>
</div>
</div>
//html
.ball-container {
.ball {
position: fixed;
left: 32px;
bottom: 22px;
z-index: 200;
.inner {
width: 16px;
height: 16px;
border-radius: 50%;
background: #FF9500;
}
&.drop-enter-active {
transition: all .4s cubic-bezier(.49, -0.29, .75, .41);
.inner {
transition: all .4s linear;
}
}
}
}
/*小球样式*/
//放入小球样式与html
this.$emit('add', event.target); // 触发当前实例上的事件,以便父元素@监听子元素。将点击的元素传入
//监听点击事件
dropBalls: [],
//存入数组控制
drop(el) { // 拿到cartcontrol点击的元素
for (let i = 0; i < this.balls.length; i++) {
let ball = this.balls[i];
if (!ball.show) { // 循环所有ball,找出未show
ball.show = true;
ball.el = el;
this.dropBalls.push(ball);
return;
}
}
},
//给与显示
<div class="inner inner-hook"></div>
//放入控制值
drop(el) { // 拿到cartcontrol点击的元素
for (let i = 0; i < this.balls.length; i++) {
let ball = this.balls[i];
if (!ball.show) { // 循环所有ball,找出未show
ball.show = true;
ball.el = el;
this.dropBalls.push(ball);
return;
}
}
},
// 小球执行步骤:
// 1. 点击cartcontrol组件中的”加号按钮“,$emit添加触发事件,将event.target作为参数,由goods父组件监听
// 2. goods组件接收事件和target,执行_drop(target),调用shopcart子组件的drop(el)方法,传入了target
// 3. shopcart 组件执行 drop(el) 方法时,获取el在视口中的位置,编程式调用ball元素的过渡hook接口,执行css过渡
// 过渡 钩子
// 动画的原理是先把它偏移到一个位置,然后按照一定的轨迹和时间完成动画,动画完成后要把位置重置
beforeDrop(el) { // el 是Vue 钩子选择作用动画的 DOM 对象
let count = this.balls.length;
while (count--) {
let ball = this.balls[count];
if (ball.show) {
let rect = ball.el.getBoundingClientRect(); // 得到元素距视口各边的偏移量
// 是相对于小球初始位置的,所以 x 是正的,y 是负的
let x = rect.left - 32;
let y = -(window.innerHeight - rect.top - 36);
el.style.display = ''; // 手动设置为空,小球会显示出来
el.style.webkitTransform = `translate3d(0,${y}px,0)`;
el.style.transform = `translate3d(0,${y}px,0)`;
let inner = el.getElementsByClassName('inner-hook')[0]; // 获取当前el的innerDom节点
inner.style.webkitTransform = `translate3d(${x}px,0,0)`;
inner.style.transform = `translate3d(${x}px,0,0)`;
}
}
},
//之前跌落
dropping(el, done) { // Vue enter 的第二个参数必须给,否则会设置成同步
/* eslint-disable no-unused-vars */
let rf = el.offsetHeight;
// 触发浏览器重绘,可以保证 dom 位置渲染正确后再执行之后的动画
this.$nextTick(() => {
el.style.webkitTransform = 'translate3d(0,-10px,0)';
el.style.transform = 'translate3d(0,-10px,0)';
let inner = el.getElementsByClassName('inner-hook')[0];
inner.style.webkitTransform = 'translate3d(0,0,0)';
inner.style.transform = 'translate3d(0,0,0)';
// 告知Vue,一个小球动画完成
el.addEventListener('transitionend', done); // 当动画结束,会有 CSS3 transitionend 事件派发
});
},
afterDrop(el) {
// 首先触发afterDrop是最先落的小球,所以把第一项也就是先落的小球重置
let ball = this.dropBalls.shift(); // 删除dropBalls第一项,并返回此项
if (ball) {
ball.show = false;
el.style.display = 'none';
}
},
//小球动画
//给予小球动画 我也不懂啊啊啊啊啊啊啊啊 啊啊