Angularjs 实现移动端在线测评效果(推荐)
程序员文章站
2022-09-02 16:48:08
注:此文所用的angular版本为 1.6
一、运行效果图
二、需求
1. 点击选项时,背景变为黄色(即选中状态),并且自动切换到下一题
2. 切换到下一题时,...
注:此文所用的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);