项目总博客见《Vue前端项目实战–移动商城–总结篇》https://blog.csdn.net/Jelly_11/article/details/113851138
这篇博客是记录项目中的首页开发过程。
附上效果图:
一、顶部导航栏navbar的封装
在components/common/navbar文件夹中新建NavBar.vue文件,设置三个插槽,用于用户设置导航栏左中右三块内容。首页中只需要用到中间这个插槽,在Home.vue中引用NavBar.vue。
详细代码:
- NavBar.vue
<template>
<div class='nav-bar' :style='{backgroundColor: navColor}'>
<div class='left'><slot name='left'></slot></div>
<div class='center'><slot name='center'></slot></div>
<div class='right'><slot name='right'></slot></div>
</div>
</template>
<script>
export default {
name: 'NavBar',
props: {
navColor: {
type: String,
default: 'var(--color-tint)'
}
},
}
</script>
<style scoped>
.nav-bar {
display: flex;
/*垂直居中*/
height: 44px;
line-height: 44px;
/*水平居中*/
text-align: center;
}
.left, .right {
width: 60px;
}
.center {
flex: 1;
}
</style>
- Home.vue
<template>
<div id='home'>
<!-- 顶部导航栏 -->
<nav-bar navColor='var(--color-tint)' class='home-nav'>
<div slot='center'>购物街</div>
</nav-bar>
</div>
</template>
<script>
import NavBar from "components/common/navbar/NavBar"
export default {
name: 'Home',
components: {
NavBar
}
}
</script>
<style scoped>
.home-nav {
color: #fff;
position: fixed;
/*否则center宽度不撑满整个屏幕*/
left: 0;
right: 0;
top: 0;
}
</style>
二、网络数据请求
使用axios进行网络数据请求,首先安装npm install axios --save
。
在network文件夹下建立request.js,封装网络请求函数,建立home.js用于存放首页中相关的网络请求函数,然后在Home.vue中引用home.js中函数,在home.js中引用request函数。
详细代码:
- request.js
import axios from 'axios'
export function request(config) {
//1.创建axios实例
const instance = axios.create({
/* 接口地址请加coderwhy老师微信*/
baseURL: 'http://152.136.185.210:7878/api/m5/',
timeout: 5000
})
//2.axios拦截器
//请求拦截
instance.interceptors.request.use(config => {
return config
}, err => {
console.log(err)
})
//相应拦截
instance.interceptors.response.use(res => {
return res.data
}, err => {
console.log(err)
})
//3.发送真正的网络请求
return instance(config)
}
- home.js
/* 存放首页的相关网络请求函数*/
import {
request} from './request'
export function getHomeMultidata() {
return request({
url: '/home/multidata'
})
export function getHomeGoods(type, page) {
return request({
url: '/home/data',
params: {
type,
page
}
})
}
- Home.vue
/* 网络请求*/
import {
getHomeMultidata} from "network/home"
export default {
name: 'Home',
components: {
NavBar
},
data() {
return {
/*轮播图数据*/
banners: [],
/*推荐数据*/
recommends: []
}
},
/* 数据网络请求在实例创建完成后进行 */
created() {
//1.请求多个数据
this.getHomeMultidata()
},
methods: {
/* 网络请求相关函数 */
getHomeMultidata() {
/*这时引用的home.js中的函数 */
getHomeMultidata().then(res => {
/*轮播图数据*/
this.banners = res.data.banner.list;
/*推荐数据*/
this.recommends = res.data.recommend.list;
})
}
}
}
</script>
三、轮播图
挂载完成后开启定时,延迟500秒后执行:
- 操作Dom:在前后各添加一张slide;
- 开启轮播定时。
操作Dom:
- 获取要操作的元素(
document.querySelector
、getElementsByClassName
); - 保存图片数/圆点个数;
- 如果图片数大于一个,则在前后分别添加一个slide [1,2,3,4]–[4’,1,2,3,4,1’](
cloneNode(true)
、insertBefore
、appendChild
),offsetWidth
返回元素的宽度,style
返回样式; - 让swiper元素, 显示第一个(目前是显示前面添加的最后一个元素),调用自定义
setTransform
函数,设置滚动到的位置。
handleDom() {
// 1.获取要操作的元素
//swiperEl:class为swiper的元素 有n个swiperItem class是swiper
//slidesELs:class为slide的元素 轮播中图在的那个div swiperItem
let swiperEl = document.querySelector('.swiper');
// let slidesEls = document.getElementsByClassName('slide');
let slidesEls = swiperEl.getElementsByClassName('slide');
//2.保存图片数/圆点个数
this.slideCount = slidesEls.length;
//3.如果图片数大于一个 则在前后分别添加一个slide [1,2,3,4]--[4',1,2,3,4,1']
if (this.slideCount > 1) {
//cloneNode方法将复制并返回调用它的节点的副本。
//如果传递参数是 true,将递归复制当前节点的所有子孙节点,否则只复制当前节点。
let cloneFirst = slidesEls[0].cloneNode(true);
let cloneLast = slidesEls[this.slideCount - 1].cloneNode(true);
//insertBefore()在指定的已有子节点之前插入新的子节点
//appendChild() 方法向节点添加最后一个子节点
swiperEl.insertBefore(cloneLast, slidesEls[0]);
swiperEl.appendChild(cloneFirst);
//offsetWidth返回元素的宽度(包括元素宽度、内边距和边框,不包括外边距)
this.totalWidth = swiperEl.offsetWidth;
this.swiperStyle = swiperEl.style;
}
// 4.让swiper元素, 显示第一个(目前是显示前面添加的最后一个元素)
this.setTransform(-this.totalWidth)
},
setTransform
函数(设置滚动到的位置):
setTransform(position) {
//transform 属性向元素应用2D或3D转换,对元素进行旋转、缩放、移动或倾斜。
// -webkit 是表示针对 safari 浏览器支持,-ms表示针对 IE 浏览器支持。
this.swiperStyle.transform = `translateX(${
position}px)`;
this.swiperStyle['-webkit-transform'] = `translateX(${
position}px)`;
this.swiperStyle['-ms-transform'] = `translateX(${
position}px)`;
},
开启轮播定时:
- window.setInterval每3000ms调用一次:
(1) 当前index数+1
(2)自定义函数scrollContent(-this.currentIndex * this.totalWidth)
滚动到正确的位置。
startTimer() {
// window.setInterval按照指定的周期(以毫秒计)来调用函数或计算表达式
this.playTimer = window.setInterval(() => {
this.currentIndex++;
this.scrollContent(-this.currentIndex * this.totalWidth);
},this.interval)
},
stopTimer() {
window.clearInterval(this.playTimer);
},
scrollContent
滚动到正确的位置:
scrollContent(currentPosition) {
// 1.设置正在滚动
this.scrolling = true;
// 2.开始滚动动画
//transition用于在一定的时间内平滑的过度
this.swiperStyle.transition = 'transform ' + this.animDuration + 'ms';
this.setTransform(currentPosition)
// 3.判断滚动到的位置,校验正确的位置
this.checkPosition();
// 4.滚动完成
this.scrolling = false
},
checkPosition()
校验正确的位置:
checkPosition() {
//延时等动画
window.setTimeout(() => {
// 1.校验正确的位置
//[1,2,3,4]--[4',1,2,3,4,1']当到达由1'变为1和4'变为4时需要瞬间变换
this.swiperStyle.transition = '0ms';
if (this.currentIndex > this.slideCount) {
this.currentIndex = 1;
this.setTransform(-this.currentIndex * this.totalWidth)
} else if (this.currentIndex == 0) {
this.currentIndex = this.slideCount;
this.setTransform(-this.currentIndex * this.totalWidth);
}
}, this.animDuration)
},
鼠标拖动事件的处理:
touchStart(e) {
// 1.如果正在滚动, 不可以拖动
if (this.scrolling) return;
// 2.停止定时器
this.stopTimer();
// 3.保存开始滚动的位置
this.startX = e.touches[0].pageX;
},
touchMove(e) {
// 1.计算出用户拖动的距离
this.currentX = e.touches[0].pageX;
this.distance = this.currentX - this.startX;
let currentPosition = -this.currentIndex * this.totalWidth;
let moveDistance = this.distance + currentPosition;
// 2.设置当前的位置
this.setTransform(moveDistance);
},
touchEnd(e) {
// 1.获取移动的距离
let currentMove = Math.abs(this