过程分析
1.首先购物车弹窗是一个组件,因为会出现在不同的页面中。
2.因为很多组件会用到购物车数据,所以统一放到vuex中。
实现步骤解析
一、加入购物车
- 将购物车数据统一放在vuex中:
state里放置一个数组:carPanelData,里面放置购物车数据。
// 购物车商品数据
state: {
carPanelData: []
}
- 往购物车里push数据:在mutation里面更改state数据。
思路:首先需要将点击加入购物车的每一条数据加一个属性:count,计数,然后将这条点击的商品数据push到state中,当然,首先是需要先用商品ID和state里的商品ID比对,如果没有就push,如果有了,就计数。
以下是代码思路:
mutations:
addCarPanelData(state,加入购物车的数据data){
//循环carPanelData购物车数据
//如果商品ID存在(购物车的id和传进来的ID比对),就设置count++
//设置开关false
===================================================================
**注意** : 如果上面的条件成立,以下是不执行的,所以可以设置一个开关bOff
//如果开关值为true
//否则就是商品ID不存在,设置一个新的变量goodsData = 传进来的data;
//Vue.set(goodsData,'count',1):为这个变量设置count属性,值为1;
//将这个goodsData,push到carPanelData中;
}
--------------------------------------------------------------
mutations: {
addCarPanelData (state,data) {
let bOff = true
state.carPanelData.forEach((goods) => {
if (goods.sku_id === data.sku_id) {
goods.count++
bOff = false
}
})
if (bOff) {
let goodsData = data
Vue.set(goodsData,'count',1)
state.carPanelData.push(goodsData)
}
console.log(state.carPanelData)
}
}
- 在商品页选择了商品,点击加入购物车按钮:这时,将这条数据传给vuex,记录到state中(mutation里面已经对该逻辑进行了处理)
要点 : 记住vuex的思想,要想改变state,必须提交mutation。
methods:{
addCarPanelHandel(data){
//改变state,必须提交mutation,并将此条数据传给vuex
this.$store.commit('addCarPanelData',data)
}
}
- 这时已经基本完成了购物车的逻辑,下面,我们把购物车单独出来做成组件:car-panel。
这个时候就可以把相关数据绑定在购物车了。
要点 :如何获取在购物车组件内获取vuex数据?
用computed即可。
//相应的绑定代码=示例
<a href="#/item/100027401">{{item.title}})</a>
//获取vuex数据
computed : {
carPanelData(){
return this.$store.state.carPanelData
},
count(){
return this.$store.getters.totleCount
},
totle(){
return this.$store.getters.totlePrice
}
}
- 最后,对购物车中的商品数量和商品总价计算。
在vue中,我们需要对变量进行进一步处理,可以放在computed里,不建议放在模板中,同样,vuex中,state中的状态如果需要进一步处理,我们可以放入getters.
getters:{
// 购物车商品数量计算
totleCount (state) {
let count = 0
state.carPanelData.forEach((goods) => {
count += goods.count
})
return count
},
// 总价格
totlePrice (state) {
let price = 0
state.carPanelData.forEach((goods) => {
price += goods.price * goods.count
})
return price
}
}
============================================================
至此,已经完成了加购物车,并且计算数量和金额。
============================================================
二、购物车删除
一开始我的思路是:
1、删除数据肯定是要改变state,改变state肯定是需要提交mutation,所以删除的相关逻辑方法应该写在mutation;
2、当时我的问题是如何知晓删除的是哪一条数据?
通过学习,弄清楚了,以后此类需求,都和加入购物和思路是一致的,都是通过对比删除的当前的ID和数据里的所有ID进行比对,就知道是删除具体哪条数据了。
3、那么我需要记住,当前选择的是哪条数据,都是通过在删除的点击方法对应的事件里,参数中传递当前数据(商品ID)即可。这是一个思路,需要牢记。
具体实现步骤总结 :
点击购物车页面的删除商品按钮,绑定一个删除方法,参数传入当前被点击的商品ID,在这个方法里调用mutation里面的删除商品方法:
- 首先需要循环state的购物车数据;
- 比对每一项的商品ID是否和当前传入的ID相同,如果是相同的那么就return,不再继续循环了;
- 在state的购物车数据里删除这项ID相同的数据。
//mutation
delCarPanelData (state,id) {
state.carPanelData.forEach((goods,index) => {
if (goods.sku_id === id) {
state.carPanelData.splice(index,1)
return
}
})
}
//购物车组件中
methods: {
delCarPanelHandel(id){
this.$store.commit('delCarPanelData',id)
}
}
<div class="del-btn" @click="delCarPanelHandel(item.sku_id)"> 删除 </div>
三、购物车商品数量限制
思路 : 这类显示隐藏的案例,都是设置变量属性的ture/false
首先是有一个弹窗组件,当商品数量大于最大值得时候,这个组件需要弹出。
数据中已经有了最大值 : limit_num。
在state中定义一个变量:maxOff :false ,默认不显示,当购物车中商品增加的时候,比对当前商品的数量是否已经大于了limit_num,如果是,就让这个弹窗出来,也就是在mutation中设置该属性为true。
- 商品数量超过后显示弹窗
// 加入购物车
addCarPanelData (state,data) {
state.carPanelData.forEach((goods) => {
if (goods.sku_id === data.sku_id) {
goods.count++
bOff = false
//比较当前商品的数量和数据中的商品最大购买数量
if (goods.count > goods.limit_num) {
goods.count--
state.maxOff = true
}
}
})
}
//组件中
<div id="prompt" v-if="maxOff">
computed: {
maxOff(){
return this.$store.state.maxOff
}
}
- 关闭弹窗
//mutations
closePrompt (state) {
state.maxOff = false
}
//组件中
methods : {
closePrompt(){
this.$store.commit('closePrompt')
}
}
四、购物车显示隐藏
思路: 一样的,这类显示隐藏,需要设置一个开关,去切换开关即可。
state: {
carPanelData: [],
maxOff : false, // 弹窗开关
carShow : false, // 购物车开关
carTimer : null // 购物车定时器
},
// 购物车显示
showCar (state) {
clearTimeout(state.carTimer)
state.carShow = true
},
// 购物车隐藏
hideCar (state) {
state.carTimer = setTimeout(() => {
state.carShow = false
},1000)
}
===================
//组件中
methods: {
// 显示购物车
showCar(){
this.$store.commit('showCar')
},
// 隐藏购物车
hideCar(){
this.$store.commit('hideCar')
}
}
四、购物车小球效果
思路:用的vue的transtion钩子函数,原理就是先把小球写死到购物车,点击的时候瞬间移入到需要的位置,然后做一个过渡动画即可,加入贝塞尔曲线。
state: {
ball: { // 购物车小球
show: false,
el: null, // 点击的是哪个购物车按钮
img: ''
}
mutations: {
// 加入购物车
addCarPanelData (state,data) {
// 加上这个条件,确保小球飞完再添加
if (!state.ball.show) {
// 显示购物车
state.carShow = true
let bOff = true
state.carPanelData.forEach((goods) => {
// 比对ID,相同就说明购物车已存在此商品,数量增加
if (goods.sku_id === data.sku_id) {
goods.count++
bOff = false
if (goods.count > goods.limit_num) {
goods.count--
state.maxOff = true
return
}
// 加入购物车,小球显示
state.ball.show = true
state.ball.img = data.ali_image
// 通过event对象获取到当前点击的按钮
state.ball.el = event.path[0]
}
})
// 商品不存在,就往数组里新增商品数据
if (bOff) {
let goodsData = data
Vue.set(goodsData,'count',1)
state.carPanelData.push(goodsData)
// 加入购物车,小球显示
state.ball.show = true
state.ball.img = data.ali_image
// 通过event对象获取到当前点击的按钮
state.ball.el = event.path[0]
}
console.log(event)
}
}
============
//组件内
// 小球进入前,初始化
beforeEnter(el){
// 获取按钮的位置
let rect = this.ball.el.getBoundingClientRect()
// 获取购物车
let rectEl = document.getElementsByClassName('ball-rect')[0].getBoundingClientRect()
// 获取小球
let ball = document.getElementsByClassName('mask-item')[0]
//计算按钮和购物车的差值 : 购物车的中心点到左侧的距离 - 按钮中心点到左侧的距离
let x = (rectEl.left + 16) - (rect.left + rect.width/2)
let y = rect.top + rect.height/2 - rectEl.top + 5 - 16
//计算小球和包着小球的父级的位置
ball.style.transform = 'translate3d(-'+x+'px,0,0)'
el.style.transform = 'translate3d(0,'+y+'px,0)'
ball.src = this.ball.img
console.log(this.ball.img)
},
//开始运动
enter (el){
let a = el.offsetHeight
// 获取小球
let ball = document.getElementsByClassName('mask-item')[0]
el.a = a //避免变量没有使用,eslint报错
el.style.transform = "translate3d(0,0,0)"
ball.style.transform = "translate3d(0,0,0)"
},
// 结束,让小球隐藏
afterEnter (el){
this.ball.show = false
}
}
===========
<transition
name="ball"
v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:after-enter="afterEnter"
v-bind:css="true"
>
<div class="addcart-mask" v-show="ball.show">
<img class="mask-item" src="" alt="">
</div>
</transition>
==========
.ball-enter-active{
transition: 1s cubic-bezier(.18,1,.94,1)
}
.ball-enter-active .mask-item{
transition: 1s cubic-bezier(0,0,0,0)
}