首页与我联系

「手写原生项目专题」一个带计时进度的在线答题应用(五)

By 前端达人
Published in 5-案例分享
October 07, 2022
1 min read
「手写原生项目专题」一个带计时进度的在线答题应用(五)

大家好,今天给大家分享一个在线答题的应用,这样的答题应用想必大家都玩过,比如常见电商平台答题领优惠券运营活动,就会用到类似的应用,如下图所示: qddemo.gif从上图示意,我们来分析下类似项目的需求,需求分析清楚,才能动手:

  • 点击 start 按钮进入答题界面
  • 答题界面包含以下内容
    • 问题的标题
    • 问题的选项
    • 计时器(每道题10秒钟,如果未作答自动跳至下一题)
    • 最底部的方块,展示题目的数量、题目是否正确、当前题目的位置
    • 答题结束后,展示正确率和正确的题目数量

一、开始创建 HTML 和 CSS 项目结构

1.1 HTML文件

聊完需求,我们先从最基础的 HTML 结构开始做起,定义页面容器 container、题目容器 quiz、答题结果容器 score-container。

<div class="container">
<!-- 开始答题按钮 -->
      <div class="start">
        <h1>Start</h1>
      </div>
 <!-- 题目容器 -->
      <div class="quiz">
        <div class="question"></div>
        <!-- 题目选项 -->
        <div class="choices">
          <div class="choice" id="A"></div>
          <div class="choice" id="B"></div>
          <div class="choice" id="C"></div>
          <div class="choice" id="D"></div>
        </div>

        <!-- 计时器容器 -->
        <div class="timer">
          <div class="counter"></div>
          <div class="time-gauge-bg"></div>
          <div class="time-gauge"></div>
        </div>

        <!-- 答题进度和正确与否提示 -->
        <div class="progress-container"></div>
      </div>

  <!-- 答题进度和正确与否提示 -->
      <!-- Score Container -->
      <div class="score-container"></div>
    </div>

1.2、CSS 文件

接下来定义相关的样式,由于非本文章重点的介绍内容,这里就不过多介绍

* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}

body {
  font-family: Arial, Helvetica, sans-serif;
  background-repeat: no-repeat;
  background-size: cover;
  background-position: center;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1.6;
}

.container {
  height: 700px;
  width: 1400px;
  position: relative;
}

.start h1 {
  font-weight: 100;
  font-size: 40px;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  cursor: pointer;
  background-color: #41444b;
  padding: 20px 50px;
  color: white;
}

.quiz {
  height: 100%;
  width: 100%;
  position: absolute;
  top: 0;
  left: 0;

  display: grid;
  grid-template-rows: repeat(4, 1fr);
  grid-row-gap: 20px;

  visibility: hidden;
}

.quiz > * {
  display: flex;
  align-items: center;
  justify-content: center;
}

.question {
  font-size: 35px;
  letter-spacing: 2px;
  background-color: rgba(247, 247, 247, 0.7);
  place-self: center;
  padding: 15px 30px;
  text-align: center;
}

.choices {
  width: 100%;
  justify-content: space-evenly;
  align-items: flex-end;
}

.choice {
  cursor: pointer;
  background-color: rgba(27, 118, 141, 0.7);
  padding: 7px 15px;
  text-align: center;
  color: white;
  font-size: 20px;
  height: 100px;
  width: 250px;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* 计时器容器 */
.timer {
  align-self: flex-end;
  flex-direction: column;
}

.counter {
  font-size: 40px;
  color: rgb(27, 118, 141);
}

.time-gauge-bg,
.time-gauge {
  position: absolute;
  top: 78%;
  left: 22%;
}

.time-gauge-bg {
  height: 10px;
  width: 800px;
  background-color: lightgrey;
}

.time-gauge {
  height: 10px;
  background-color: rgb(179, 6, 247);
}

/* 答题进度容器 */
.progress-box {
  height: 30px;
  width: 30px;
  background-color: lightgrey;
  margin-left: 15px;
}

/* 答题结果容器 */
.score-container {
  position: absolute;
  top: 0;
  left: 0;
  background-color: rgba(0, 0, 0, 0.85);
  height: 100%;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;

  padding: 50px 100px;

  visibility: hidden;
}

.score-container h2 {
  font-size: 30px;
  font-weight: 100;
  text-align: center;
  color: white;
}

二、业务核心 APP.JS 文件

接下来,我们基于需求编写业务的核心代码文件 app.js 。

2.1、准备基础数据和变量

编写核心函数之前,我们先定义基础的变量和题目数据源。

首先定义 DOM 相关的变量,方便我们进行相关的 DOM 操作

const start = document.querySelector(".start");
const quiz = document.querySelector(".quiz");
const question = document.querySelector(".question");
const allAnswerChoices = document.querySelectorAll(".choice");
const answerChoiceA = document.querySelector("#A");
const answerChoiceB = document.querySelector("#B");
const answerChoiceC = document.querySelector("#C");
const answerChoiceD = document.querySelector("#D");
const counter = document.querySelector(".counter");
const timeGauge = document.querySelector(".time-gauge");
const progressContainer = document.querySelector(".progress-container");
const ScoreContainer = document.querySelector(".score-container");

定义问题数据源变量 questions,结构如下所示:

let questions = [
    {
        question: "How many different sounds can a cat make?",
        questionImg: "img/1.jpg",
        choiceA: "100",
        choiceB: "150",
        choiceC: "10",
        choiceD: "27",
        correctAnswer: "100",
    },
   //...此处省略多条数据
    {
        question:
            "If a wolf trespasses on the territory of other wolves, what will happen?",
        questionImg: "img/10.jpg",
        choiceA: "Nothing",
        choiceB: "It will be accepted into the pack",
        choiceC: "It will be chased or killed",
        choiceD: "It will be required to present prey to the pack",
        correctAnswer: "It will be chased or killed",
    },
];

最后定义一些基础常量,比如问题的数量、当前问题的序号、定义每道题的作答时长等,相关变量如下:

const lastQuestion = questions.length - 1;
let activeQuestion = 0;
const questionTime = 10; // 10 秒作答时间
const gaugeWidth = 800;// 800 px
const gaugeUnit = gaugeWidth / questionTime; //每秒进度 80px
let count = 0;
let Timer; //定时器
let score = 0; //分数(答对的题目数量)

2.2、定义监听事件

接下来我们为 start 按钮添加 startQuiz 开始答题监听事件,以及为每个答案选项添加 checkAnswer 事件,确认用户选的答案是否正确。

//添加开始答题监听事件
start.addEventListener("click", startQuiz);

allAnswerChoices.forEach(function (clickAnswer) {
    clickAnswer.addEventListener("click", function (e) {
        let userAnswer = e.target.innerText;
        checkAnswer(userAnswer);
    })
})

接下我们来定义 startQuiz() 开始答题函数:start 开始按钮隐藏,显示问题容器,加载当前的问题 renderQuestion()、以及显示当前题目数量的小方格 renderProgress()、答题时进行的计时函数renderCounter(),及计时器 Timer,定时刷新计时函数。

//开始答题函数
function startQuiz() {
    start.style.display = "none";
    renderQuestion();
    quiz.style.visibility = "visible";
    renderProgress();
    renderCounter();
    Timer = setInterval(renderCounter,1000);
}

//渲染问题
function renderQuestion() {
    let q = questions[activeQuestion];
    question.innerHTML = "<p>" + q.question + "</p>";
    answerChoiceA.innerHTML = q.choiceA;
    answerChoiceB.innerHTML = q.choiceB;
    answerChoiceC.innerHTML = q.choiceC;
    answerChoiceD.innerHTML = q.choiceD;
    let bodyImg = `url('${q.questionImg}')`;
    document.body.style.background = bodyImg;
}

//计时器进度
function renderCounter() {
    if (count <= questionTime) {
        counter.innerHTML = count;
        timeGauge.style.width = count * gaugeUnit + "px";
        count++
    } else {
        answerIsIncorrect();
        nextQuestion();
    }
}

//加载目前答题数量
function renderProgress() {
    for (let questionIndex = 0; questionIndex <= lastQuestion; questionIndex++) {
        progressContainer.innerHTML +=
            "<div class='progress-box' id=" + questionIndex + "></div>";
    }
}

最后我们来定义判断答案是否正确的函数 checkAnswer ,以及对应当前方格的颜色(正确显示绿色 answerIsCorrect(),错误显示红色 answerIsIncorrect()),加载下一题的函数 nextQuestion()和汇总所有题目的结果renderScore():正确率和正确的题目数量。

//判断答案是否正确
function checkAnswer(answer) {
    if (answer === questions[activeQuestion].correctAnswer) {
        score++
        answerIsCorrect();
    } else {
        answerIsIncorrect();
    }
    nextQuestion();
}

//答案正确,对应进度背景方格显示绿色
function answerIsCorrect() {
    document.getElementById(activeQuestion).style.background = "green";
}

//答案错误,对应进度背景方格显示红色
function answerIsIncorrect() {
    document.getElementById(activeQuestion).style.background = "red";
}

//显示下一个题目
function nextQuestion(){
    count = 0;
    if(activeQuestion<lastQuestion){
        activeQuestion++
        renderQuestion();
    } else{
        clearInterval(Timer);
        renderScore();
    }

}

//加载目前答题的数量
function renderProgress() {
    for (let questionIndex = 0; questionIndex <= lastQuestion; questionIndex++) {
        progressContainer.innerHTML +=
            "<div class='progress-box' id=" + questionIndex + "></div>";
    }
}

//显示分数
function renderScore(){
    ScoreContainer.style.visibility="visible";
    let scorePercentage= Math.round((100*score)/questions.length);
    ScoreContainer.innerHTML+=`<h2>当前正确率:${scorePercentage}</h2>`;
    ScoreContainer.innerHTML+=`<h2>答对的题目数量:${score}</h2>`;
}

结束

到这里本案例就介绍完了,是不是很容易理解呢,小编建议大家亲自动手实践下,大家可以点击链接进行体验,https://darenqademo.netlify.app/,点击源码地址获取源码链接: https://pan.baidu.com/s/17c1GFVxnWp15dl1YjbRPfw?pwd=ifs5 提取码: ifs5

前端达人公众号.jpg

注:本文属于原创文章,版权属于「前端达人」公众号及 qianduandaren.com 所有,未经授权,谢绝一切形式的转载


Tags

javascriptproject
Previous Article
「手写原生代码专题」一个简单的列表拖拽示例(四)
前端达人

前端达人

专注前端知识分享

Table Of Contents

1
一、开始创建 HTML 和 CSS 项目结构
2
二、业务核心 APP.JS 文件
3
结束

相关文章

「手写原生代码专题」一个简单的列表拖拽示例(四)
October 04, 2022
1 min

前端站点

VUE官网React官网TypeScript官网

公众号:前端达人

前端达人公众号