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

大概就是这个样子了,代码都写了注释。大家有什么建议或发现什么问题请留言,帮忙优化优化,谢谢~

Angularjs 实现移动端在线测评效果的更多相关文章

  1. 简单java在线测评程序

    简单java程序在线测评程序 一.前言 大家过年好!今年的第一篇博客啊!家里没有网,到处蹭无线!日子过得真纠结!因为毕设的需求,简单写了一个java程序在线测评程序,当然也可以在本地测试. 二.思路 ...

  2. Slides - 在线制作效果精美的幻灯片(PPT)

    Slides 是可以在浏览器中使用的在线幻灯片编辑器.与传统的演示软件,比如 PowerPoint 相比,Slides 不需要下载任何东西.你所有的信息都是安全地存储在我们的服务器上,无论你在哪里.不 ...

  3. 用pdf.js实现在移动端在线预览pdf文件

    用pdf.js实现在移动端在线预览pdf文件1.下载pdf.js    官网地址:https://mozilla.github.io/pdf.js/ 2.配置    下载下来的文件包,就是一个demo ...

  4. 创建您的 ActiveReports Web端在线报表设计器

    概述 ActiveReports Web端在线报表设计器已经正式上线!看到它这么帅气.实用,你是不是也想自己动手创建一个? 现在我们就来教您,如何创建一个简单的 ActiveReports Web端在 ...

  5. 如何弹出QQ临时对话框实现不添加好友在线交谈效果

    如何不添加好友弹出QQ临时对话框实现在线交谈效果,这样的一个需求,我们真的是太需要了,实现起来也很简单,一行代码即可搞定,需要的朋友可以参考下 其实这个很简单,在img我们加入一个a标签,然后 < ...

  6. CSS3 Generator提供了13个CSS3较为常用的属性代码生成工具,而且可以通过这款工具除了在线生成效果代码之外,还可以实时看到你修改的效果,以及浏览器的兼容性。

    CSS3 Generator提供了13个CSS3较为常用的属性代码生成工具,而且可以通过这款工具除了在线生成效果代码之外,还可以实时看到你修改的效果,以及浏览器的兼容性. CSS3 Generator ...

  7. Westciv Tools主要为CSS3提供了渐变gradients、盒子阴影box-shadow、变形transform和文字描边四种在线生成效果的工具

    Westciv Tools主要为CSS3提供了渐变gradients.盒子阴影box-shadow.变形transform和文字描边四种在线生成效果的工具 1.Westciv Tools 彩蛋爆料直击 ...

  8. 软工博客之关于CSDN的移动端软件测评

    关于CSDN的移动端软件测评 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 软件测评作业 我在这个课程的目标 不求变强,只求做好,成为一颗有用的 ...

  9. Web端在线实时聊天,基于WebSocket(前后端分离)

    这是一个简易的Demo,已经实现了基础的功能 之前一直想实现一个实时聊天的系统,一直没有去实践他.有一天吃饭的时候扫码点菜,几个人点菜能够实时更新,当时就在想,这应该是同一种技术. 刚好前段时间项目上 ...

随机推荐

  1. MATLAB中的分类器

    MATLAB中的分类器   目前了解到的MATLAB中分类器有:K近邻分类器,随机森林分类器,朴素贝叶斯,集成学习方法,鉴别分析分类器,支持向量机.现将其主要函数使用方法总结如下,更多细节需参考MAT ...

  2. dev简单实现柱状图,曲线图

    1.数据源代码: DataTable dt = new DataTable(); dt.Columns.Add("A"); dt.Columns.Add("B" ...

  3. Unity 碰撞器和触发器的理解

    要产生碰撞必须为游戏对象添加刚体(Rigidbody)和碰撞器,刚体可以让物体在物理影响下运动.碰撞体是物理组件的一类,它要与刚体一起添加到游戏对象上才能触发碰撞.如果两个刚体相互撞在一起,除非两个对 ...

  4. asp.net中listview下嵌套gridview

    最近在上软件工程实践课程,想做一个类似于QQ空间或者朋友圈一样的效果.即显示所有好友发送的动态以及动态下回复的信息. 自己YY了一种方法,一开始以为不能达到效果,研究了2个小时终于实现了,感觉效果还是 ...

  5. 过程 : 概念 : 结构 jobbox jobPost

    概念是employer创建jobPost时,可以publish或unpublish. sort expired后,会通过server tast 去更新成history.所有的publish和unpub ...

  6. C#实体类生成XML与XML Schema文档

    一.实体类生成XML private void CreateXML() { Type[] objType = DBEntityRegst(); foreach (var item in objType ...

  7. 快捷配置Hibernate实现对数据库的配置(构建通道)

    在基于MVC设计模式的JAVA WEB应用中,Hibernate可以作为模型层/数据访问层.它通过配置文件(hibernate.properties或hibernate.cfg.xml)和映射文件(* ...

  8. mfc---手动给toolbar按钮添加消息View中

    手动给toolbar按钮添加消息View中: .h: afx_msg void OnButtonBG(); .cpp: ON_COMMAND(ID_BUTTON_BG,OnButtonBG) .cpp ...

  9. 深入理解 JavaScript 异步系列(1)—— 什么是异步

    前言 2014年秋季写完了<深入理解javascript原型和闭包系列>,已经帮助过很多人走出了 js 原型.作用域.闭包的困惑,至今仍能经常受到好评的留言. 很早之前我就总结了JS三座大 ...

  10. DOM基础(四)

    每次写DOM的时候,就觉得好像没什么好写,因为涉及到知识点的方面的确不多,对于DOM来说,更多的还是练习为主.在练习的时候,最好能结合着js基础语法的知识点来学习.这样,在学习DOM的时候就不会那么枯 ...