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

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

时间: 2024-10-10 08:04:16

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

AngularJS:实现轮播图效果

要实现这个功能,可以https://github.com/sroze/ngInfiniteScroll这个第三方控件实现的.实现步骤如下: 1. 下载ng-infinite-scroll.js程序http://sroze.github.io/ngInfiniteScroll/ 目前版本是1.0.0 2. 如果你是用的jQuery2.0以上版本,还需要修改ng-infinite-scroll.js程序,将所有的将所有的$window.xxx改为$(window).xxx, elem.xxx改为$(

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

如何不添加好友弹出QQ临时对话框实现在线交谈效果,这样的一个需求,我们真的是太需要了,实现起来也很简单,一行代码即可搞定,需要的朋友可以参考下 其实这个很简单,在img我们加入一个a标签,然后 <a href='tencent://message/?uin=QQ号码&Site=网站地址&Menu=yes'></a> 例子: <a target="_blank" href="http://wpa.qq.com/msgrd?v=1&a

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

Slides 是可以在浏览器中使用的在线幻灯片编辑器.与传统的演示软件,比如 PowerPoint 相比,Slides 不需要下载任何东西.你所有的信息都是安全地存储在我们的服务器上,无论你在哪里.不管你说的是在一次会议上演讲或者只是通过电子邮件发送你的信息,你的精心制作传达给大家美丽的故事.您还可以导出 PDF 和听众分享或打印出来. 在线演示 您可能感兴趣的相关文章 网站开发中很有用的 jQuery 效果[附源码] 分享35个让人惊讶的 CSS3 动画效果演示 十分惊艳的8个 HTML5 &

Java小项目之:在线测评考试系统

Java小项目之:在线测评考试系统,让你可以在家考科一!今天带来的java小项目是一套在线测评考试系统,题库是科目一的.不仅可以在家练练java技术,还可以边学习学习科目一,一举两得.界面介绍:开始.分数.考试规则.离开,四个选项.还有很多题库可以选择,可以私信我!代码展示:package service; import java.util.ArrayList; import java.util.List; import java.util.Random; import util.Config;

hammer.js实现移动端的拖放效果

hammer.js可以实现移动端的多种触摸效果.详细可以点击http://www.cnblogs.com/iamlilinfeng/p/4239957.html 不过发现hammer.js实现功能时,只能是原生js,jquery代码在hammer中不起效果. 一.引入hammer.js 下载http://hammerjs.github.io/ <script type="text/javascript" src="js/hammer.min.js">&l

快速生成漂亮的移动端视差滚动效果

在线演示 本地下载 使用stellar.js可以帮助开发者快速的生成基于移动设备的视差滚动效果. 使用简单的data属性定义即可生成相关视差效果.

AngularJS 指令 实现文本水平滚动效果

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta content="text/html;

广州酷狗计算机科技在线测评题 Android岗位

一 选择题 15道 1 集合类是否线程安全Vector 线程安全ArrayList 非线程安全LinkedList 非线程安全Hashtable 线程安全HashMap 非线程安全TreeMap 非线程安全 2 Tween动画alpha 渐变scale 大小translate 移动rotate 旋转 3 Activity各个什么周期 获取焦点 onCreate Activity正在被创建 方法中做一些初始化的工作 加载界面布局 初始化数据等 onRestart Activity重新启动 onSt

angularJS+Ionic移动端图片上传的解决办法

前端开发中经常会碰到图片上传的问题,网上的解决办法很多,可是有些图片上传的插件会有一些附属的插件,因此因为一个图片上传的问题可能额需要引入其他插件到项目中,久而久之项目会不伦不类,有时候插件之间也会有一些冲突,所以我们可以自己写一个图片上传的方法. 今天的demo是帮朋友做的一个移动端微信公众号项目,项目架构采用angular+ionic,因为对dom的操作jQuery会方便很多,但是jQuery比较厚重,所以最后选择用轻量级的zepto来对项目dom进行操作. 项目中有一个需求是上传个人作品,