【JavasScript】折腾一个基础到不能再基础的顺滑抽奖页面
前言
事情是这样的,作为一个意志力极低的人,最近一直在找寻提高意志力的方法。
然后决定试一试所谓的“建立奖励机制”,也就是说,完成一项意志力挑战后给自己一些奖励(具体操作方法不在这里进行赘述)。
那么,一款丝滑的“抽奖页面”也就理所当然加入了我的(瞎)待(折)办(腾)事项中。
整个过程还是比较简单的,涉及到的知识也相对基础,作为这个博客的第一篇技术文章,再加上作为一个技术小白,写的相对简单。希望能带个大家些许帮助,如果有建议批评也欢迎大家踊跃反馈。
涉及到的技术栈/框架
- 前端开发基础知识(Html/Css/JavaScript)
- Javascript ES6语法
- Vue.js相关技术知识
- JavaScript异步知识
基础代码
先上一段整体的基础代码
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lottery</title>
<style>
#app {
margin: auto;
width: 200px;
text-align: center;
}
</style>
</head>
<body>
<div id="app">
<p style="height:22px">{{item}}</p>
<button @click="start">开始!</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</body>
<script>
const app = new Vue({
el: "#app",
data: {
list: ['1积分', '5积分', '10积分', '20积分', '谢谢惠顾', '再来一次'],
item: "开始抽奖!"
},
methods: {
start() {
for (let index = 0; index < 50; index++) {
setTimeout(() => {
this.item = this.list[Math.round(Math.random() * (this.list.length - 1))]
}, 100 + index * 100)
}
}
}
})
</script>
</html>
可以看到整体页面元素是再简单不过的,页面上除了 最外层的div元素 之外,就只剩一个用于启动抽奖的 按钮 和一个用于显示当前Item的 段落元素。通过外链的方式引用了 Vue框架。
data中,list数组 作为奖池,用于放置可以被抽取到的item,item变量 则用来指明当前抽取到的item并与用于显示当前item的 段落元素 进行绑定 ,在 start方法 执行之前,item变量 默认值为“开始抽奖!”。
methods中只有一个 start方法 用于启动抽奖。通过 for循环 ,进行50次的抽取。setTimeOut 将抽取时间控制在100毫秒,也就是说每100毫秒进行一次抽取,从 list数组 中随机抽取值替换当前的 item变量。
效果如下图️
需求
我们现有的基础功能毫无疑问是过于简陋的,僵硬的抽取过程,无严谨可言的抽取规则等等,让我们的抽奖系统现在看起来就像是并夕夕的 “赢取百万现金”轮盘一样透露和不可言说的廉价感,那么“如何改善”就显得尤为关键了。
- 优化抽取过程,使抽取过程流畅不那么僵硬
- 优化抽取规则,使抽取规则更为严谨
- 优化完善部分细节
完善过程
优化抽取过程
由于item抽取的间隔时间都为固定的100毫秒,看起来很呆板不丝滑,所以对抽取的间隔时间做出优化,让它看起来丝滑流畅一些。
- start 方法的更新:
async start() {
for (let index = 0; index < 50; index++) {
this.item = await new Promise(resolve => {
setTimeout(() => {
resolve(this.list[Math.round(Math.random() * (this.list.length - 1))])
}, Math.round(this.getTime(index)))
})
}
},
将异步函数 setTimeOut 进行 await 异步处理,让间隔时间可以叠加方便我们进行间隔时间的控制。由于使用了es6语法中的 await 关键字,所以此函数加上了 async 前缀。间隔时间使用新添加的函数 getTime 传参获取。
- 新增 getTime 方法
getTime(index) {
// 初始/正常间隔时间
let time = 80
// 启动间隔时间处理
if (index <= 10) {
time = time + (11 - index) * 20
}
// 结束间隔时间的处理
if (index >= 39) {
time = time + (index - 39) * 20
if (index === 49) time = 1000
}
return time
}
通过参数 index 获取当时的循环次数,并计算出本次对应的 间隔时间 进行返回。(简单写的一个方法,且只针对于本项目,以后有空再进行封装完善。间隔时间的规律算法只经过了作者简单的计算,让它看起来比较符合作者对“顺滑”的定义,大家要是不喜欢也可以自己去尝试更改。毕竟现在是实验项目,能用就行)
优化抽取规则
由于抽取的规则并没有添加任何限制,导致项目抽取item的时候很可能抽取到的item与上一个相同,那就导致item在显示切换的时候出现问题,看起来好像并没有进行抽取。
- start 方法的更新:
async start() {
for (let index = 0; index < 50; index++) {
this.item = await new Promise(resolve => {
setTimeout(() => {
resolve(this.getItem())
}, Math.round(this.getTime(index)))
})
}
},
不再是简单的返回抽取结果,而是返回 getItem 方法计算出的item,对抽取的item结果添加限制。
- 新增 getItem 方法
getItem() {
let newItem;
do {
newItem = this.list[Math.round(Math.random() * (this.list.length - 1))]
} while (this.item === newItem);
return newItem
}
如果抽取到与上一个item相同的结果,那么就进行重复抽取,直到抽取到不同的结果为止。
优化细节
data: {
startButtonDisabled: false,
finished: false,
list: ['1积分', '5积分', '10积分', '20积分', '谢谢惠顾', '再来一次'],
item: "开始抽奖!"
},
在 data 中加入 startButtonDisabled 和 finished 用来分别记录按钮状态以及开奖的状态。
handleStart(index) {
if (index === 0) {
this.finished = false
this.startButtonDisabled = true
}
},
handleEnd(index) {
if (index === 49) {
this.finished = true
this.startButtonDisabled = false
}
}
添加函数 handleStart 和 handleEnd对 start 方法中循环体的首尾添加事件进行细节上的优化。在传入参数 index 为0(循环开始)时,利用方法 handleStart 将 开奖状态 改变为未开奖,按钮禁用,且在传入参数为49(循环结束)时,利用方法 handleEnd 再次改变其状态。
async start() {
for (let index = 0; index < 50; index++) {
this.handleStart(index)
this.item = await new Promise(resolve => {
setTimeout(() => {
resolve(this.getItem())
}, Math.round(this.getTime(index)))
})
this.handleEnd(index)
}
},
在 start 方法的循环体首尾调用 handleStart 和 handleEnd 方法。
<div id="app">
<p :class="{active:finished}" style="height:22px">{{item}}</p>
<button :disabled="startButtonDisabled" @click="start">开始!</button>
</div>
将页面元素进行修改,使对应元素会随着 data 中数据状态的改变而产生变化。
p.active {
font-weight: bold;
color: red
}
最后为开奖状态为true的 active 类添加css样式,优化完成!
最终代码
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lottery</title>
<style>
#app {
margin: auto;
width: 200px;
text-align: center;
}
p.active {
font-weight: bold;
color: red
}
</style>
</head>
<body>
<div id="app">
<p :class="{active:finished}" style="height:22px">{{item}}</p>
<button :disabled="startButtonDisabled" @click="start">开始!</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</body>
<script>
const app = new Vue({
el: "#app",
data: {
startButtonDisabled: false,
finished: false,
list: ['1积分', '5积分', '10积分', '20积分', '谢谢惠顾', '再来一次'],
item: "开始抽奖!"
},
methods: {
async start() {
for (let index = 0; index < 50; index++) {
this.handleStart(index)
this.item = await new Promise(resolve => {
setTimeout(() => {
resolve(this.getItem())
}, Math.round(this.getTime(index)))
})
this.handleEnd(index)
}
},
getTime(index) {
// 初始/正常间隔时间
let time = 80
// 启动间隔时间处理
if (index <= 10) {
time = time + (11 - index) * 20
}
// 结束间隔时间的处理
if (index >= 39) {
time = time + (index - 39) * 20
if (index === 49) time = 1000
}
return time
},
getItem() {
let newItem;
do {
newItem = this.list[Math.round(Math.random() * (this.list.length - 1))]
} while (this.item === newItem);
return newItem
},
handleStart(index) {
if (index === 0) {
this.finished = false
this.startButtonDisabled = true
}
},
handleEnd(index) {
if (index === 49) {
this.finished = true
this.startButtonDisabled = false
}
}
}
})
</script>
</html>
效果如下️
结语
一个简单到不简单的前端例子而已,没有进行完善的封装,就这样发布了。希望嫩个够对大家有所帮助。以后我会尽量经常发布一些有营养的技术案例等等同时也会记录我在学习工作过程中遇到的有趣的知(坑)识。
感谢阅读,祝你生活愉快。
【JavasScript】折腾一个基础到不能再基础的顺滑抽奖页面的更多相关文章
- 记一个社交APP的开发过程——基础架构选型(转自一位大哥)
记一个社交APP的开发过程——基础架构选型 目录[-] 基本产品形态 技术选型 最近两周在忙于开发一个社交App,因为之前做过一点儿社交方面的东西,就被拉去做API后端了,一个人头一次完整的去搭这么一 ...
- 【转】手摸手,带你用vue撸后台 系列四(vueAdmin 一个极简的后台基础模板)
前言 做这个 vueAdmin-template 的主要原因是: vue-element-admin 这个项目的初衷是一个vue的管理后台集成方案,把平时用到的一些组件或者经验分享给大家,同时它也在不 ...
- 「六」创建一个带 weblogic 服务的基础镜像
Weblogic Weblogic 简单介绍以及其在 Docker 环境下的特殊应用 WebLogic是美国Oracle公司出品的一个application server确切的说是一个基于JAVAEE ...
- 转:ORACLEERP开发基础之EBS开发基础
转自:http://blog.itpub.net/8781091/viewspace-1012244/ [内容导航] 第1页:开发工具安装 第2页:增加数据块 第3页:注册表单FORM 第4页:注册请 ...
- .NET基础拾遗(1)类型语法基础和内存管理基础
Index : (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理 (3)字符串.集合与流 (4)委托.事件.反射与特性 (5)多线程开发基础 (6)ADO.NET与数据库开发基 ...
- 基础拾掇之——http基础
基础拾掇之——http基础 http协议介绍 http:Hyper Text Transfer Protocol 超文本传输协议,是互联网应用最为广泛的一种网络协议,主要用于Web服务.通过计算机处理 ...
- 20165206学习基础和C语言基础调查
- 技能 我的一项可以拿的出手的技能是萨克斯.但不敢说有多厉害,更不敢说比大多数人更好,只能说是还可以.我学萨克斯有5年左右的时间吧,这5年里印象最深刻的还是前两年.前两年主要是基础训练.我从最基础的 ...
- 20165230 学习基础和C语言基础调查
20165230 学习基础和C语言基础调查 技能学习经验 我擅长弹钢琴.小时候我曾上过很多兴趣班,比如钢琴.跳舞.书法.绘画等等,唯一坚持至今的只有钢琴.仔细一算学习钢琴至今已有12年,不能说已经精通 ...
- 20165237 学习基础和C语言基础调查
学习基础和C语言基础调查 一.技能学习与特长 你有什么技能比大多人(超过90%以上)更好? 我的爱好和技能说实话挺广泛的.如果要挑出来一个很擅长的话,我觉得应该是钢琴. 针对这个技能的获取你有什么成功 ...
随机推荐
- 达梦数据库(DM8)大规模并行集群MPP 2节点安装部署
达梦数据库大规模并行集群MPP 2节点安装部署 1.环境准备 os 数据库版本 ip mpp角色 centos7.x86 DM8 192.168.30.100 mpp1 centos7.x86 ...
- 祝贺|合肥.NET俱乐部第二期技术沙龙活动圆满成功
热烈祝贺合肥.NET俱乐部第二期技术沙龙圆满成功,感恩参与活动的每一位小伙伴!正是因为有你们才促成了这次聚会的成功.现对此次活动进行简单回顾并附上精彩的活动图片,每一位参与活动者名单,以及此次活动讲师 ...
- synchronized锁代码块(七)
synchronized同步代码块 用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法执行一个较长时间的任务,那么B线程必须等待比较长的时间.这种情况下可以尝试使用 ...
- js循环修改对象内层元素的值
问题:存在一个对象,该对象的内部元素也为对象,子对象的元素也为对象,...(即多层对象构成的对象,具体如下),那么应该如何修改最内层元素的值(如 obj.a.a.a = 5)? var obj = { ...
- n皇后问题(回溯法)——Python实现
八皇后问题 问题: 国际象棋棋盘是8 * 8的方格,每个方格里放一个棋子.皇后这种棋子可以攻击同一行或者同一列或者斜线(左上左下右上右下四个方向)上的棋子.在一个棋盘上如果要放八个皇后,使得她们互 ...
- Maven的属性,${project.basedir},${project.build.directory}:项目构件输出目录,默认为 target/
内置属性 主要有两个常用内置属性:${basedir}项目的根目录(包含pom.xml文件的目录),${version}项目版本 POM属性 用户可以使用该属性引用POM文件中对应元素的值,常用的PO ...
- 凯撒密码(Caesar cipher) 详解
------------恢复内容开始------------ 最近训练CTF的时候,发现密码学这块的知识不太系统,所以自己接下来会陆陆续续整理出来 就先从古典密码中的凯撒密码说起吧 凯撒密码内容比较简 ...
- Java面向对象01——什么是面向对象
面向过程&面向对象 面向过程思想(微观): 步骤清晰简单,第一步做什么,第二部做什么....... 面向过程适合处理一些较为简单的问题 面向对象思想(宏观): 物以类聚,分类的思维模式,思考问 ...
- C++ //拷贝构造函数调用时机//1.使用一个已经创建完毕的对象来初始化一个新对象 //2.值传递的方式给函数参数传值 //3.值方式返回局部对象
1 //拷贝构造函数调用时机 2 3 4 #include <iostream> 5 using namespace std; 6 7 //1.使用一个已经创建完毕的对象来初始化一个新对象 ...
- 清晰易懂的RxJava入门实践
导入 我相信大家肯定对ReactiveX 和 RxJava 都不陌生,因为现在只要是和技术相关的网站,博客都会随处见到介绍ReactiveX和RxJava的文章. ReactiveX Reactive ...