项目呢是模仿的王者荣耀抽奖界面的一部分。。。
|-- app.js
|-- router.js 路由接口文件
|-- controllers 控制器
|-- models
|-- node_modules 第三方包
|-- package.json 包描述文件
|-- package-lock.json 第三方包版本锁定文件(npm 5 以后才有)
|-- public 公共的静态资源
|-- README.md 项目说明文档
|-- routes
|-- views 存储视图目录
本项目包含用户注册、登录以及抽奖等功能:
用户注册功能
a. 提供手机号验证码注册功能: 用户输入手机号,对应手机号接收验证码
b.输入完成手机号和验证码之后进入下一步,允许用户输入密码以及确认密码:密码要求12~16位的数字、大小写字母以及符号
c. 注册成功后显示用户抽奖界面(注: 页面不刷新)
用户登录功能
a. 提供手机号和密码、手机号和验证码两种方式登录
b. 判断如果用户没有注册的话,直接跳转用户注册界面(注: 页面不刷新)
c. 登录成功后显示用户抽奖界面(注: 页面不刷新)
用户抽奖功能
a. 每个用户—次拥有三次抽奖机会
b. 抽奖方式采用转盘方式抽奖:不同奖项的得奖率不同(转盘抽奖可参考百度)
c. 三次抽奖完成后在界面显示三次抽奖汇总结果
由于要求是页面不跳转,所以应该是只写一个路由即可
路径 | 方法 | get参数 | post参数 | 是否需要登录 | 备注 |
---|---|---|---|---|---|
/ | GET | 渲染首页 |
一、先把静态页面写好
HTML完整代码(views/index.html)
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>京东抽奖title>
<link rel="stylesheet" href="../public/css/style.css" />
<link rel="stylesheet" href="../public/css/login.css" />
<link rel="stylesheet" href="../public/css/main.css" />
head>
<body>
<div class="loginBox">
<h2>Loginh2>
<form action="#" method="" id="login">
<div class="item">
<input type="text" name="phone" id="phone" required />
<label for="">Phonelabel>
div>
<div class="item">
<input type="password" name="password" id="password" required />
<label for="">PassWordlabel>
div>
<button class="btn">
submit
<span>span>
<span>span>
<span>span>
<span>span>
<input type="submit" value="" id="submit" />
button>
form>
div>
<table>
<tr>
<td class="prize prize_0" name="阿古朵">
<img
class="jpg_ jpg_0"
src="https://cdn.jsdelivr.net/gh/extheor/images/Ajax%E5%9B%BE%E7%89%87/jpg_1.jpg"
alt=""
/>
td>
<td class="prize prize_1" name="萌芽">
<img
class="jpg_ jpg_1"
src="https://cdn.jsdelivr.net/gh/extheor/images/Ajax%E5%9B%BE%E7%89%87/jpg_2.jpg"
alt=""
/>
td>
<td class="prize prize_2" name="蒙恬">
<img
class="jpg_ jpg_2"
src="https://cdn.jsdelivr.net/gh/extheor/images/Ajax%E5%9B%BE%E7%89%87/jpg_3.jpg"
alt=""
/>
td>
tr>
<tr>
<td class="prize prize_7" name="华硕笔记本电脑">
<img
class="jpg_ jpg_7"
src="https://cdn.jsdelivr.net/gh/extheor/images/Ajax%E5%9B%BE%E7%89%87/jpg_4.jpg"
alt=""
/>
td>
<td class="button">抽奖td>
<td class="prize prize_3" name="运动鞋一双">
<img
class="jpg_ jpg_3"
src="https://cdn.jsdelivr.net/gh/extheor/images/Ajax%E5%9B%BE%E7%89%87/jpg_5.jpg"
alt=""
/>
td>
tr>
<tr>
<td class="prize prize_6" name="摄像机">
<img
class="jpg_ jpg_6"
src="https://cdn.jsdelivr.net/gh/extheor/images/Ajax%E5%9B%BE%E7%89%87/jpg_6.jpg"
alt=""
/>
td>
<td class="prize prize_5" name="联想笔记本电脑">
<img
class="jpg_ jpg_5"
src="https://cdn.jsdelivr.net/gh/extheor/images/Ajax%E5%9B%BE%E7%89%87/jpg_7.jpg"
alt=""
/>
td>
<td class="prize prize_4" name="谢谢惠顾">
<img
class="jpg_ jpg_4"
src="https://cdn.jsdelivr.net/gh/extheor/images/Ajax%E5%9B%BE%E7%89%87/jpg_8.jpg"
alt=""
/>
td>
tr>
table>
<div class="coverLottery">div>
<div class="lotteryResult">
<div class="msgbox">div>
div>
<div class="main">div>
<script
type="text/javascript"
src="https://cdn.bootcdn.net/ajax/libs/jquery/1.9.1/jquery.min.js"
>script>
<script src="../node_modules/axios/dist/axios.js">script>
<script src="../public/js/axios_default.js">script>
<script src="../public/js/main.js">script>
<script src="../public/js/login.js">script>
<script src="../public/js/index.js">script>
body>
html>
首页CSS代码(style.css)
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
height: 100%;
overflow: hidden;
}
input,
button {
background: transparent;
border: none;
outline: none;
}
body {
height: 100vh;
background: linear-gradient(#141e30, #243b55);
display: flex;
justify-content: center;
align-items: center;
font-size: 16px;
color: #03e9f4;
}
/* 抽奖样式 */
table {
width: 300px;
height: 300px;
/* margin: 50px auto; */
text-align: center;
border-radius: 10px;
position: absolute;
left: 40%;
top: 30%;
display: none;
}
td {
/* border: 1px solid #900; */
color: #fff;
border-radius: 10px;
}
.prize {
background: #fff;
}
.button {
cursor: pointer;
}
.jpg_ {
width: 100px;
height: 100px;
border-radius: 10px;
opacity: 0.5;
}
.coverLottery {
height: 200px;
width: 200px;
/* background: red; */
/* margin: -307px auto; */
position: absolute;
left: 45%;
top: 35%;
display: none;
}
.lotteryResult {
width: 800px;
height: 500px;
background: #325c8e;
margin: 25px auto;
border-radius: 20px;
position: relative;
display: none;
}
.lotteryResult > .jpg_ {
width: 100px;
height: 100px;
position: absolute;
top: 150px;
}
.lotteryResult > img:nth-child(2) {
left: 100px;
}
.lotteryResult > img:nth-child(3) {
left: 350px;
}
.lotteryResult > img:nth-child(4) {
left: 600px;
}
.lotteryResult > .msgbox {
width: 500px;
height: 200px;
background: #000;
color: rgb(170, 170, 73) !important;
position: absolute;
left: 140px;
top: 260px;
border-radius: 20px;
text-align: center;
padding-top: 50px;
font-size: 24px;
color: #fff;
/* display: none; */
}
/* 注册样式 */
登录页CSS样式(login.css)
/* 登录样式 */
.loginBox {
width: 450px;
height: 400px;
background: rgba(0, 0, 0, 0.5);
box-shadow: 0px 15px 25px 0 rgba(0, 0, 0, 0.6);
padding: 40px;
}
h2 {
text-align: center;
color: #fff;
margin-bottom: 30px;
font-weight: 800;
}
.item {
height: 45px;
border-bottom: 1px solid #fff;
margin-bottom: 40px;
position: relative;
}
.item input {
width: 100%;
height: 100%;
color: #fff;
padding-top: 20px;
}
.item input:focus + label,
.item input:valid + label {
color: #fff;
font-size: 12px;
top: 0;
}
.item label {
position: absolute;
left: 0;
top: 12px;
transition: all 0.5s;
}
.btn {
padding: 10px 20px;
margin-top: 30px;
color: #03e9f4;
text-transform: uppercase;
letter-spacing: 2px;
position: relative;
overflow: hidden;
transition: all 0.2s;
}
.btn:hover {
background: #03e9f4;
border-radius: 5px;
color: #fff;
box-shadow: 0 0 5px 0 #03e9f4, 0 0 25px 0 #03e9f4, 0 0 50px 0 #03e9f4,
0 0 100px 0 #03e9f4;
}
.btn > span {
position: absolute;
}
.btn > span:nth-child(1) {
width: 100%;
height: 2px;
background: -webkit-linear-gradient(left, transparent, #03e9f4);
left: -100%;
top: 0;
animation: line1 1s infinite;
}
#submit {
width: 108px;
height: 38px;
position: absolute;
left: 0;
top: 0;
}
@keyframes line1 {
50%,
100% {
left: 100%;
}
}
.btn > span:nth-child(2) {
width: 2px;
height: 100%;
background: -webkit-linear-gradient(top, transparent, #03e9f4);
right: 0%;
top: -100%;
animation: line2 1s 0.25s infinite;
}
@keyframes line2 {
50%,
100% {
top: 100%;
}
}
.btn > span:nth-child(3) {
width: 100%;
height: 2px;
background: -webkit-linear-gradient(right, transparent, #03e9f4);
right: -100%;
bottom: 0;
animation: line3 1s 0.5s infinite;
}
@keyframes line3 {
50%,
100% {
right: 100%;
}
}
.btn > span:nth-child(4) {
width: 2px;
height: 100%;
background: -webkit-linear-gradient(bottom, transparent, #03e9f4);
left: 0%;
bottom: -100%;
animation: line4 1s 0.75s infinite;
}
@keyframes line4 {
50%,
100% {
bottom: 100%;
}
}
奖品结果背景特效CSS样式(main.css)
.main {
width: 8px;
height: 8px;
position: absolute;
left: 50%;
top: 61%;
transform: translate(-50%, -50%);
perspective: 800px;
display: none;
}
.main i {
width: 8px;
height: 8px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.5);
box-shadow: 0 0 10px 0 white;
position: absolute;
animation: run 3s ease-in-out infinite;
}
.main i:nth-child(1) {
transform: rotate(0deg) translateX(80px);
}
.main i:nth-child(2) {
transform: rotate(12deg) translateX(80px);
}
.main i:nth-child(3) {
transform: rotate(24deg) translateX(80px);
}
@keyframes run {
0% {
opacity: 0;
}
10% {
opacity: 1;
}
100% {
opacity: 1;
transform: translate3d(0, 0, 560px);
}
}
然后就可以看到,我们的静态页面已经写好了,接下来可以开始写JS逻辑代码了
首页的JS代码(index.js)
$(function () {
// 全局计时器
var drawTimer = null;
// 全局步数
var drawStep = -1;
// 全局缓动计时
var easeTime = 2;
// 全局随机奖品(最后停止的位置)
var stopPosition = 1;
// 点击按钮的次数
var clickNum = 0;
// 当我点击按钮时,开始抽奖
$(".button").click(function () {
// 点击抽奖之后,禁用抽奖功能
$(".coverLottery").css("display", "block");
// console.log(clickNum);
// 声明一个 1~8 的数组,写重复的数来提高此数的概率值
var arr = [1, 2, 3, 4, 5, 6, 7, 8];
// 0 ~ arr.length-1 的随机数
var randomNum = Math.floor(Math.random() * arr.length);
stopPosition = arr[randomNum];
// console.log(stopPosition);
// easeTime * 100 毫秒后开始抽奖
drawTimer = setTimeout(drawRun, easeTime * 100);
// 抽三次奖,停止抽奖
if (clickNum >= 3) {
// 清除计时器,停止抽奖
clearTimeout(drawTimer);
// 显示那个中奖页面
$(".lotteryResult").css("display", "block");
// 让奖品不透明
$(".lotteryResult").children().css("opacity", "1");
// 显示背景特效
$(".main").css("display", "block");
}
clickNum++;
});
// 抽奖游戏滚动函数
function drawRun() {
// 抽奖游戏滚动
if (drawStep >= 40 + stopPosition) {
// prize为奖品序号(序号从0开始)
var prize = drawStep % 8;
$(".jpg_" + prize).css("opacity", "1");
// 重置drawStep,为下一次的抽奖做准备
// 让下一次抽奖的位置从上一次抽奖结束的位置开始走
drawStep = stopPosition;
// 以防万一,写上 easeTime = 2
easeTime = 2;
// 提示消息
$(".prize_" + prize).each(function (index, value) {
var prizeName = value.getAttribute("name");
msg(prizeName);
});
var img = $(".prize_" + prize)
.children()
.clone();
$(".lotteryResult").append(img);
// 清除计时器
clearTimeout(drawTimer);
// 返回结果
$(".button").trigger("click");
return;
}
if (drawStep >= 36 + stopPosition) {
// 最后四个的时候开始缓动
easeTime += 1;
} else {
if (easeTime <= 2) {
easeTime = 2;
}
easeTime--;
}
drawStep++;
// 使每个方格在变成不透明之后再变成半透明
$(".jpg_").each(function (index, value) {
// console.log(value);
$(this).css("opacity", "0.5");
});
// 每次抽奖时的不透明效果
$(".jpg_" + (drawStep % 8)).css("opacity", "1");
// 延迟递归
// 每 easeTime * 70 的时间走一格
drawTimer = setTimeout(drawRun, easeTime * 70);
}
// 消息提示函数
function msg(prizeName) {
var say = "恭喜您抽中了奖品" + prizeName;
var $pElement = $(`${
say}`
);
$(".msgbox").append($pElement);
}
});
登录页的JS代码(login.js)
首先需要写一个json文件来存储用户信息
{
"username": "zhangsan",
"phone": "17692414892",
"password": "123456",
"msg": "登录成功"
}
$("#login").bind("submit", function (event) {
// 阻止默认行为
event.preventDefault();
// 表单序列化 - 根据表单默认同步提交获取数据的方式
var data = $("#login").serialize();
console.log(data);
// 通过异步交互提交表单
// 因为我在 axios_default.js 中写了baseURL,所以可以直接写/data/login.json
axios.get("/data/login.json").then(function (response) {
console.log(response);
// 获取到json文件中的phone和password的值
var phoneJson = response.phone;
var passwordJson = response.password;
// console.log(phoneJson, passwordJson);
// 获取到用户输入的phone和password的值
var phoneInput = $("#phone").val();
var passwordInput = $("#password").val();
// console.log(phoneInput, passwordInput);
// 如果用户输入的 phone和password都一样,那么跳转到抽奖页面(页面不刷新)
if (phoneInput === phoneJson && passwordInput === passwordJson) {
// 登录页面隐藏
$(".loginBox").css("display", "none");
// 抽奖页面显示
$("table").css("display", "block");
} else {
// 提示错误信息,可以改成页面
alert("手机号或密码错误");
}
});
});
中奖结果背景特效JS代码
var main = document.getElementsByClassName("main")[0];
for (var x = 0; x < 60; x++) {
var i = document.createElement("i");
main.appendChild(i);
}
var is = document.getElementsByTagName("i");
for (var i = 0; i < is.length; i++) {
is[i].style.transform = `rotate(${
i * 12}deg) translateX(80px)`;
is[i].style.animationDelay = `${
i * 0.05}s`;
}
二、写后台代码
首先先创建一个服务器
npm i axios
npm i express
npm i ejs
router.js - 把所有的路由都放在这个文件里面了
// 引入express第三方模块,用来写服务器
var express = require("express");
// router路由对象
var router = express.Router();
// 写路由
router.get("/", function (req, res) {
res.render("index");
});
router.get("/login", function (req, res) {
res.send("666");
});
module.exports = router;
app.js代码
// 引入express第三方模块,用来写服务器
var express = require("express");
// 引入path内置模块,用来获取文件路径
var path = require("path");
// 引入router.js文件(自己写的路由文件)
var router = require("./router");
// 创建一个服务器
var app = express();
// 模板引擎设置(ejs模板引擎)
app.set("view engine", "html");
app.set("views", `${
__dirname}/views`);
app.engine("html", require("ejs").renderFile); // 用ejs模板渲染html
// console.log("__dirname: ", __dirname);
// 把文件暴露出去,使每个文件都能访问到暴露出去的文件
// 使用"/public"作为前缀来加载public文件夹下的文件
app.use("/public", express.static(path.join(__dirname, "./public/")));
// 使用"/node_modules"作为前缀来加载node_modules文件夹下的文件
app.use(
"/node_modules",
express.static(path.join(__dirname, "./node_modules/"))
);
// 使用"/data"作为前缀来加载data文件夹下的文件
app.use("/data", express.static(path.join(__dirname, "./data/")));
// 把路由挂载到 app 中
//router路由对象中的路由都会匹配到
app.use(router);
// 监听3000端口
app.listen(3000, function () {
console.log("服务器开启成功了 -- 3000");
});
我写了一个配置默认的设置的文件,可以在 axios.defaults.xxx
中进行配置
axios_default.js文件
// 默认配置
if (typeof axios !== "undefined") {
axios.defaults.baseURL = "http://127.0.0.1:3000";
axios.defaults.withCredentials = true; //=> 允许跨域请求
axios.defaults.transformRequest = (data) => {
let str = ``;
if (data && typeof data === "object") {
for (let attr in data) {
if (data.hasOwnProperty(attr)) {
str += `${
attr}=${
data[attr]}&`;
}
}
}
return str.substring(0, str.length - 1);
};
axios.defaults.headers["Contet-Type"] = "x-www-form-urlencoded";
axios.interceptors.response.use((result) => result.data);
}
在浏览器输入 127.0.0.1:3000
,可以发现,我们已经可以成功启动服务了,并且能够访问到我写的网页了