答题卡AnswerSheet效果封装(二)原生js封装插件,可以

2021-04-23  本文已影响0人  Raral

答题卡效果

在前端开发中,我们大量使用开源很多UI框架和js框架,让我们使用的越好,做项目越快,但是同时让我们也对最基本的css属性和js最基本和最底层的api都遗忘,所以我们通过自己封装插件和组件,让我们更容易拾起最基本的知识点;以下我会从3个部分总结这个过程:1.原生js和jquery实现基本tab效果,2.使用面向对象js和jquery常用封装方法,3.通过vue封装和优化AnswerSheet组件

核心js封装

/**
* 插件作用:
* 1. 切换效果
* 2. 封装初始化模板,前端不需要构建dom结构
* 3. 对数据的处理,传入数据,修改数据状态,返回更新后数据
* 4. 监测用户正在操作当前数据状态
*/
(function(window) {
   function AnswerSheet(options,callback) {
       var opts = Object.assign({}, AnswerSheet.defaultOptions, options);
       this.callback = callback;
       this.opts = opts;
       this.insertDom = opts.insertDom;
       this.questions = opts.questions;
       this.activeFiled = opts.activeFiled;
       this.currentIndex = 0;//记录当前题目的索引
       this.cardConts = null;//获取所有卡片node
       this.btnNodes = null;//下一个按钮node
       this.answerLables = null;//当前卡片的答案
       this.activedQuestionNode = null;//记录激活状态答案node
       this.init();
   }
   AnswerSheet.prototype = {
       constructor: AnswerSheet,
       init: function() {
           this.initData(this.questions);
           this.initTemplate(this.questions);
       },

       //初始化模板,
       initTemplate: function(data) {
           let strHtml = ` <div id="answer" class="card_wrap">`;
           data.forEach((item, index) => {
               strHtml += `
                   <div class="card_cont card${index}" style="z-index:${data.length - index}">
                       <div class="card">
                           <p class="card_top">${index + 1}. &nbsp;${item.question}</p>
                           <div class="card_bottom">
                              ${item.answerList.length > 0 && this.renderChild(item.answerList)}
                               <div class="answer-bottom">
                                   ${index > 0 ? '<span class="prev" >上一题</span>' : ''}
                                   <span class="answer-currentNum">${index + 1}</span>/ <span class="answer-totalNum">${this.questions.length}</span>
                               </div>
                           </div>
                       </div>
                   </div>
               `
           })
           strHtml += `</div>`
           this.insertDom.appendChild(this.strToNode(strHtml));
           this.cardConts = document.querySelectorAll(".card_cont");
           this.btnNodes = document.querySelectorAll(".prev");
           this.answerLables =document.querySelectorAll(".answer-item");
           //给答案绑定事件
           this.bindEvent(this.answerLables,"click", this.nextHandler);
           //给上一个按钮绑定事件
           this.bindEvent(this.btnNodes, "click", this.prevHandler);
       },
       renderChild: function(answerList) {
           let strChild = ` <div class="answer-wrapper">`;
           answerList.forEach(item => {
               strChild += `<div class="answer-item" >${item.lable}</div>`
           })
           strChild += `</div>`
           return strChild;
       },

       //初始化数据
       initData: function(data) {
           data.forEach(item => {
               item.answerList.forEach(child => {
                   child[this.activeFiled] = false;
               })
           });

       },

       //初始化card编号
       initCardNum: function() {

       },

       //点击答案下一个卡片操作
       nextHandler(e) {
           //设置激活状态
           this.setActivedClass(e.target)
           //更新数据
           this.updateData(e.target.textContent,this.currentIndex);
           let restCount = this.cardConts.length - (this.currentIndex + 1);
           if(restCount <= 0) {
               this.callback && this.callback(this.questions,this.currentIndex);
               return;
           }
          
           this.cardConts[this.currentIndex].classList.remove("card0");
           this.cardConts[this.currentIndex].classList.add("cardn");
           restCount >=1 && this.changeCardClass("next",1, this.cardConts[this.currentIndex]);
           restCount >=2 && this.changeCardClass("next",2, this.cardConts[this.currentIndex]);
           restCount >=3 && this.changeCardClass("next",3, this.cardConts[this.currentIndex]);
           this.currentIndex += 1;
          
       },
       //点击上一个按钮卡片操作
       prevHandler() {
           let rest2 = this.currentIndex;
           let rest = this.cardConts.length - (this.currentIndex + 1);
           if(rest2 <= 0) {
               alert("上面没题了")
               return
           }
             //把当前变为cardn
           this.cardConts[this.currentIndex].classList.remove("card0");
           this.cardConts[this.currentIndex].classList.add("card1");
           //把上一个变为card0
           rest2 >=1 && this.changeCardClass("prev", -1, this.cardConts[this.currentIndex])
           //把下一个变为card0
           rest >=1 && this.changeCardClass("prev",1, this.cardConts[this.currentIndex]);
           //把下下个变为card1
           rest >=2 && this.changeCardClass("prev",2, this.cardConts[this.currentIndex]);
           //把下下下个变为card2
           // rest >=2 && changeCardClass("prev",3, cardConts[currentIndex]);
           this.currentIndex -= 1;
       },
       //获取当前card的相邻的卡片,处理相邻卡片类名变化
       changeCardClass(type, num, currentNode) {
           let _temp = null;
           switch (num) {
               case -1:
                   _temp = currentNode.previousElementSibling;
                   break;
               case 1:
                   _temp = currentNode.nextElementSibling;
                   break;
               case 2:
                   _temp = currentNode.nextElementSibling.nextElementSibling;
                   break;
               case 3:
                   _temp = currentNode.nextElementSibling.nextElementSibling.nextElementSibling;
                   break;
               default:
                   break;
           }
           //区分上一个和下一个操作
           if(type == "next") {
               _temp.classList.remove("card" + num);
               _temp.classList.add("card" + (num - 1));
           }else {
               if(num < 1) {
                   _temp.classList.remove("cardn");
                   _temp.classList.add("card0");
               }else {
                   _temp.classList.remove("card" + num);
                   _temp.classList.add("card" + (num + 1));
               }
           }  
       },

       //给选中答案设置激活样式
       setActivedClass: function(node) {
           //把兄弟node去除actice
           let siblingNodes = node.parentNode.childNodes;
           siblingNodes.forEach(item => {
               item.classList.remove("active");
           })
           node.classList.add("active");
       },
       //修改数据状态
       updateData: function(value, index) {
           console.log(value, index);
           let question = this.questions[index];
           question.answerList.forEach(item => {
               item[this.activeFiled] = false;
           })
           let answer = question.answerList.find(item => item.lable == value);
           answer[this.activeFiled] = true;

           console.log(this.questions)
       },
       //绑定事件
       bindEvent: function(node, event, cb) {
           if(node.length >= 0) {
               Array.prototype.forEach.call(node, item => {
                   item.addEventListener(event, (e) => {
                       cb && cb.call(this,e);
                   }, false)
               })
           }else {
               node.addEventListener(event, (e) => {
                   cb && cb.call(this,e);
               }, false)
           }
       },
       //字符串转换成 DOM对象
       strToNode: function(strHtml) {
           return new DOMParser().parseFromString(strHtml,'text/html').body.childNodes[0];
       }
   }


   AnswerSheet.defaultOptions = {
       insertDom: null,//设置要插入的节点
       questions: [],//传入题目数据,结构是有要求的,层级感
       activeFiled: "checked",//设置答案的激活状态字段

   }

   return window.AnswerSheet = AnswerSheet;

})(window)

使用

var data = [
   {
       id: "001",
       question: "这是第一个问题",
       answerList: [
           {
               id:"001",
               lable: "A",//这个是前端展示的字段,根据自己业务需求进行设置
               value: 10// 这个是后台真实所处理的字段,根据自己业务需求进行设置
           },{
               id:"002",
               lable: "B",
               value: 20
           },{
               id:"003",
               lable: "C",
               value: 10
           },{
               id:"004",
               lable: "D",
               value: 20
           }
       ]
   },{
       id: "002",
       question: "这是第二个问题",
       answerList: [
           {
               id:"001",
               lable: "A",//这个是前端展示的字段,根据自己业务需求进行设置
               value: 10// 这个是后台真实所处理的字段,根据自己业务需求进行设置
           },{
               id:"002",
               lable: "B",
               value: 20
           },{
               id:"003",
               lable: "C",
               value: 10
           },{
               id:"004",
               lable: "D",
               value: 20
           }
       ]
   },{
       id: "003",
       question: "这是第三个问题",
       answerList: [
           {
               id:"001",
               lable: "A",//这个是前端展示的字段,根据自己业务需求进行设置
               value: 10// 这个是后台真实所处理的字段,根据自己业务需求进行设置
           },{
               id:"002",
               lable: "B",
               value: 20
           },{
               id:"003",
               lable: "C",
               value: 10
           },{
               id:"004",
               lable: "D",
               value: 20
           }
       ]
   },{
       id: "004",
       question: "这是第四个问题",
       answerList: [
           {
               id:"001",
               lable: "A",//这个是前端展示的字段,根据自己业务需求进行设置
               value: 10// 这个是后台真实所处理的字段,根据自己业务需求进行设置
           },{
               id:"002",
               lable: "B",
               value: 20
           },{
               id:"003",
               lable: "C",
               value: 10
           },{
               id:"004",
               lable: "D",
               value: 20
           }
       ]
   },{
       id: "005",
       question: "这是第五个问题",
       answerList: [
           {
               id:"001",
               lable: "A",//这个是前端展示的字段,根据自己业务需求进行设置
               value: 10// 这个是后台真实所处理的字段,根据自己业务需求进行设置
           },{
               id:"002",
               lable: "B",
               value: 20
           },{
               id:"003",
               lable: "C",
               value: 10
           },{
               id:"004",
               lable: "D",
               value: 20
           }
       ]
   }
]


<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <meta http-equiv="X-UA-Compatible" content="IE=edge">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>答题卡</title>
   <style>
       * {
           margin: 0;
           padding: 0;
       }
       .wrapper {
           padding-top: 34px;
           padding-left: 12px;
           padding-right: 12px;
           padding-bottom: 30px;
           background-color: #999;
       }
       .card_wrap {
           width: 100%;
           height: 500px;
           position: relative;
           overflow: hidden;
       }
       .card_cont {
           width: 100%;
           height: 452px;
           box-sizing: border-box;
           margin: 0 auto;
           position: absolute;
           border-radius: 10px;
           /* background-color:rgb(59, 178, 115); */
           background-color: #fff;
           box-shadow: 1px 1px 2px #ddd;
           position: absolute;
           bottom: 0;
           display: block;
           transition: all 1s;
       }
       /* 表示在视图第一个卡片状态 */
       .card0 {
           transform: scale(1,1) translate(0, 0) !important;
       }
         /* 表示在视图第二个卡片状态 */
       .card1 {
           transform: scale(.85,.85) translate(0, -62px) !important;
       }
         /* 表示在视图第三个卡片状态 */
       .card2 {
           transform: scale(.72,.72) translate(0, -135px) !important;
       }
      
       .next {
           color: #f00;
           position: relative;
           right: 0;
           bottom:0;
       }
       .cardn {
           transform: translate(0, -1000px);
       }


       /* 卡片样式 */
       .card {
           /* height: 3.75rem; */
           /* line-height: 3.75rem; */
       }
       .card .card_top {
           line-height: 60px;
           text-indent: 40px;
           color: #fff;
            background-color:rgb(59, 178, 115);
       }
       .card .card_bottom {
           background-color: #fff;


       }
       .card .card_bottom .answer-wrapper {

       }
       .answer-item {
           color: rgb(59, 178, 115);
           font-size: 14px;
           line-height: 30px;
           text-align: center;
           border-radius: 20px;
           border: 1px solid rgb(59, 178, 115);
           width: 60%;
           margin: 20px auto;
       }
       .answer-bottom {
           color: rgb(59, 178, 115);
           text-align: right;
           margin-top: 20px;
           width: 60%;
           margin: 60px auto;
       }
       .prev {
           float: left;
       }
       .active {
           background-color: rgb(59, 178, 115);
           color: #fff;
       }
       

   </style>
</head>
<body>
   <div class="wrapper"></div>
   
</body>
<script src="./js/data.js"></script>
<script src="./js/answerSheet.js"></script>
<script>
 new AnswerSheet({
   insertDom: document.getElementsByClassName("wrapper")[0],
   questions: data
 }, function(data,index) {
   alert("做完题了,可以进行自己的操作逻辑")
   alert("提交数据"+ JSON.stringify(data));
 })
</script>
</html>

效果

1619167053(1).jpg

下次更新使用vue封装AnswerSheet组件
使用vue封装组的新功能:

  1. 根据题目种类变颜色
  2. 可以人工播放题目功能
    万水千山总是情,点波关注行不行呦!!!
上一篇下一篇

猜你喜欢

热点阅读