京东抽奖案例

京东抽奖案例

0.设计思路

项目呢是模仿的王者荣耀抽奖界面的一部分。。。

1.目录结构

|-- app.js
|-- router.js          路由接口文件
|-- controllers        控制器
|-- models
|-- node_modules       第三方包
|-- package.json       包描述文件
|-- package-lock.json  第三方包版本锁定文件(npm 5 以后才有)
|-- public             公共的静态资源
|-- README.md          项目说明文档
|-- routes
|-- views              存储视图目录

2.项目需求

本项目包含用户注册、登录以及抽奖等功能:

  1. 用户注册功能

    a. 提供手机号验证码注册功能: 用户输入手机号,对应手机号接收验证码

    b.输入完成手机号和验证码之后进入下一步,允许用户输入密码以及确认密码:密码要求12~16位的数字、大小写字母以及符号

    c. 注册成功后显示用户抽奖界面(: 页面不刷新)

  2. 用户登录功能

    a. 提供手机号和密码、手机号和验证码两种方式登录

    b. 判断如果用户没有注册的话,直接跳转用户注册界面(: 页面不刷新)

    c. 登录成功后显示用户抽奖界面(: 页面不刷新)

  3. 用户抽奖功能

    a. 每个用户—次拥有三次抽奖机会

    b. 抽奖方式采用转盘方式抽奖:不同奖项的得奖率不同(转盘抽奖可参考百度)

    c. 三次抽奖完成后在界面显示三次抽奖汇总结果

3.路由设计

由于要求是页面不跳转,所以应该是只写一个路由即可

路径 方法 get参数 post参数 是否需要登录 备注
/ GET 渲染首页

4.项目构建

  • 一、先把静态页面写好

    • HTML完整代码(views/index.html)

      <!DOCTYPE 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>
          <!-- 注册 -->
          <!-- <form action="#" class="register">
            <input type="text" name="username" id="username" />
            <input type="text" name="password" id="password" />
            <input type="submit" />
          </form> -->
      
          <!-- 登录 -->
          <div class="loginBox">
            <h2>Login</h2>
            <form action="#" method="" id="login">
              <div class="item">
                <input type="text" name="phone" id="phone" required />
                <label for="">Phone</label>
              </div>
              <div class="item">
                <input type="password" name="password" id="password" required />
                <label for="">PassWord</label>
              </div>
              <button class="btn">
                submit
                <span></span>
                <span></span>
                <span></span>
                <span></span>
                <input type="submit" value="" id="submit" />
              </button>
            </form>
          </div>
      
          <!-- 
              共有一圈奖品,是8个
              中间是一个抽奖键
              点击抽奖之后抽奖按键消失,随后弹出3项奖品内容,这是一个蒙层
              有再次抽奖按钮,和确定按钮
                  点击再次抽奖按钮可以再次抽奖,点击确定按钮完成抽奖
            -->
      
          <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 -->
          <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 = $(`<p>${
                
                say}<p>`);
          $(".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 ,可以发现,我们已经可以成功启动服务了,并且能够访问到我写的网页了

猜你喜欢

转载自blog.csdn.net/Cool_breeze_/article/details/108505874