时间:2020-10-20 15:08:29 | 栏目:vue | 点击:次
各位掘友,好久不见,最近利用工作之余开源了Vue电商项目,高仿某知名O2O买菜平台,整个项目做下来收获还是蛮多的,可以扫描下方二维码体验,本篇是项目的核心知识拆解篇,主要是拆解增加商品飞入购物车的实现过程。
首先我先简单的介绍下本项目所用到的技术栈:
整个项目采用 vue-cli3 脚手架搭建, Vue全家桶(vue、vuex、vue-router) 、 Vant UI框架 以及很多著名好用的第三方库如: axios 、 fastclick 、 better-scroll 、 twix.js 、 pubsub.js 、 moment.js 、 vue-amap 等等。 像素单位选择 rem ,后台数据接口通过 Easy-Mock 搭建,以最接近企业开发的方式,组件化模块化,最大程度实现高内聚低耦合,大大提升各模块的可维护及可扩展性,相信读完源码和我写的系列拆解文章,无论你是哪个段位的攻城狮都会有所启发!开源不易,你的点赞就是对我最大的鼓励:tada::tada:感谢~Thanks♪(・ω・)ノ
首先先来瞅瞅要做成的样子,是不是很炫酷:airplane::airplane:随手点赞支持下作者:heartbeat:
实现思路
首先我们先来把关注点放到加入购物车这块,当我们点击购物车图标的时候,这个时候会在当前点击的商品图片的中央范围先出现个圆形的商品缩略图,其次这个商品缩略图会沿着当前位置以曲线的形式逐渐变小飞入到购物车里,当商品飞入到购物车时,购物车的数字图标会增加,同时购物车会出现弹簧动画。
通过上面大白话分析整个实现步骤可简短分为三个阶段来完成编码:
第一阶段编码:绘制一个圆形的小球到商品的中间区域 这个非常简单,就是在商品图片区域加一个 div
,设置 position:fixed
及宽度高度等,但是呢?
先埋个问题,如何让小球出现在点击当前商品的范围内呢?
起初我通过计算属性遍历了商品列表,在所有的商品视图中加入商品缩略图小球,然后,通过给每个商品列表里增加一个boolean属性 show
,通过点击购物车图标来控制我刚创建的小球的显示与隐藏,然后自己就进入了死胡同,饶了半天,计算属性的数据通过 set
方式改变后,Dom死活不听话就是不按套路出牌(因为计算属性存在数据缓存),于是不得已放弃此笨拙的办法。
上面踩坑是因为贪多,想一次性到位,提前加载所有商品的小球缩略图,然后通过点击购物车来控制当前商品缩略图的显示与隐藏.那么我们要不换一种思路,仅创建一个缩略小球怎么样,通过 boolean
来控制它的显示和隐藏,位置动态变化,点击哪个商品就让他显示到哪个商品的范围内,并且每次点击给他设置个true的属性丢到一个数组中.好,我们先在data中定义小球是否显示的属性及显示小球的数组
data () { return { showMoveDot: [], //控制下落的小圆点显示隐藏 }
然后我们在点击购物车的时候来给 showMoveDot
数组动态增加属性,然后在Dom中遍历这个数组,双向绑定,最后通过 v-if
来控制显示和隐藏,这样是不是非常妙~~
点击购物车给 showMoveDot
增加 true
的属性
methods: { addToCart (product, num) { this.showMoveDot = [...this.showMoveDot, true]; } }
Dom中遍历 showMoveDot
,并且通过 v-if
来控制商品缩略小球的显示
<div v-for="(item,index) in showMoveDot" :key="index"> <div class="move_dot" ref="ball" v-if="item"> </div> </div>
此时点击购物车图标就会在当前商品中出现商品缩略图的小球,至此,阶段一大功告成 ✿✿?c(°?°)ノ✿ ~
本阶段需要用到动画知识,所以首先想到的是Vue的 transition 属性,首先给缩略图的小球包一层 transition
并且增加 appear
动画并且实现 beforeEnter
、 afterEnter
事件方法,所以此时修改Dom代码
<transition appear @before-appear="beforeEnter" @after-appear='afterEnter' v-for="(item,index) in showMoveDot" :key="index.id"> <div class="move_dot" ref="ball" v-if="item"> </div> </transition>
上面Dom代码的两个方法 beforeEnter
和 afterEnter
方法分别是动画初次渲染前和动画渲染后,那么我们就要把注意点放到这两个状态中. 在初次渲染的时候我们确定下小球的位置,那么这个时候我们就要用到一个方法 getBoundingClientRect ,这个方法返回的是一组矩形的集合,这下就好啦,可以通过这个方法来定位某个元素在屏幕中的位置啦:blush:~
那好,那我们这个时候就成热打铁,通过这个方法先来确定点击购物车图标的时候,购物车这个小图标距左边和顶部相对屏幕的距离。
定义两个 data
来接受点击增加购物车图标获取到的值.
data () { return { showMoveDot: [], //控制下落的小圆点显示隐藏 elLeft: 0, //当前点击购物车按钮在网页中的绝对top值 elTop: 0, //当前点击购物车按钮在网页中的绝对left值 }
然后我们在点击添加购物车的方法里获取位置。
methods: { addToCart () { this.showMoveDot = [...this.showMoveDot, true]; this.elLeft = event.target.getBoundingClientRect().left; this.elTop = event.target.getBoundingClientRect().top; } }
此时就获取到了点击加入购物车图标的位置啦:v:这个时候离成功进了一大半~ 获取到增加购物车图标的距离后,通过相对位置来确定商品缩略小球的位置,于是在动画渲染前我们设置下他的 transform
值,x,y的值自己可以调整,并且让他的透明度为0,暂时不显示.
beforeEnter (el) { // 设置transform值 el.style.transform = `translate3d(${this.elLeft - 30}px,${this.elTop - 100}px , 0)`; // 设置透明度 el.style.opacity = 0; },
接下来就是关键,如何让小球从当前位置移动到底部 Tabbar
的购物车中呢?同样的方法,我们通过 getBoundingClientRect
方法来确定底部Tabbar的购物车徽标的 left
和 top
值,获取到这个值后,就让小球在当前位置,以贝塞尔曲线的方式向购物车x和y的方向移动,当移动完成后将数组 showMoveDot
的属性设置为 false
且透明度为1.
在小球绘制完成后的方法中
afterEnter (el) { // 获取底部购物车徽标 const badgePosition = document .getElementById("buycar") .getBoundingClientRect(); // 设置小球移动的位移 el.style.transform = `translate3d(${badgePosition.left + 30}px,${badgePosition.top - 30}px,0)` // 增加贝塞尔曲线 el.style.transition = 'transform .88s cubic-bezier(0.3, -0.25, 0.7, -0.15)'; el.style.transition = 'transform .88s linear'; this.showMoveDot = this.showMoveDot.map(item => false); // 设置透明度 el.style.opacity = 1; }
此时大功告成!点击添加购物车按钮,小球可以曲线飞到购物车中了,来个Gif图炫耀下✿✿?c(°?°)ノ✿
掘友请留步(?i?s^?t?i)你以为这样就算完成了嘛~对于Geek:monkey_face:来说,这样的效果,简直太Low啦,于是乎,咱们继续一起来做个优化吧~
优化一:把小球变成点击当前商品的图片
刚开始还真TM把我给难住了,这么多图片,鬼知道显示哪个呢?后来灵机一动,不就是个动态加载图片嘛,点击加入购物车的时候当前的商品对象已经拿到了,你怕啥,直接取就完啦!~so easy:smile:好,思路有了,那咱就上代码!
1.在data中追加个 dropImage
属性.
data () { return { showMoveDot: [], //控制下落的小圆点显示隐藏 elLeft: 0, //当前点击购物车按钮在网页中的绝对top值 elTop: 0, //当前点击购物车按钮在网页中的绝对left值 dropImage: '' // 小球图片 }
2.在Dom中通过动态绑定的方式来获取 dropImage
<transition appear @after-appear='afterEnter' @before-appear="beforeEnter" v-for="(item,index) in showMoveDot" :key="index.id"> <div class="move_dot" ref="ball" v-if="item"> <!-- 小球图片 --> <img :src="dropImage" alt=""> </div> </transition>
3.动态给 dropImage
赋值
addToCart (product) { // 取出商品的图片 this.dropImage = product.small_image; // 将商品添加到购物车中 this.ADD_TO_CART(product); // 购物车左边的 this.elLeft = event.target.getBoundingClientRect().left; this.elTop = event.target.getBoundingClientRect().top; this.showMoveDot = [...this.showMoveDot, true]; },
好啦!此时我们就完成了小球图片的动态加载,来个Gif图炫一哈✿✿?c(°?°)ノ✿
但是有木有发现个问题,图片飞过去很突兀,直来直去的,不够友好,行那咱继续优化~~
哈哈~让商品飞入的时候逐渐变小,思来想去,还是用 css3
的 keyframes
来搞比较好,废话不多说直接上代码.
1.设置 keyframes
的值
@keyframes mymove { 0% { transform: scale(1); } 25% { transform: scale(0.8); } 50% { transform: scale(0.6); } 75% { transform: scale(0.4); } 100% { transform: scale(0.2); } }
animation
并加入 keyframes
img { animation: 0.88s mymove ease-in-out; width: 3rem; height: 3rem; border-radius: 50%; }
好嘞,搞定!来个Gif图炫一哈✿✿?c(°?°)ノ✿
为啥我觉得还是有点突兀呢,没办法,处女座,必须让他更完美:see_no_evil:
商品缩略小球逐渐变小的落入到购物车中,此时,购物车再来个弹簧效果就更美了,还是老办法用 css3
的 keyframes
再合适不过啦~
由于Tabbar是用的 Vant
UI组件,在单独的 Dashboard.vue
文件中,所以在 Dashboard.vue
文件中给购物车这个图标设置 keyframes
值.
@keyframes carmove { 0% { transform: scale(1); } 25% { transform: scale(0.8); } 50% { transform: scale(1.1); } 75% { transform: scale(0.9); } 100% { transform: scale(1); } }
并且把这个 keyframes
值设置给购物车这个图标
.moveToCart { animation: mymove 0.5s ease-in-out; }
购物车的动画是加完了,但是如何控制小球移入到购物车后让动画生效呢?于是先找到小球动画结束的方法,通过查资料找到了 transitionend
和 webkitAnimationEnd
两个方法,于是我对他们做了监听,当小球消失的时候在这两个方法中动态的增加Tabbar底部购物车的 keyframes
值.
afterEnter (el) { // 监听小球动画结束方法 el.addEventListener('transitionend', () => { el.style.display = 'none'; this.listenInCart(); }) // 监听小球动画结束方法 el.addEventListener('webkitAnimationEnd', () => { el.style.display = 'none'; this.listenInCart(); }) },
this.listenInCart() 方法是控制底部Tabbar购物车图标动画的方法,我们已经定义了一个class moveToCart ,我采用取巧的办法,当动画结束的时候,给Tabbar的购物车添加class moveToCart ,然后让他在500ms后在移除这个class,这样就会保证每次增加购物车后,底部Tabbar都会执行 keyframs 动画.
listenInCart () { // 拿到底部Tabbar购物车的DOM元素添加class document.getElementById("buycar").classList.add('moveToCart'); setTimeout(() => { // 500毫秒后移除底部Tabbar购物车的DOM元素class document.getElementById("buycar").classList.remove('moveToCart'); }, 500); }
此时算是真正的大功告成:cherry_blossom:,来个Gif图炫耀一哈✿✿?c(°?°)ノ✿
都看到这里啦,还不点赞支持下:smile:,鼓励下作者~ 三、重点来了:rocket::rocket:
我知道掘友们已经迫不及待的想看源码啦~当然本篇知识点只是整个项目的冰山一角,还有很多好用好玩的新技术,如 Better-scroll 滚动使用,高德地图的集成,图片瀑布流,移动端适配等等主流技术在这个项目中几乎都有~放个GitHub连接,掘友们可不要吝啬手中的小星星哦✿✿?c(°?°)ノ✿
:tada:Vue构建大型单页面电商应用 开源啦!点我看源码:rocket::rocket:
总结