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

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

一、运行效果图

二、需求

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);
(0)

相关推荐

  • JS实现简单的选择题测评系统代码思路详解(demo)

    包含内容:JS封装表单,JS校验表单 说是测评系统,感觉只能算是一个小小的Demo,很水,,没有数据库库,,仅使用JS做简单的选择题测评系统 -------------------------------------------------------------------------------- 一.设计思路 表单封装: [1]由于采用JS封装提交所以,不需要form标签 [2]放置多个input标签,作为输入项 [3]编写JS获取输入项,并通过get方式提交到另一个页面 校验表单(显示结

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

    注:此文所用的angular版本为 1.6 一.运行效果图 二.需求 1. 点击选项时,背景变为黄色(即选中状态),并且自动切换到下一题 2. 切换到下一题时,顶部进度随之改变 3. 选中时要把对应的分值记录下来(因为要根据分值算出最后的测评结果) 4. 通过向右滑动可以查看前面做过的题目 5. 当前题目没选,无法切换到下一题 6. 当选中最后一道题目时,切换到测评结果页 三.具体实现 题目json数据,总共10道题,这里为了节省篇幅,就只贴出3道了. (Score是分数, OrderNo是答案

  • Vue实现移动端页面切换效果【推荐】

    在子页面把整个页面做绝对定位,覆盖整个屏幕,子父页面将 router-view 用  transition 套起来,并加上过渡动画就可以啦. 代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, init

  • AngularJS递归指令实现Tree View效果示例

    本文实例讲述了AngularJS递归指令实现Tree View效果的方法.分享给大家供大家参考,具体如下: 在层次数据结构展示中,树是一种极其常见的展现方式.比如系统中目录结构.企业组织结构.电子商务产品分类都是常见的树形结构数据. 这里我们采用Angular的方式来实现这类常见的tree view结构. 首先我们定义数据结构,采用以children属性来挂接子节点方式来展现树层次结构,示例如下: [ { "id":"1", "pid":&quo

  • Android实现TV端大图浏览效果的全过程

    目录 前言 1.BitmapRegionDecoder: 2.SubsamplingScaleImageView 3.Glide+SubsamplingScaleImageView混合加载渲染 4.自定义LongImageView,结合BitmapRegionDecoder使用 5.在MainActivity中的使用: 6.布局文件代码: 7.实现的效果如下: 总结 前言 最近TV开发需要加载的图片很长,大小也很大,并且还不允许压缩.比如显示:世界地图.清明上河图.微博长图.海报.活动照片等.

  • 原生javascript移动端滑动banner效果

    本文实例为大家分享了移动端滑动banner效果的具体代码,供大家参考,具体内容如下 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no,

  • vue实现ajax滚动下拉加载,同时具有loading效果(推荐)

    代码如下所示: <!doctype html> <html> <head> <meta charset="utf-8"> <title>vue测试ajax的使用</title> <meta id="viewport" name="viewport" content="width=device-width, initial-scale=1.0, minimum-

  • vue 和vue-touch 实现移动端左右导航效果(仿京东移动站导航)

    先给大家展示下效果图,感觉还不错请参考实现代码: 使用技术:vue2.0 webpack vue-touch 一些简单的javascript; (注意:vue-touch 使用的是2.0.0版本 需要与vue2.0.0兼容) vue-touch(地址:https://github.com/vuejs/vue-touch 注意是next 分支) 左侧导航可滑动(右侧视图窗因为和左逻辑一样 就没写) var VueTouch = require('vue-touch') Lib.Vue.use(Vu

  • js中常用的Tab切换效果(推荐)

    如下所示: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>tab</title> <style> *{margin:0; padding:0; list-style:none;} .box{ width: 1000px; overflow: hidden; margin:100px auto

  • 基于JS如何实现类似QQ好友头像hover时显示资料卡的效果(推荐)

    一.应用场景 鼠标hover弹出div,并且鼠标离开后不能马上隐藏,因为这个div上还有功能入口.比如: 鼠标经过好友列表中的好友头像时显示资料卡的效果如下所示: hover时显示二维码 二.实现 用如下这样一个简单的效果:鼠标hover到A上显示B来模拟 有2种实现方式,推荐第二种,第一种有弊端下面会说. 1.方法一 原理:把触发元素A和要显示元素B放于同一个父级元素内,鼠标经过父级元素时触发显示B.这样鼠标移动到B时仍然 处于该父级元素内,则div不会隐藏. 代码: <!DOCTYPE ht

  • Jquery+Ajax+xml实现中国地区选择三级联动菜单效果(推荐)

    本文主要介绍使用 Jquery+Ajax+xml,首先需要一个包含我国所有地图信息的xml文档. 此处选用的xml文档(共1000多行)主要结构如下: <?xml version="1.0" encoding="utf-8"?> <area Country="China"> <province ID="1" provinceID="110000" province="

随机推荐