2017年四月初,有幸参加了奇虎360组织的前端星计划并且顺利拿到360搜索的暑假实习生offer,应360的波波老师(PS:真名裕波,绝对是程序员中最会主持的,主持界中代码写的最好的)的号召,写成博客分享给大家。
先放一张图给大家,这是在奇虎360总部参加星计划的最后一天拍的合影。
跟所有互联网公司校招一样,通过一次笔试以及一份作业去甄选了一些小伙伴,因为这次HR丹丹姐大发善心,所以名额扩至了60人,估计是这样,所以我才有幸入选的吧,毕竟笔试成绩还是有点低的。。。 (笔试:78,作业:90),看上去还可以? 其实笔试满分 160 ,作业满分 200 。。。哦,天呐!
贴出第三届前端星计划的作业题目:
任务要求如下
在移动设备上,“手势密码”是一个很常见的UI。实现一个移动网页,允许用户设置手势密码和验证手势密码。已设置的密码记录在本地 localStorage 中。
请同学们按照上面的需求实现这个网页,在手机上可用。可以不用太考虑古老机器的兼容性,最新的 android 和 iPhone 可用即可。
代码实现如下
为了结构清晰,模块化依赖使用了 requireJs 。偷个懒使用了JQuery方便各种选择器。
index.html
<html>
<head lang="en">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"/>
<meta charset="UTF-8">
<title>title>
<link rel="stylesheet" href="./css/normalize.css"/>
<link rel="stylesheet" href="./css/style.css"/>
head>
<body>
<div id="unlock">
<h1>手势密码h1>
<div class="unlock-bg">div>
<ul class="unlock-circle">
<li>li>
<li>li>
<li>li>
<li>li>
<li>li>
<li>li>
<li>li>
<li>li>
<li>li>
ul>
<p class="unlock-prompt">请输入手势密码p>
<div class="unlock-radio">
<label><input name="Fruit" type="radio" checked="checked"/> 设置密码label><br/>
<br/><label><input name="Fruit" type="radio"/> 验证密码label>
div>
div>
<script src="./js/require.js" data-main="./js/main.js">script>
body>
html>
入口 js 文件,main.js
require.config({
paths : {
"jquery" : "jquery-3.1.1.min"
}
});
define(['jquery', 'calcAngle', 'init'],function($, calcAngle, unlockSeq){
var seq = "";
var $container = $('#unlock');
var $li = $('.unlock-circle > li');
var $p = $('.unlock-prompt');
var $input = $('.unlock-radio input');
var queue = []; //盛放已经装入的元素
var lastData = {}; //存放上一次触碰的圆点
var confirmSeq = ""; //用于设置密码的确认密码
//预处理圆点的圆心以及其序号
$li.each(function(index, elem){
elem.index = index;
elem.pos = {
left: elem.offsetLeft + elem.offsetWidth / 2,
top: elem.offsetTop + elem.offsetHeight / 2
}
});
/*判断 localstorage 中是否存在值,如果不存在,那么默认输入解锁手势,
若存在,则设置解锁手势,设置手势默认输入一遍*/
$container.on('touchmove', touchMove);
$container.on('touchend',function(event){
var setFlag = $input.eq(0).prop('checked'); //判断当前是否在设置密码
if(lastData.$hr){
lastData.$hr.remove();
$container.off('touchmove');
setTimeout(function(){ //800ms后页面初始化
queue = [];
lastData = {};
seq = "";
$('hr').remove();
$p.html('请输入手势密码');
$li.removeClass('unlock-selected')
$container.on('touchmove', touchMove);
},800);
if(setFlag && confirmSeq){ //确认设置密码
if(confirmSeq == seq){
localStorage.setItem("unlockSeq", confirmSeq);
unlockSeq = confirmSeq;
$input.removeProp('checked');
$input.eq(1).prop('checked', 'checked'); //变换至解锁状态,同时更新 localstroge
$p.html('密码设置成功');
confirmSeq = "";
}
else{
confirmSeq = "";
$p.html('两次输入的不一样');
}
return ;
}
if(setFlag && seq.length < 5){
$p.html('密码太短,至少需要5个点');
return ;
}
//初始设置密码
if(setFlag && !confirmSeq) { //首次设置密码
confirmSeq = seq;
$p.html('请再次输入手势密码');
return ;
}
if(!setFlag){ //验证密码
if(!unlockSeq){ //如果没有设置密码去验证密码,强行跳回设置密码
$input.removeProp('checked');
$input.eq(0).prop('checked', 'checked');
$p.html('请先设置密码');
}
else{
if(unlockSeq != seq) $p.html('输入的密码不正确');
else $p.html('密码正确!');
}
}
}
});
function touchMove(event){
var clientX = event.touches[0].clientX;
var clientY = event.touches[0].clientY;
var $target = collision(clientX, clientY);
var angle = 0;
if($target && $.inArray($target.prop('index'), queue) == -1){
var index = $target.prop('index');
queue.push(index);
seq += index;
$target.addClass('unlock-selected');
var $hr = $('
');
$container.append($hr);
var pos = $target.prop('pos');
//不是第一次进来,既已经在画线...
if(lastData.$hr){
angle = calcAngle(pos.left, pos.top, lastData.pos);
lastData.$hr.css({
'width': dist(pos.left, pos.top, lastData.$target),
'transform': 'rotate(-'+ angle +'deg)'
});
}
//保存这次的状态
lastData.$hr = $hr;
lastData.pos = pos;
lastData.index = index;
lastData.$target = $target;
angle = calcAngle(clientX, clientY, lastData.pos);
$hr.css({
'left': pos.left,
'top': pos.top,
'width': dist(clientX, clientY, $target),
'transform': 'rotate(-'+ angle +'deg)'
});
}else if(lastData.$hr){
angle = calcAngle(clientX, clientY, lastData.pos);
lastData.$hr.css({
'width': dist(clientX, clientY, lastData.$target),
'transform': 'rotate(-'+ angle +'deg)'
});
}
event.preventDefault(); //禁止默认行为
}
//碰撞检测
function collision(x, y){
var $target;
var flag = false;
$li.each(function(index, elem){
var distance = dist(x, y, $(elem));
if(distance < elem.offsetWidth / 2){
flag = true;
$target = $(elem);
}
});
if(flag) return $target;
else return null;
}
//计算 触点 到 LI 圆心的距离
function dist(x, y, $target){
var pos = $target.prop('pos');
return Math.sqrt((x - pos.left) * (x - pos.left) + (y - pos.top) * (y - pos.top));
}
});
具体代码实现请见:我的 github
运行实例(可在chorme下模拟):戳我看实例
更多干货请见系列文章。