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