最近用vue模仿饿了么,写一个外卖商城webApp
先上效果图:
html部分
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<title>menu-bar</title>
<link rel="stylesheet" href="./css/font/iconfont.css">
<link rel="stylesheet" href="./css/style.css">
<script type="text/javascript" src="./js/common/mock.js"></script>
<script type="text/javascript" src="./js/common/axios.js"></script>
<script type="text/javascript" src="./js/common/vue.js"></script>
<script type="text/javascript" src="./css/font/iconfont.js"></script>
<script type="text/javascript" src="./js/index.js"></script>
<script type="text/javascript" src="./js/menu.js"></script>
</head>
<body>
<div id="menus">
<div class="menu-list">
<ul
class="menus"
@touchstart="touchStart"
@touchmove="touchMove"
@touchend="touchEnd"
ref="menus">
<li class="menus-item" v-for="(menu, i) in menuList" :key="i">
<i class="iconfont" :class="menu.url"></i>
<span>{{menu.title}}</span>
</li>
</ul>
</div>
<div class="circle" v-if="menuList.length>0">
<span v-for="j in menuPage" :key="j" :class="{spanactive: j===circleIndex}"></span>
</div>
</div>
</body>
</html>
CSS部分
* {
margin: 0;
padding: 0;
list-style: none !important;
}
html {
width: 100%;
}
.menu-list {
overflow: hidden;
}
.menus {
position: relative;
width: 200%;
display: flex;
flex-wrap: wrap;
font-size: 1.1em;
text-align: center;
}
.menus-item {
width: 12.5%;
cursor: pointer;
}
.menus-item i {
display: block;
}
.iconfont{
color: coral;
margin-bottom: 4px;
font-size: 30px;
}
.menus-item span {
font-size: 18px;
color: #aaaaaa;
}
.circle {
width: 100%;
text-align: center;
}
.circle span {
display: inline-block;
width: 5%;
height: .15em;
margin-right: 1%;
margin-bottom: 2%;
background-color: #cccccc;
}
.circle .spanactive {
background-color: #0292ff;
}
vue部分
window.onload = () => {
new Vue({
el: '#menus',
data: {
menuList: [],
listWidth: '',
num: 8,
ulIndex: 0,
circleIndex: 1,
startX: 0,
moveX: 0
},
mounted () {
this.getMenuList()
this.listWidth = window.innerWidth
console.log(this.listWidth)
console.log(this.$refs.menus.style.transform + 10)
},
computed: {
curTranslateX () {
return -this.listWidth * this.ulIndex
},
// 计算页数
menuPage () {
return Math.ceil(this.menuList.length / this.num)
}
},
watch: {
circleIndex () {
if (this.circleIndex > this.menuPage) this.circleIndex = this.menuPage
if(this.circleIndex < 1) this.circleIndex = 1
}
},
methods: {
getMenuList () {
axios({
methods: 'get',
url: '/api/menuList'
}).then(res => {
// console.log(res.data.menuList)
this.menuList = res.data.menuList
}).catch(err => {
console.log(err)
})
},
animateEnd () {
if(this.ulIndex > this.menuPage - 1) {
this.ulIndex = this.menuPage - 1
this.$refs.menus.style.transition = 'none'
this.$refs.menus.style.transform = `translateX(${this.menuList}px)`
}
if(this.ulIndex < 0) {
this.ulIndex = 0
this.$refs.menus.style.transition = 'none' // 过渡动画
this.$refs.menus.style.transform = `translateX(${this.curTranslateX}px)` // 调整相对位置
}
},
touchStart (event) {
this.startX = event.targetTouches[0].clientX
},
touchMove (event) {
this.moveX = event.targetTouches[0].clientX - this.startX
this.$refs.menus.style.transition = 'none'
this.$refs.menus.style.transform = `translateX(${this.curTranslateX + this.moveX}px)`
},
touchEnd () {
if(this.moveX > this.listWidth / 3 && this.ulIndex > 0) {
this.ulIndex--
this.circleIndex--
}else if(this.moveX < -this.listWidth / 3 && this.ulIndex < this.menuPage - 1) {
this.ulIndex++
this.circleIndex++
}
console.log(this.startX, this.moveX)
this.$refs.menus.style.transition = 'all .5s'
this.$refs.menus.style.transform = `translateX(${this.curTranslateX}px)`
this.moveX = 0
}
}
})
}
总结,总的来说要实现动画必须先开启定位,移动端的触摸事件包括touchstart,touchmove,touchend三个,touchstart记录开始的触摸点坐标,touchmove算出滑动的距离,touchend可以进行最终过渡动画的操作,相关的状态属性也是很重要的,比如startX,moveX,要明白需要那些状态属性