欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Angularjs 实现移动端在线测评效果(推荐)

程序员文章站 2022-04-29 09:00:03
注:此文所用的angular版本为 1.6 一、运行效果图 二、需求 1. 点击选项时,背景变为黄色(即选中状态),并且自动切换到下一题 2. 切换到下一题时,...

注:此文所用的angular版本为 1.6

一、运行效果图

Angularjs 实现移动端在线测评效果(推荐)

二、需求

1. 点击选项时,背景变为黄色(即选中状态),并且自动切换到下一题

2. 切换到下一题时,顶部进度随之改变

3. 选中时要把对应的分值记录下来(因为要根据分值算出最后的测评结果)

4. 通过向右滑动可以查看前面做过的题目

5. 当前题目没选,无法切换到下一题

6. 当选中最后一道题目时,切换到测评结果页

三、具体实现

 题目json数据,总共10道题,这里为了节省篇幅,就只贴出3道了。 (score是分数, orderno是答案序号)

{ "questions":
 [
 { 
  "question":"您的年龄范围:",
  "answerlist":[
   {"text":"30岁以下","score":5,"orderno":0},
   {"text":"30-39岁","score":4,"orderno":1},
   {"text":"40-49岁","score":3,"orderno":2},
   {"text":"50-59岁","score":2,"orderno":3},
   {"text":"60岁以上","score":1,"orderno":4}]
 },
 { 
  "question":"您的婚姻状况为:",
  "answerlist":[
  {"text":"未婚","score":5,"orderno":1},
  {"text":"已婚","score":4,"orderno":2},
  {"text":"单身有婚史","score":3,"orderno":3},
  {"text":"丧偶","score":2,"orderno":4},
  {"text":"不详","score":1,"orderno":5}]
 },
 { 
  "question":"您的收入需要用来供养其他人(如父母或子女)吗?",
  "answerlist":[
  {"text":"不需供养其他人","score":5,"orderno":1},
  {"text":"供养1人","score":4,"orderno":2},
  {"text":"供养2人","score":3,"orderno":3},
  {"text":"供养3人","score":2,"orderno":4},
  {"text":"供养4人或以上","score":1,"orderno":5}]
 }
 ]
}

html代码

<div class="wrapper" ng-controller="risktestcontroller as vm">
 <div class="process-box">
 <ul>
  <li class="page-icon"><span class="icon icon-txt">1</span></li>
  <li class="page-icon"><span class="icon icon-txt">2</span></li>
  <li class="page-icon"><span class="icon icon-txt">3</span></li>
  <li class="page-icon"><span class="icon icon-txt">4</span></li>
  <li class="page-icon"><span class="icon icon-txt">5</span></li>
  <li class="page-icon"><span class="icon icon-txt">6</span></li>
  <li class="page-icon"><span class="icon icon-txt">7</span></li>
  <li class="page-icon"><span class="icon icon-txt">8</span></li>
  <li class="page-icon"><span class="icon icon-txt">9</span></li>
  <li class="page-icon"><span class="icon icon-txt">10</span></li>
 </ul>
 <div class="page-info">
  已完成 {{vm.count}}/10
 </div>
 </div>
 <ul class="list-box" id="listbox">
 <li class="list-item" ng-repeat="question in vm.questionlist track by $index" ng-class="{'first-li': $index == 0}">
  <div class="question-box">
  <div class="question">{{$index + 1}}. {{question.question}}</div>
  <ul class="answer">
   <li class="answer-item" 
   ng-repeat="answer in question.answerlist track by $index" 
   ng-click="vm.onclickanswer(answer, $parent.$index)"
   ng-class="{'selected': answer.selected}">
   {{vm.letter[$index]}}. {{answer.text}}
   </li>
  </ul>
  </div>
 </li>
 </ul>
 <div ng-show="vm.showresult">
 <span>{{vm.point}}</span>
 </div>
 </div>

核心css样式代码

.wrapper{
  width: 100%;
  height: 100%;
  position: relative;
  overflow: hidden;
 }
 .process-box{
  width: 17.25rem;
  height: 2.5rem;
  line-height: 2.5rem;
  background-color: #fff;
  margin: 1.5rem auto;
  border-radius: 0.2rem;
 }
 .page-icon{
  float: left;
  font-size: 0.4rem;
  color: #ffe7c9;
  width: 1.32rem;
  text-align: center;
 }
 .page-info{
  font-size: 0.65rem;
  color: #f3a84d;
 }
 .question-box{
  width: 17.25rem;
  background-color: #fff;
  margin-left: 0.75rem;
  border-radius: 0.2rem;
 }
 .question{
  font-size: 0.8rem;
  color: #43689f;
  padding: 1.1rem 0 0.8rem 0.75rem;
 }
 .answer-item{
  font-size: 0.75rem;
  color: #80a1d0;
  border-top: 1px solid #eee;
  padding: 1.1rem 0 1.1rem 1.0rem;
 }
 .icon-txt{
  background-color: orange;
  border-radius: 0.5rem;
  display: block;
  width: 0.8rem;
  height: 0.8rem;
  line-height: 0.8rem;
  margin: 0.95rem auto;
 }
 .icon-txt-active{
  background-color: #ffe7c9;
  border-radius: 0.3rem;
  display: block;
  width: 0.3rem;
  height: 0.3rem;
  line-height: 2.0rem;
  color: #fff;
  margin: 1.25rem auto;
 }
 .list-item {
  width: 100%;
  position: absolute;
  transform: translate3d(100%,0,0);
  transition: transform 0.5s;
 }
 .first-li {
  transform: translate3d(0,0,0);
 }
 .selected {
  background-color: orange;
 }

控制器代码(controller)

(function (agr) {
 //模块 - app
 var app = agr.module('app', []);
 //控制器 - 风险测评
 app.controller('risktestcontroller', ['$scope', '$http', risktestcontroller]);
 function risktestcontroller($scope, $http) {
 var vm = this;
 vm.letter = ['a', 'b', 'c', 'd', 'e']; //答案编号
 vm.questionlist = []; //题目
 vm.point = 0;  //得分
 vm.showresult = false; //是否显示结果页
 //加载数据 
 $http({
  method: 'get',
  url: '/service/risktest',
 }).then(function (resp) {  
  vm.questionlist = resp.data.questions;
 }, function (resp) {
  console.log("error", resp);
 });
 var lis = document.queryselectorall(".list-item"), //题目列表
  count = 0, //做了多少道题
  index = 0, //当前第几题
  big = 9; //最大索引值,因为总共10道题,所以是9(常量)
 //选择答案
 vm.onclickanswer = function (answer, $parentindex) {

  var icons = document.queryselectorall(".icon"),
  curr = $parentindex; //当前题目索引
  next = $parentindex + 1; //下一题索引
  nextquestion = vm.questionlist[next]; //下一道题
  //当前问题的答案列表
  var answerlist = vm.questionlist[$parentindex].answerlist;
  //为每个答案对象添加属性 selected, 默认值为false
  for (var i = 0, len = answerlist.length; i < len; i++) {
  answerlist[i].selected = false;
  }
  //将选中的答案设置为true (从而应用样式.selected 将背景色设置为黄色)
  answer.selected = true;
  //判断是否为最后一道题
  if ($parentindex < big) { //不是最后一题
  //改变顶部进度样式
  icons[curr].classlist.remove("icon-txt");
  icons[curr].classlist.add("icon-txt-active");
  //切换到下一题
  lis[curr].style.webkittransform = 'translate3d(-100%,0,0)';
  nextquestion && (lis[next].style.webkittransform = 'translate3d(0,0,0)');
  } else { //是最后一题
  //改变顶部进度样式
  icons[curr].classlist.remove("icon-txt");
  icons[curr].classlist.add("icon-txt-active");
  //计算分数
  vm.point = calcpoint();
  //显示测评结果
  vm.showresult = true;
  }
  //做了多少题
  count = calccount();

  //因为选中答案会自动切换到下一题,所以索引更新为next
  index = next;  
 }
 //计算分数
 var calcpoint = function () {
  var point = 0;
  for (var i = 0, lenq = vm.questionlist.length; i < lenq; i++) {
  for (var k = 0, lena = vm.questionlist[i].answerlist.length; k < lena; k++) {
   if (vm.questionlist[i].answerlist[k].selected) {
   point += vm.questionlist[i].answerlist[k].score;
   }
  }
  }
  return point;
 }
 //计算当前做了多少道题
 var calccount = function(){
  var count = 0;
  for (var i = 0, lenq = vm.questionlist.length; i < lenq; i++) {
  for (var k = 0, lena = vm.questionlist[i].answerlist.length; k < lena; k++) {
   if (vm.questionlist[i].answerlist[k].selected) {
   count++;
   }
  }
  }
  return count;
 }
 /** 触屏滑动效果处理 == 开始 == **/
 var offsetx = 0, //手指滑动偏移量
  startx,  //滑动开始时的x轴坐标点
  starttime; //手指滑动开始时间
 //触屏开始
 var starthandler = function (evt) {
  //每次触屏时将偏移量重置为0
  offsetx = 0;
  //记录x坐标
  startx = evt.touches[0].pagex;
  //取得时间戳
  starttime = new date() * 1;
 };
 //触屏滑动
 var movehandler = function (evt) {
  //阻止默认事件
  evt.preventdefault();
  //记录手指滑动的偏移量
  offsetx = evt.touches[0].pagex - startx;
  var curr = index,
  prev = index - 1,
  next = index + 1,
  prevquestion = vm.questionlist[prev],
  nextquestion = vm.questionlist[next],
  width = window.innerwidth; 
  //手指滑动时题卡跟着手指滑动(向右滑:[偏移量大于0,即正数,并且不是第一道题])
  if (offsetx > 0 && index > 0) {
  lis[curr].style.webkittransform = 'translate3d(' + offsetx + 'px, 0, 0)';
  prevquestion && (lis[prev].style.webkittransform = 'translate3d(' + (offsetx - width) + 'px, 0, 0)');
  }
  //手指滑动时题卡跟着手指滑动(向左滑:[偏移量小于0,即负数,并且不是最后一题])
  if (offsetx < 0 && index < count) {
  lis[curr].style.webkittransform = 'translate3d(' + offsetx + 'px, 0, 0)';
  nextquestion && (lis[next].style.webkittransform = 'translate3d(' + (offsetx + width) + 'px, 0, 0)');
  }
 };
 //触屏结束
 var endhandler = function (evt) {
  var boundary = window.innerwidth / 5, //当手指滑动的偏移量为屏幕的5分之一时才进行切换
  quickboundary = 60,   //当手指快速滑动时,偏移量为60即可 
  endtime = new date() * 1;   //获取结束时间戳
  //判断是否为快速滑动
  if (endtime - starttime > 1000) {
  //判断是向左滑还是向右滑
  if (offsetx > 0) {
   //判断是否达到切换偏移量
   if (offsetx >= boundary) {
   movetoright();
   } else {
   resetmoveright();
   }
  } else{
   if (offsetx < -boundary) {
   movetoleft();
   } else {
   resetmoveleft();
   }
  }
  } else {
  if (offsetx > 0) {
   if (offsetx >= quickboundary) {
   movetoright();
   } else {
   resetmoveright();
   }
  } else {
   if (offsetx < -quickboundary) {
   movetoleft();
   } else {
   resetmoveleft();
   }
  }
  }
 };
 //向右滑动事件
 var movetoright = function () {
  var curr = index,
  prev = index -1,
  prevquestion = vm.questionlist[prev];
  if (curr > 0) {
  lis[curr].style.webkittransform = 'translate3d(100%, 0, 0)';
  prevquestion && (lis[prev].style.webkittransform = 'translate3d(0, 0, 0)');
  index--;
  }  
 }
 //右滑重置(当滑动距离没达到切换偏移量时,题卡回到原点)
 var resetmoveright = function () {
  var curr = index,
  prev = index -1,
  prevquestion = vm.questionlist[prev];
  lis[curr].style.webkittransform = 'translate3d(0, 0, 0)';
  prevquestion && (lis[prev].style.webkittransform = 'translate3d(-100%, 0, 0)');
 }
 //向左滑动事件
 var movetoleft = function () {
  var curr = index,
  next = index + 1,
  nextquestion = vm.questionlist[next];
  if (curr < count) {
  lis[curr].style.webkittransform = 'translate3d(-100%, 0, 0)';
  nextquestion && (lis[next].style.webkittransform = 'translate3d(0, 0, 0)');
  index++;
  } 
 }
 //左滑重置(当滑动距离没达到切换偏移量时,题卡回到原点)
 var resetmoveleft = function () {
  var curr = index,
  next = index + 1,
  nextquestion = vm.questionlist[next];
  lis[curr].style.webkittransform = 'translate3d(0, 0, 0)';
  nextquestion && (lis[next].style.webkittransform = 'translate3d(100%, 0, 0)');
 }
 //监听滑动事件 
 var outer = document.getelementbyid("listbox");
 outer.addeventlistener('touchstart', starthandler);
 outer.addeventlistener('touchmove', movehandler);
 outer.addeventlistener('touchend', endhandler);
 

 /** 触屏滑动效果处理 == 结束 == **/
 }
})(angular);