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


Posted in Javascript onApril 05, 2017

注:此文所用的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);

Javascript 相关文章推荐
用Juery网页选项卡实现代码
Jun 13 Javascript
jQuery EasyUI API 中文文档 - ComboTree组合树
Oct 11 Javascript
js字符串转换成xml对象并使用技巧解读
Apr 18 Javascript
javascript怎么禁用浏览器后退按钮
Mar 27 Javascript
Javascript中的回调函数和匿名函数的回调示例介绍
May 12 Javascript
用JS动态改变表单form里的action值属性的两种方法
May 25 Javascript
JavaScript实现旋转轮播图
Aug 18 Javascript
基于js原生和ajax的get和post方法以及jsonp的原生写法实例
Oct 16 Javascript
JS使用贪心算法解决找零问题示例
Nov 27 Javascript
解决vue+webpack打包路径的问题
Mar 06 Javascript
JS async 函数的含义和用法实例总结
Apr 08 Javascript
微信小程序实现打卡签到页面
Sep 21 Javascript
微信小程序中的onLoad详解及简单实例
Apr 05 #Javascript
微信小程序 页面跳转如何实现传值
Apr 05 #Javascript
微信小程序 数据遍历的实现
Apr 05 #Javascript
微信小程序 图片绝对定位(背景图片)
Apr 05 #Javascript
JS实现复选框的全选和批量删除功能
Apr 05 #Javascript
Ajax验证用户名或昵称是否已被注册
Apr 05 #Javascript
JS二叉树的简单实现方法示例
Apr 05 #Javascript
You might like
自动跳转中英文页面
2006/10/09 PHP
php基于PDO连接MSSQL示例DEMO
2016/07/13 PHP
Linux系统中为php添加pcntl扩展
2016/08/28 PHP
PHP基于timestamp和nonce实现的防止重放攻击方案分析
2019/07/26 PHP
JS 时间显示效果代码
2009/08/23 Javascript
JavaScript 面向对象编程(2) 定义类
2010/05/18 Javascript
Js setInterval与setTimeout(定时执行与循环执行)的代码(可以传入参数)
2010/06/11 Javascript
使用PHP+JQuery+Ajax分页的实现
2013/04/23 Javascript
轻松创建nodejs服务器(2):nodejs服务器的构成分析
2014/12/18 NodeJs
JavaScript编写页面半透明遮罩效果的简单示例
2016/05/09 Javascript
js实现文本上下来回滚动
2017/02/03 Javascript
jQuery中的deferred使用方法
2017/03/27 jQuery
深入浅析var,let,const的异同点
2018/08/07 Javascript
原生JS实现DOM加载完成马上执行JS代码的方法
2018/09/07 Javascript
react项目实践之webpack-dev-serve
2018/09/14 Javascript
深入理解Angularjs 脏值检测
2018/10/12 Javascript
CentOS7中源码编译安装NodeJS的完整步骤
2018/10/13 NodeJs
浅谈Vue.js 中的 v-on 事件指令的使用
2018/11/25 Javascript
Python计时相关操作详解【time,datetime】
2017/05/26 Python
python使用turtle库绘制树
2018/06/25 Python
解决python中os.listdir()函数读取文件夹下文件的乱序和排序问题
2018/10/17 Python
python操作小程序云数据库实现简单的增删改查功能
2019/06/06 Python
Python 变量的创建过程详解
2019/09/02 Python
Vs Code中8个好用的python 扩展插件
2020/10/12 Python
TOWER London官网:鞋子、靴子、运动鞋等
2019/07/14 全球购物
英国时尚首饰品牌:Missoma
2020/06/29 全球购物
销售人员自我评价怎么写
2013/09/19 职场文书
市场部岗位职责
2015/02/12 职场文书
会计试用期自我评价
2015/03/10 职场文书
企业安全隐患排查治理制度
2015/08/05 职场文书
女性励志书籍推荐
2019/08/19 职场文书
Python中使用Lambda函数的5种用法
2021/04/01 Python
mysql知识点整理
2021/04/05 MySQL
Nginx配置根据url参数重定向
2022/04/11 Servers
Python Matplotlib绘制动画的代码详解
2022/05/30 Python
如何让你的Nginx支持分布式追踪详解
2022/07/07 Servers