说来惭愧,好久没有更新博客了,大概过了两个多月吧,这两个多月当然并没有出去浪啊,还是在好好的学习代码的。前一段时间和后端合作了两个项目,一个是问卷平台的项目,就是这次将要总结的内容,还有一个是一个个人的博客。这两个项目也算是和后端的两次比较详细的合作,相比上次的微信小程序有了更大的提高。两个项目坐下来感觉技术提高了不少,但是由于赶进度的原因也欠下了很多技术债,寒假时候要好好的补一补了。不过正所谓业务驱动需求,需求驱动技术嘛。

两个项目下来,发现都用原生的代码写真的累,前一段时间每天基本都是代码,没怎么闲过,有很多重复的劳动,原生代码也写了不少,感觉可以进行学习一些框架了。

页面展示

界面做的还是很low的,不过也还勉强能看

技术细节

这一部分将整理一下在写页面的时候学到的一些新的技术。

首页

首页部分其实做的挺丑的,当时时间比较紧,所以并没有进行设计,直接就开始动手写了,这部分都是一些很常见的东西,但是有一块就是一个正反面的翻转效果感觉挺赞的,是从别的网站上看到然后学习了一下写出来的。

从技术上看实现方法就是采用了两个div层,一个显示的是正面,另一个反面使用rotateY(-180deg)来进行一个180度的翻转,显示成反面,然后使用z-index来达到一个遮罩效果,然后就是hover实现一个点击后的效果将正面设置为rotateY(180deg)并将透明度opacity设置成0,反面设置为rotateY(0deg)透明度opacity设置为1,来使转换更加的自然。不过需要注意的是要将position设置为绝对定位或一些其他的方式,不然z-index不会生效。关键代码如下:

1
2
3
4
5
6
7
8
9
10
<div class="developers">
<!-- <img src="img/xiaoqi.png"> -->
<div class="renpicture">
<div class="img img1"></div>
<div class="img-font">
<p>后端开发</p>
</div>
</div>
<div class="developers-font LwolveJ-font">LwolveJ</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
.developers {
width: 33.3%;
height: 500px;
display: inline-block;
vertical-align: top;
text-align: center;
position: relative;
}

.renpicture {
width: 250px;
height: 250px;
margin: 0 auto;
position: relative;
-webkit-perspective: 1000;
}

.img , .img-font {
position: absolute;
margin: 0 auto;
width: 250px;
height: 250px;
border-radius: 50%;
box-shadow: 0 3px 5px #9E9E9E;
-webkit-transition: all 0.5s;
}

.img {
z-index: 5;
background-size: 250px 250px;
}

.img1 {
background-image: url("../img/lijie.jpg");
}

.img-font {
font-size: 25px;
background-color: #1799e5;
-webkit-transform: rotateY(-180deg);
opacity: 0;
}

.img-font p {
line-height: 250px;
color: #FFFFFF;
}

.renpicture:hover .img{
-webkit-transform:rotateY(180deg);
z-index: 5;
opacity: 0;
}

.renpicture:hover .img-font {
-webkit-transform: rotateY(0deg);
z-index: 10;
opacity: 1;
}

生成问卷页面

这一个页面所涉及到的代码最多,而且也是该项目的重点,需求主要就是右侧的窗口能生成标题、单选题、多选题、简短回答这几个问卷常见的题型,而且右边所添加的选项能即时在右侧展示,并且能删除选项,然后点击生成问卷会在左侧显示出相应的效果。初次之外还有个比较人性化的细节,即右侧的操作框会随着左侧题目的位置而变动。效果大概就是这么多,那么就是一些技术的实现了。

首先是HTML结构,右侧操作框的HTML结构每种题型都写成一块HTML,然后都使用display:none隐藏,然后默认将标题显示为第一个,然后通过js获取下拉框选择的题型来显示相应的格式。然后是CSS部分,这一部分都是一些之前就用过的,所以此处不再整理。本部分将着重整理js部分的代码。

表单切换功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//===================表单切换=======================
var inputTypeSelectBtn = g('input-type-select');

var selectTypes = {
"title": g("title-box"),
"radio": g('radio-box'),
"checkbox": g('checkbox-box'),
"text": g('text-box')
}

//获取选择value
var getSelectContent = function(select) {
var index = select.selectedIndex;
return select.options[index].value;
}

//初始化value值
var selectValue = getSelectContent(inputTypeSelectBtn);

var changeType = function(type) {
for(var i in selectTypes){
selectTypes[i].style.display = 'none';
}
selectTypes[type].style.display = "block";
}

//当表单切换时触发
inputTypeSelectBtn.onchange = function() {
selectValue = getSelectContent(inputTypeSelectBtn);
changeType(selectValue);
}

该部分思路是当用户点击题型切换时出发onchange然后getSelectContent函数获取改变的值,并返回给selectValue值,然后当作参数传给changeType函数来控制相应表单的显示与隐藏,这里将四种题型的节点都写在了selectTypes对象里面,也同时方便了后面其他功能的实现。

选项的添加与删除功能

首先是绑定事件,分别是鼠标点击事件和键盘回车事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var addOptionsBtns = {
'radio': selectTypes['radio'].getElementsByClassName('add-btn')[0],
'checkbox': selectTypes['checkbox'].getElementsByClassName('add-btn')[0]
}

var addOptionsInputs = {
'radio': selectTypes['radio'].getElementsByClassName('add-option')[0],
'checkbox': selectTypes['checkbox'].getElementsByClassName('add-option')[0]
}

//为按钮绑定事件
for(var key in addOptionsBtns) {
(function(e) {
addOptionsBtns[e].onclick = function() {
var value = selectTypes[e].getElementsByClassName('add-option')[0].value;
if(value === "") {
return false;
}
addOption(selectTypes[e], e, value);
selectTypes[e].getElementsByClassName('add-option')[0].value = '';
}
})(key)
}

for (var key in addOptionsInputs) {
(function(e) {
addOptionsInputs[e].addEventListener('keyup', function() {
if (event.keyCode === 13) {
addOptionsBtns[e].click();
}
});
})(key)
}

此处for循环里面的function事件采用了(function(e){}(key))来调用,key即function的参数,当js运行到此处的时候将直接调用,里面的两个addOptionsBtns和addOptionsInputs对象分别对应button按钮的点击事件和input输入框的键盘事件,键盘事件实现的逻辑就是调用了点击事件addOptionsBtns[e].click();

然后是添加选项和删除选项功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//======================配置选项添加及删除功能===========

var Option = function(type, value, deleteBtn, selfElement) {
this.type = type;
this.value = value;
this.deleteBtn = deleteBtn;
this.deleteBtn.self = this;
this.selfElement = selfElement;
this.selfElement.self = this;
}

var options = []; //存放所有选项
var addOption = function(selectTypes, key, value) {


var type = key;
var optionsWrap = selectTypes.getElementsByClassName('options-wrap')[0];
var optionWrap = document.createElement('div');
optionWrap.className = 'option-wrap';
optionWrap.innerHTML = '<div class="delete-mask">删除</div> <span class="option-set">' + value + '</span>';
optionsWrap.appendChild(optionWrap);
var deleteBtn = optionWrap.getElementsByClassName('delete-mask')[0];
var selfElement = optionWrap;
var option = new Option(type, value, deleteBtn, selfElement);
options.push(option);

deleteBtn.addEventListener('click', function() {
deleteOption(deleteBtn);
});
}

var deleteOption = function(btn) {
for(var i = 0; i < options.length; i++) {
if(options[i] === btn.self) {
options.splice(i ,1);
break;
}
}
btn.self.selfElement.parentNode.removeChild(btn.self.selfElement);
delete btn.self;
}

这一部分先是定义了一个Option()函数,并在addOption()中new了一个option对象出来,然后使用push方法依次存入开始定义的options数组里面,此处的new是一个我目前还不完全理解的用法,以及js原型链,这一个项目整理完之后将整理一片关于new和js原型链的文章。

右侧框移动功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var formSetBox = g('content-right');
var formBox = g('content-body');
var moveSetBox = function() {
var questionElements = formBox.getElementsByClassName('question-wrap');
var topPosition = formBox.offsetHeight - questionElements[questionElements.length - 1].offsetHeight;
startMove(formSetBox, topPosition, 10);
}

var timer = null;
var positionTop = 0;
startMove = function(element, target, interval) {

clearInterval(timer);
timer = setInterval(function() {
var speed = (target - element.offsetTop) / 10;
speed = speed > 0 ? Math.ceil(speed) : 0;
if (element.offsetTop >= target) {

clearInterval(timer);
} else {
positionTop = positionTop + speed;
element.style.top = positionTop + 'px';
}
}, interval)
}

moveSetBox()函数用来计算目标位置即target,startMove()函数是核心功能,target-element.offsetTop是计算目标位置和右侧框到顶部的距离只差。然后,此处需要一些数学的计算,计算出speed速度,interval参数是毫秒单位,即每10毫秒的speed,在该函数中即每10毫秒移动speed值的px,可知随着越接近位置,speed越来越小,但是由于ceil()函数的存在,speed不会小于0。

HTMLElement.offsetHeight 是一个只读属性,它返回该元素的像素高度,高度包含该元素的垂直内边距和边框,且是一个整数。

HTMLElement.offsetTop 为只读属性,它返回当前元素相对于其 offsetParent 元素的顶部的距离。

问卷填写页面

这部分页面内容较为简单,有一处值得整理的就是页面间传参的一个功能,页面间传参就是说在问卷选择页面选择一个问卷之后会有使用window.location.href来指定url,然后根据参数的不同来获得问卷内容,并渲染出相应的页面。这里是通过地址传值,并使用函数解析地址,获取到参数,然后GET请求获取到问卷内容。

1
2
3
4
5
6
7
//页面间参数传递函数获取函数
var parseURL = function(url) {
var url = url.split("?")[1];
var res = url.split("=")[1];
console.log(res);
return res;
}

上面代码即获取参数的代码,res即参数。

问卷结果展示页面

该部分通过ajax获取相应选择人数数据,然后乘上一定比值,来使图标更加清晰,然后使用js渲染出来即可,建议可以先在html和对应css写上假内容,然后再来写js,之后将html部分注释掉即可。

后记

这次项目算得上真正意义的第一个和后端合作开发的项目,之前的微信小程序更多依靠着封装的API勉强完成,很多地方的原理都不太懂。这次通过完全的原生HTML+CSS+JS开发的项目,虽然代码较多,使用很多框架之类的可以有效的简化代码,但是基础还是很重要的,所以这次项目很有必要。