写在前面
本人自从事前端工作以来,每每遇到技术问题后,都是向百度/谷歌求教,久而久之便养成了伸手即来的‘本领’。。。可能是出于‘懒’的原因(没错,就是因为懒),从未发表过技术文章或开发经验,深感惭愧,故洗心革面,决心分享一些东西。
本文所述仅冰山一角,欢迎大家留言宝贵经验~。
1.需求
页面属于活动页面
a.进入页面会请求后台接口,拉取当前用户抽奖剩余次数
b.点击九宫格开始按钮,开始转动,并请求后台接口
c.接口请求成功,根据接口返回的停留位置停止转动
d.可连续抽奖,直至抽奖次数为0
大致展示效果如下
2.组件源码
template部分
{{item.info}}
按钮
您有{{luckyTimes}}次抽奖机会
script部分
1.接口说明:如果本地localhost启动,可以用setTimeout模拟post请求
2.函数说明:
move函数 为100ms转动,切最小转动次数为12(一圈半,也就是防止接口返回太快,刚开始转就停止),当接口返回成功后,记录当前index及服务器的返回停止位置stopIndex,并执行enter函数;
enter函数 100ms转动,接受参数,计算当前index与stopIndex的差值,如果>4,就可以执行递减,===4时;<=4,再次判断是否>-4(这个判断是为了计算多转几次),执行stop函数;
stop函数 为300ms转动,只转4次,然后完成整个流程;
3.对于setInterval,当组件销毁时,要清除定时器
import { post } from '@common/js/ajax';
import { deepQuery } from '@mfw/trans-core';
export default {
data() {
return {
// 奖单
list: [
{ img: 'https://n3-q.mafengwo.net/s12/M00/43/1C/wKgED1v__MqAPEuxAABG61CRQek46.jpeg', info: '谢谢参与', active: false },
{ img: 'https://b1-q.mafengwo.net/s12/M00/43/1C/wKgED1v__MqATFWOAAA8r6he_a471.jpeg', info: '北京-东京往返机票', active: false },
{ img: 'https://n3-q.mafengwo.net/s12/M00/42/2C/wKgED1v__AyAGkNKAAA33_Ke5y444.jpeg', info: '50元代金券', active: false },
{ img: 'https://n3-q.mafengwo.net/s12/M00/43/1C/wKgED1v__MqAPEuxAABG61CRQek46.jpeg', info: '马蜂窝护照包', active: false },
{ img: 'https://b1-q.mafengwo.net/s12/M00/43/1C/wKgED1v__MqATFWOAAA8r6he_a471.jpeg', info: '谢谢参与', active: false },
{ img: 'https://n3-q.mafengwo.net/s12/M00/42/2C/wKgED1v__AyAGkNKAAA33_Ke5y444.jpeg', info: '北京-札幌往返机票', active: false },
{ img: 'https://b1-q.mafengwo.net/s12/M00/43/1C/wKgED1v__MqATFWOAAA8r6he_a471.jpeg', info: '蚂蜂窝旅行箱', active: false },
{ img: 'https://n3-q.mafengwo.net/s12/M00/42/2C/wKgED1v__AyAGkNKAAA33_Ke5y444.jpeg', info: '马蜂窝日历', active: false },
],
// 上次停留位置 index
lastIndex: 0,
// 服务器返回停留位置 index
stopIndex: 0,
// 正常转动动画
timer1: null,
// 进入低速转动动画
timer2: null,
// 低速转动动画
timer3: null,
// 正在执行动画
isMoving: false,
// 转动次数 最小12次(一圈半) 防止转动时间过短
times: 0,
// 接口成功返回停止stopIndex 标识
ajaxEnd: false,
// 进入低速转动动画 标识
timerEnd: false,
// 用户抽奖次数(实际应该是后台返回的次数)
luckyTimes: 5,
};
},
watch: {
times(val) {
// 如果计数过程中,ajax未成功
if (!this.ajaxEnd) {
return false;
}
if (val >= 12) {
this.timerEnd = true;
}
},
},
methods: {
getStopIndex() { // 获取服务器返回的index
post('/lottery')
.then((res) => {
if (deepQuery(res, 'data', 'success')) {
this.stopIndex = deepQuery(res, 'data', 'data');
if (this.lastIndex !== this.stopIndex || this.lastIndex === this.stopIndex) {
this.ajaxEnd = true;
}
} else {
return Promise.reject(deepQuery(res, 'data', 'message'));
}
})
.catch((err) => {
// 初始化数据
clearInterval(this.timer1);
this.list.map((e) => {
e.active = false;
});
this.lastIndex = 0;
this.isMoving = false;
this.lastIndex = 0;
this.times = 0;
const errMsg = typeof err === 'string' ? err : '服务器繁忙,请稍后重试';
this.showToast(errMsg);
});
},
move() { // 点击开始游戏按钮
if (!this.luckyTimes) {
console.error('没有抽奖次数了');
return false;
}
if (this.isMoving) {
return false;
}
this.isMoving = true;
this.ajaxEnd = false;
this.timerEnd = false;
this.times = 0;
console.error('上次停止位置: ', this.lastIndex);
let i = this.lastIndex;
// 执行ajax请求数据
this.getStopIndex();
// 执行动画
this.timer1 = setInterval(() => {
this.times++;
i++;
if (i === 8) {
i = 0;
}
this.list.map((e) => {
e.active = false;
});
this.$set(this.list[i], 'active', true);
// 当获取到服务器数据 index
if (this.timerEnd) {
console.log('服务器返回的停止位置: ', this.stopIndex);
clearInterval(this.timer1);
this.enter(i, this.stopIndex);
}
}, 100);
},
enter(cur, stop) { // 计算需要停止的index
console.log(cur, stop);
let count = stop - cur;
if (count <= 4) {
count = count > -4 ? count + 8 : count + 16;
}
let i = cur;
this.timer2 = setInterval(() => {
count--;
i++;
if (i === 8) {
i = 0;
}
this.list.map((e) => {
e.active = false;
});
this.$set(this.list[i], 'active', true);
if (count === 4) {
clearInterval(this.timer2);
this.stop(i, stop);
}
}, 100);
},
stop(cur, stop) { // 计算需要停止的index
let count = 0;
let i = cur;
this.timer3 = setInterval(() => {
count++;
i++;
if (i === 8) {
i = 0;
}
this.list.map((e) => {
e.active = false;
});
this.$set(this.list[i], 'active', true);
if (count === 4) {
clearInterval(this.timer3);
this.isMoving = false;
this.lastIndex = this.stopIndex;
this.luckyTimes--;
}
}, 300);
},
showToast(err) { // toast 提示
alert(err);
},
},
beforeDestroy() { // 组件销毁前
// 清除定时器
clearInterval(this.timer1);
clearInterval(this.timer2);
clearInterval(this.timer3);
},
};
style部分
关于正方形布局,通过{width:100%;padding-bottom:100%;}实现
.v-common-com-lottery {
height: 100%;
font-size: .24rem;
color: #fff;
.list {
position: relative;
background: lightblue;
width: 100%;
padding-bottom: 100%;
}
.item {
width: 32%;
padding-bottom: 32%;
position: absolute;
background: lightgreen;
border-radius: .24rem;
overflow: hidden;
&.item0 {
left: 0;
top: 0;
}
&.item1 {
left: 34%;
top: 0;
}
&.item2 {
left: 68%;
top: 0;
}
&.item3 {
left: 68%;
top: 34%;
}
&.item4 {
left: 68%;
top: 68%;
}
&.item5 {
left: 34%;
top: 68%;
}
&.item6 {
left: 0;
top: 68%;
}
&.item7 {
left: 0;
top: 34%;
}
.icon {
position: absolute;
height: 100%;
width: 100%;
z-index: 1;
}
.info {
position: absolute;
left: 0;
bottom: 0;
background: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, .5));
height: .48rem;
width: 100%;
z-index: 2;
display: flex;
align-items: center;
justify-content: center;
}
.mask {
position: absolute;
left: 0;
top: 0;
z-index: 3;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, .4);
opacity: 0;
}
&.active .mask{
opacity: 1;
}
}
.btn {
width: 32%;
padding-bottom: 32%;
background: lightsalmon;
position: absolute;
left: 34%;
top: 34%;
font-size: .72rem;
border-radius: .24rem;
overflow: hidden;
.text {
position: absolute;
height: 100%;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
z-index: 2;
}
.mask {
position: absolute;
height: 100%;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
z-index: 2;
}
}
.lucky-times {
color: #000;
font-size: .36rem;
}
}
@common/js/ajax源码如下
// https://github.com/axios/axios
const axios = require('axios');
// 超时 15秒
// axios.defaults.timeout = 15000;
// 允许发送cookie
axios.defaults.withCredentials = true;
/**
* get请求
* @param {string} url - 请求的路径
* @param {Object} params - 参数
* @return {Promise}
*/
export function get(url, params = {}) {
return axios.get(url, {
params,
});
}
/**
* post请求
* @param {string} url - 请求的路径
* @param {Object} data - 参数
* @param {Object} config - 原始配置
* @return {Promise}
*/
export function post(url, data = {}, config = {}) {
return axios.post(url, data, config);
}
deepQuery源码如下
/**
* 深度查询 deepQuery({},"a","b","c")
* @param {any} func 要查询的对象
* @return {boolean} true or false
*/
export function deepQuery(obj, ...args) {
return args.reduce((a, b) => (a ? a[b] : a), obj);
}
3.写在后面
欢迎各位大佬指正
后续会分享自定义日历组件/城市地点选择组件等等,欢迎大家一起讨论学习!