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

 

大概就是這個樣子了,代碼都寫了注釋。大家有什么建議或發現什么問題請留言,幫忙優化優化,謝謝~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM