公司的项目中需要对数据做可视化处理,高级点的D3.js目前还没接触到,因此选用了大众化的Echarts, 在vue的生态系统中已经有实现好的vue-echarts,但是使用现成的就意味着必须使用它定制好的数据结构,我也没办法对他进行一些修改。我个人也偏向于原生JS编程,因此没有采用,而是自己在vue中实现了对数据的可视化处理,先来看看效果图
这是目前用到的三种图。
可以看到,我在图表的外部添加了标题及说明,以及右侧的选择框组件,视图可以根据选择的不同,图表进行动态切换(echarts也是数据驱动),这就是个人定制化的好处
所有的数据都是动态获取的,由前端向后台请求。当然请求数据肯定不会放在图表组件中,而是放在了外部。因为架构设计的不合理(MD前端就我一个人!),因此前期获取数据及存取数据的方式,和后期也较大的不同。
这么大型的项目,vuex少不了。在前面的组件中,一次请求数据,然后将数据存储到了vuex中,echarts组件再从vuex中获取数据。这样的做法可能代码要稍微复杂点,但是数据存储在vuex中是随时可见的,我们也能随时保存获取的结果,对这些数据可以添加收藏也可以加入缓存中,下次再请求可以先从缓存调用。
(然而这只是我前端的想法,后台已经实现了对请求数据的缓存)
再对数据进行还原的时候(我的收藏,最近浏览),因为不需要保存或者收藏数据,我就直接用一个父组件请求,然后再分发到echarts组件,这样没有经过vuex,代码想多要少些。
<template> <div class="r-echarts-line"> <div class="top"> <div class="title"> {{origin.title}} </div> <div class="select-list"> <Select style="width:120px;margin-right:.5rem" v-model="pagePick"> <Option v-for="item in origin.page_select" :key="item" :value="item.val">{{item.name}}</Option> </Select> <Select style="width:120px" v-model="typePick"> <Option v-for="item in typeList" :value="item.name" :key="item">{{item.name}}</Option> </Select> </div> </div> <div class="des">说明:符合于本次筛选条件的共有<span class='tips'>{{origin.desc}}</span>条<span style="font-weight:700;color:black">职位信息</span>。</div> <div class="bottom" id="echart" ref="mychart"> </div> </div></template>
这是组件的html部分,可以看见top以及des是我自己添加的,bottom才是核心,也是整个echarts展示的部分,注意这里添加了ref,在script的代码中,我们将通过这个钩子,将DOM挂载到echarts中
<script>// echarts相关let echarts = require('echarts/lib/echarts');require('echarts/lib/chart/bar');require('echarts/lib/component/tooltip');require('echarts/lib/component/toolbox');require('echarts/lib/component/legend');require('echarts/lib/component/markLine');export default { name: 'r-echarts-line', data () { return { typePick: '数值', typeList: [ { name: '数值' }, { name: '百分比' } ], pagePick: 0, // myChart实例 myChart: {}, percent: { label: { normal: { show: true, position: 'inside', formatter: '{c}%' } } }, numeric: { label: { normal: { show: true, position: 'inside', formatter: '{c}' } } } } }, props: { index: { required: true, type: Number }, data: { required: true, type: Object } }, mounted () { this.setEchart(); }, updated () { if (!this.myChart) { this.setEchart(); } this.chartChange(); }, computed: { origin () { return this.data; }, opt() { let that = this; let obj = { color: ['#606c94'], tooltip: { }, toolbox: { show: true, feature: { saveAsImage: {show: true} } }, label: { normal: { show: true, position: 'inside', formatter: '{c}' }, emphasis: { show: true } }, xAxis: { type: 'value', }, yAxis: { data: that.origin[that.type][that.pagePick].key, axisLabel: { interval: 0, rotate: -30 } }, series: [{ name: that.origin.title, type: 'bar', data: that.origin[that.type][that.pagePick].val, barMaxWidth: '30', markLine: { data: [ {type: 'average', name: '平均值'} ] } }] } return obj; }, type () { if (this.typePick == '数值') { return 'numeric'; } else if (this.typePick == '百分比') { return 'percent'; } else { return 'numeric'; } } }, methods: { setEchart () { let dom = this.$refs.mychart; this.myChart = echarts.init(dom); this.myChart.setOption(this.opt); }, chartChange () { this.myChart.setOption(this.opt); if (this.typePick == '百分比') { this.myChart.setOption(this.percent); } if (this.typePick == '数值') { this.myChart.setOption(this.numeric); } } }}</script>
首先我引入了需要的echarts组件,这个部分通过npm i echarts -S添加。
接着data部分我设置了那些将会引起变化的参数。需要注意的是,我并没有将echarts的opt部分写入到data中,因为整个图表是基于数据驱动的,并且随时会发生变化,因此我将opt设置为计算属性computed,这样opt将会根据我的选择动态变化,echarts也将动态响应,mychart用于接收echarts生成的图表实例,再参数变换的时候将会起作用。
props部分是我接收到的参数,这个组件时基于前面我讲的第二种方式——父组件获取数据分发,data是父组件分发给echarts的数据源。
暂时忽略两个Vue生命周期钩子, 后面讲
计算属性中设置了两个属性,origin和opt,注意这个origin很重要,通过它我将opt项与复杂的数据解耦,无论外面的数据怎么换,opt只关心origin的值,而这个opt在两种数据获取的方式中是不一样的,使用vuex的方式,origin将会直接从vuex中获取数据。这样一定程度上也实现了组件的复用。
opt就是该图表组件的设置项了,这个参数按照官网给的配置,自己搭配即可。
接下来是methods部分,setEchart将会完成对整个图表的初始化,通过this.$refs获取DOM实例,再由echars生成实例并绑定在data中的mychart选项。
chartChange是用来响应我自定义组件的变化的,针对选框的不同将会有不同的显示情况。在这里是百分比和数据的切换
接着是前面忽略的生命周期部分
mounted里使用setEchart方法,初始化图表组件,一定要在这里使用该方法,否则会找不到DOM
updated周期里是响应参数变化的方法,首先检测该实例有没有生成(单页应用因为用户可能存在的误操作,很可能导致实例没有生成,这里检测是很有必要的),接着在vue中的数据发生改变时运行chartChange方法,注意,我的选择框是没有绑定事件的,只是通过v-model改变了参数,然后opt动态响应了参数的变化。当opt的参数变化的时候,updated中的方法就会执行,echarts也会动态响应。这个就是使用基于数据驱动vue最精巧的地方,避免了通过事件调用echartChange方法。也是vue中使用echarts核心的一环
另外还有一个就是获取地图参数的,并不用在官网里下载,提供的npm包里就有,按需引用就好了(使用官网的js版本会报错没找到echarts)
import echarts from 'echarts/lib/echarts';import 'echarts/lib/chart/map';import 'echarts/map/js/china.js';
style部分就没什么好聊的了,只需要记住一点,必须显式指定加载echarts 的DOM的宽度和高度
调用组件的方法按照常规组件调用就好了,只是因为echarts加载数据绘制需要耗费不少时间,我们可能需要通过keep-alive保存组件在内存中,避免切出去的时候被释放了。另外可能一个页面需要展示多个echarts类型组件,这里考虑使用component动态组件
<template> <div class="focus-echarts-wrap"> <keep-alive> <component :is="currentView" :index="focusType"></component> </keep-alive> </div></template>
vue 中有一个 keep-alive 的功能,可以将组件渲染结果缓存起来,结合 vue-router 中使用,可以缓存某个 view 的整个内容(比如列表页)。
通常会有这样的需求:
用户单击列表页(List)中某个标题,进入到详情页(Detail)。
此后会有两种情况返回到 List:
用户点击导航栏中的 /list 链接;
用户点击浏览器返回按钮 ←
情况 1 发生时,我们希望用户进入 List 后,页面的数据需要刷新。用户在导航切换时(点击导航菜单中的链接),其意愿是需要页面刷新,由于我们的应用是 SPA 应用,所以需要主动重新发送 ajax 请求获取数据,以让用户知晓,页面的数据有变化,否则他还要手动去点击浏览器的刷新按钮。
情况 2 发生时,我们希望用户进入 List 后,按照 h5 的行为自动将滚动条还原到原来位置,并且此时不应刷新页面数据,直接使用缓存。
需要缓存数据,那么在 app.vue 中的 router-view 配合使用 keep-alive :
入口文件(app.vue):
//- app.vue <template> #app .page-header .page-main keep-alive router-view(v-if="$route.meta.keep_alive") router-view(v-if="!$route.meta.keep_alive") .page-footer </template>
注: v-if 是加在 router-view 上,而不是加在 keep-alive 上。
路由配置(router.coffee):
# router.coffee router = new VueRouter( mode: 'history' # 需要使用 h5 模式,才能使用 scrollBehavior routes: [ { path: '/list' name: 'list' meta: title: '列表页' keep_alive: true # true 表示需要使用缓存 component: require('../views/list.vue') }, ... ] scrollBehavior: (to, from, saved_position) -> # 保存到 meta 中,备用 to.meta.saved_position = saved_position return saved_position ? {x: 0, y: 0} )
注:
某一个 route 的 meta.keep_alive 值,设定之后就不能再修改其值,即不能动态变更 meta.keep_alive 的方式来动态控制是否使用缓存。
原因:某个 route.meta.keep_alive 初始设定为 true,会有以下两种情况:
若在 scrollBehavior 中判断出是情况 1,此时如果使用 to.meta.keep_alive = false
来禁用缓存,那么 app.vue 中的两个 router-view 会切换,之前缓存在 <keep-alive/> 中的第一个 router-view 的数据,会被 v-if 清空,导致缓存失效;
同理,当判断出是情况 2,如果 to.meta.keep_alive = true
来开启缓存,此时又会发生一次切换,导致重新生成组件。故 route.meta.keep_alive 一旦设定,就表示此组件一定是需要缓存的,后期不能改变其值。
既然不能改变 meta.keep_alive 的值,那么如果做到在 keep-alive 包裹内的组件进行数据刷新呢?
答案是:使用 vue-router 中的 beforeRouteEnter 钩子。
这里不能使用 vue 文档中的 mounted、updated 等生命周期中的钩子函数,因为在使用 keep-alive 后,这些钩子,统统不会被调用。
而在 vue-router 中提供了 beforeRouteEnter 钩子。此钩子与 keep-alive 无关,不会像 mounted 等内置钩子那样被 keep-alive “吃掉”。
# list.coffee export default List = name: 'List' data: -> lists: [] beforeRouteEnter: (to, from, next) -> next (vm) -> # 如果 saved_position === null,那么说明是点击了导航链接(情况1), # 此时需要刷新数据,获取新的列表内容。 # 否则什么也不做,直接使用 keep-alive 中的缓存。 if to.meta.saved_position is null vm.fetchLists() methods: fetchLists: -> @$store .dispatch(types.dashboard.actions.GET_LISTS) .then (res) => if res.status == 'success' @posts = res.data else @posts_loading_error_message = res.data.msg
注: 注意 beforeRouteEnter 中使用的是 is null,而不是 if to.meta.saved_position?。
一点说明:
is null 相当于 === null;
to.meta.saved_position? 相当于 to.meta.saved_position == null。
undefined == null 为 true,但 undefined === null 为 false。
这一点很重要,因为在调试过程中,如果你在 beforeRouteEnter 的 next 方法中:
console.log to.meta.saved_position
当点击的是导航链接(情况 1),此时输出 null,因为 scrollBehavior 中的 saved_position 为 null。
这里显示相关代码:
// src/util/scroll.js#L40// wait until re-render finishes before scrollingrouter.app.$nextTick(() => { let position = getScrollPosition() const shouldScroll = behavior(to, from, isPop ? position : null)
可以看出 isPop ? position : null 当点击导航链接(情况 1, isPop == false)时,传递的是 null。
其中 isPop 可以理解为是否是点击了浏览器后退按钮。点击了就传递 position,否则传递 null。
那么什么时候会出现 undefined 的情况呢?
上面代码中的 position = getScrollPosition(),其 源码 如下:
function getScrollPosition (): ?Object { const key = getStateKey() if (key) { return positionStore[key] }}
其中 positionStore 的值由 函数 saveScrollPosition 保存。
如果找到了 key 就返回,此时为形如 {x: 33, y: 12} 的值,表示之前横纵滚动条的位置。
如果没有找到 key,此函数没有返回任何内容,那么自然是返回 undefined 了。
今天我们来分享一款模拟汉字书写笔画特效果,我们先看图:
这款效果的代码比较少,我们直接上代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>HTML5 汉字书写笔画特效 </title>
<script src="js/hanzi-writer.min.js"></script>
<script src="js/polyfill.min.js"></script>
</head>
<body>
<div id="character-target-div"></div>
<button id="animate-button">重写</button>
<script>
var char = '你好世界',
writer = [];
for(var x = 0; x < char.length; x++) {
writer.push(writeChar(char[x]))
}
function writeChar(char) {
return HanziWriter.create('character-target-div', char, {
width: 100,
height: 100,
padding: 5,
showOutline: true
});
}
document.getElementById('animate-button').addEventListener('click', function() {
if(writer.length > 0) {
writer.map(function(w) {
w.animateCharacter()
})
}
});
</script>
</body>
</html>
这个用到js文件一起在附件里,那么我们下期再见了
今天我们来分享一款很好看的千纸鹤飞翔效果,如图所示:
主要是使用到Canvas这个额技术。先看html5的代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>HTML5 Canvas千纸鹤动画特效</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div id="controls"></div>
<script src='js/p5.min.js'></script>
<script src="js/index.js"></script>
</body>
</html>
然后是样式的效果:
* {
margin: 0;
padding: 0;
}
html, body {
width: 100%;
height: 100%;
overflow: hidden;
background: black;
}
canvas {
display: block;
}
#controls {
z-index: 2;
margin: 20px;
position: absolute;
top: 0;
left: 0;
color: white;
}
接下来是脚本:
birds = [];
let numBirds = 50;
class Bird{
constructor(){
this.pos = [random(width), random(height)];
this.size = pow(random(), 2)*80 + 40;
this.angle = random(PI/16) + PI/8;
this.speed = this.size/600;
this.hue = random();
let p = [
[0, 0],
[random(.00, .10), random(.1, .4)], //beak
[random(.10, .50), -random(.1, .2)], //head
[random(.50, .80), random(.1, .3)], //body
[random(.80, 1.0), -random(.2, .4)], //tail
[1, 0],
[random(.30, .40), 0], //wing base 1
[random(.80, .90), 0], //wing base 2
[random(.50, .65), -random(.4, .6)], //wing tip 1
[random(.65, .70), -random(.4, .6)], //wing tip 2
];
this.triangles = [
// idx, idx, idx, color , isWing
[...p[0], ...p[1], ...p[2], randColor(this.hue), false], //head
[...p[3], ...p[4], ...p[5], randColor(this.hue), false], //tail
[...p[6], ...p[7], ...p[8], randColor(this.hue), true ], //back wing
[...p[0], ...p[2], ...p[3], randColor(this.hue), false], //body 1
[...p[2], ...p[3], ...p[5], randColor(this.hue), false], //body 2
[...p[6], ...p[7], ...p[9], randColor(this.hue), true ], //front wing
]
}
render(){
push();
let p = this.pos;
translate(...p);
scale(this.size);
strokeWeight(1/this.size);
p[0] += cos(this.angle+PI)*this.speed*20;
p[1] += sin(this.angle+PI)*this.speed*20;
if (p[0] < -this.size ) p[0] += width +this.size;
if (p[1] < -this.size/2) p[1] += height+this.size/2;
let s = cos(frameCount*this.speed)
rotate(this.angle);
this.triangles.map(t => {
fill(...(t[6]))
if (t[7]) triangle(...t.slice(0, 5), t[5]*s);
else triangle(...t);
})
pop();
}
}
function randColor(base = 0, amt=.2){
return [(base+random(amt)-amt/2)%1, .2 + random(amt), .8 + random(amt)];
}
function setup (){
pixelDensity(1);
createCanvas();
colorMode(HSB, 1, 1, 1);
windowResized();
}
function init(){
birds = [];
for (let i = 0; i < numBirds; i++) birds.push(new Bird());
birds = birds.sort((a,b) => a.size - b.size);
}
function draw(){
background(0, .5);
birds.map(b => b.render());
}
function mousePressed(){windowResized();}
function windowResized(){
resizeCanvas(windowWidth, windowHeight);
init();
}
这期就到这里,下期见。
今天来分享一款js拖拽到边缘悬浮球代码,仿360加速球的,下面来放图:
下面我们来看代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>js拖拽到边缘悬浮球代码 </title>
<style>
* {
margin: 0;
padding: 0;
list-style: none;
}
html, body {
width: 100%;
height: 100%;
}
#neko {
width: 100px;
height: 100px;
background: #ddd;
position: fixed;
cursor: move;
box-sizing: border-box;
border: 4px solid #66cc66;
border-radius: 50%;
background: url('tp.png') no-repeat center center;
background-size: 100% 100%;
overflow: hidden;
}
</style>
</head>
<body>
<h3 style="padding: 20px">请将图片拖动到浏览器边缘,查看效果</h3>
<div id="neko"></div>
<script>
var neko = document.querySelector('#neko');
var nekoW = neko.offsetWidth;
var nekoH = neko.offsetHeight;
var cuntW = 0;
var cuntH = 0;
neko.style.left = parseInt(Math.random() * (document.body.offsetWidth - nekoW)) + 'px';
neko.style.top = parseInt(Math.random() * (document.body.offsetHeight - nekoH)) + 'px';
function move(obj, w, h) {
if(obj.direction === 'left') {
obj.style.left = 0 - w + 'px';
} else if(obj.direction === 'right') {
obj.style.left = document.body.offsetWidth - nekoW + w + 'px';
}
if(obj.direction === 'top') {
obj.style.top = 0 - h + 'px';
} else if(obj.direction === 'bottom') {
obj.style.top = document.body.offsetHeight - nekoH + h + 'px';
}
}
function rate(obj, a) {
// console.log(a);
obj.style.transform = ' rotate(' + a + ')'
}
function action(obj) {
var dir = obj.direction;
switch(dir) {
case 'left':
rate(obj, '90deg');
break;
case 'right':
rate(obj, '-90deg');
break;
case 'top':
rate(obj, '-180deg');
break;
default:
rate(obj, '-0');
break;
}
}
neko.onmousedown = function(e) {
var nekoL = e.clientX - neko.offsetLeft;
var nekoT = e.clientY - neko.offsetTop;
document.onmousemove = function(e) {
cuntW = 0;
cuntH = 0;
neko.direction = '';
neko.style.transition = '';
neko.style.left = (e.clientX - nekoL) + 'px';
neko.style.top = (e.clientY - nekoT) + 'px';
if(e.clientX - nekoL < 5) {
neko.direction = 'left';
}
if(e.clientY - nekoT < 5) {
neko.direction = 'top';
}
if(e.clientX - nekoL > document.body.offsetWidth - nekoW - 5) {
neko.direction = 'right';
}
if(e.clientY - nekoT > document.body.offsetHeight - nekoH - 5) {
neko.direction = 'bottom';
}
move(neko, 0, 0);
}
}
neko.onmouseover = function() {
move(this, 0, 0);
rate(this, 0)
}
neko.onmouseout = function() {
move(this, nekoW / 2, nekoH / 2);
action(this);
}
neko.onmouseup = function() {
document.onmousemove = null;
this.style.transition = '.5s';
move(this, nekoW / 2, nekoH / 2);
action(this);
}
window.onresize = function() {
var bodyH = document.body.offsetHeight;
var nekoT = neko.offsetTop;
var bodyW = document.body.offsetWidth;
var nekoL = neko.offsetLeft;
if(nekoT + nekoH > bodyH) {
neko.style.top = bodyH - nekoH + 'px';
cuntH++;
}
if(bodyH > nekoT && cuntH > 0) {
neko.style.top = bodyH - nekoH + 'px';
}
if(nekoL + nekoW > bodyW) {
neko.style.left = bodyW - nekoW + 'px';
cuntW++;
}
if(bodyW > nekoL && cuntW > 0) {
neko.style.left = bodyW - nekoW + 'px';
}
move(neko, nekoW / 2, nekoH / 2);
}
</script>
</body>
</html>
这个效果和图片本身没有什么关系,所以什么图都可以实现。那么这期就到这里,下期再见。
今天我们来分享一个地图效果:
点击那个省,哪个省就会放大。那么我们来看代码部分:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=EDGE">
<title>ECharts China Map</title>
<style>
#china-map {
width: 1000px;
height: 1000px;
margin: auto;
}
#box {
display: none;
background-color: goldenrod;
width: 180px;
height: 30px;
}
#box-title {
display: block;
}
</style>
<script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript" src="js/echarts.min.js"></script>
<script type="text/javascript" src="js/map/china.js"></script>
</head>
<body>
<button id="back">返回全国</button>
<div id="china-map"></div>
<script>
var myChart = echarts.init(document.getElementById('china-map'));
var oBack = document.getElementById("back");
var provinces = ['shanghai', 'hebei', 'shanxi', 'neimenggu', 'liaoning', 'jilin', 'heilongjiang', 'jiangsu', 'zhejiang', 'anhui', 'fujian', 'jiangxi', 'shandong', 'henan', 'hubei', 'hunan', 'guangdong', 'guangxi', 'hainan', 'sichuan', 'guizhou', 'yunnan', 'xizang', 'shanxi1', 'gansu', 'qinghai', 'ningxia', 'xinjiang', 'beijing', 'tianjin', 'chongqing', 'xianggang', 'aomen'];
var provincesText = ['上海', '河北', '山西', '内蒙古', '辽宁', '吉林', '黑龙江', '江苏', '浙江', '安徽', '福建', '江西', '山东', '河南', '湖北', '湖南', '广东', '广西', '海南', '四川', '贵州', '云南', '西藏', '陕西', '甘肃', '青海', '宁夏', '新疆', '北京', '天津', '重庆', '香港', '澳门'];
var seriesData = [{
name: '北京',
value: 100
}, {
name: '天津',
value: 0
}, {
name: '上海',
value: 60
}, {
name: '重庆',
value: 0
}, {
name: '河北',
value: 60
}, {
name: '河南',
value: 60
}, {
name: '云南',
value: 0
}, {
name: '辽宁',
value: 0
}, {
name: '黑龙江',
value: 0
}, {
name: '湖南',
value: 60
}, {
name: '安徽',
value: 0
}, {
name: '山东',
value: 60
}, {
name: '新疆',
value: 0
}, {
name: '江苏',
value: 0
}, {
name: '浙江',
value: 0
}, {
name: '江西',
value: 0
}, {
name: '湖北',
value: 60
}, {
name: '广西',
value: 60
}, {
name: '甘肃',
value: 0
}, {
name: '山西',
value: 60
}, {
name: '内蒙古',
value: 0
}, {
name: '陕西',
value: 0
}, {
name: '吉林',
value: 0
}, {
name: '福建',
value: 0
}, {
name: '贵州',
value: 0
}, {
name: '广东',
value: 597
}, {
name: '青海',
value: 0
}, {
name: '西藏',
value: 0
}, {
name: '四川',
value: 60
}, {
name: '宁夏',
value: 0
}, {
name: '海南',
value: 60
}, {
name: '台湾',
value: 0
}, {
name: '香港',
value: 0
}, {
name: '澳门',
value: 0
}];
oBack.onclick = function() {
initEcharts("china", "中国");
};
initEcharts("china", "中国");
// 初始化echarts
function initEcharts(pName, Chinese_) {
var tmpSeriesData = pName === "china" ? seriesData : [];
var option = {
title: {
text: Chinese_ || pName,
left: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{b}<br/>{c} (p / km2)'
},
series: [{
name: Chinese_ || pName,
type: 'map',
mapType: pName,
roam: false, //是否开启鼠标缩放和平移漫游
data: tmpSeriesData,
top: "3%", //组件距离容器的距离
zoom: 1.1,
selectedMode: 'single',
label: {
normal: {
show: true, //显示省份标签
textStyle: {
color: "#fbfdfe"
} //省份标签字体颜色
},
emphasis: { //对应的鼠标悬浮效果
show: true,
textStyle: {
color: "#323232"
}
}
},
itemStyle: {
normal: {
borderWidth: .5, //区域边框宽度
borderColor: '#0550c3', //区域边框颜色
areaColor: "#4ea397", //区域颜色
},
emphasis: {
borderWidth: .5,
borderColor: '#4b0082',
areaColor: "#ece39e",
}
},
}]
};
myChart.setOption(option);
myChart.off("click");
if(pName === "china") { // 全国时,添加click 进入省级
myChart.on('click', function(param) {
console.log(param.name);
// 遍历取到provincesText 中的下标 去拿到对应的省js
for(var i = 0; i < provincesText.length; i++) {
if(param.name === provincesText[i]) {
//显示对应省份的方法
showProvince(provinces[i], provincesText[i]);
break;
}
}
if(param.componentType === 'series') {
var provinceName = param.name;
$('#box').css('display', 'block');
$("#box-title").html(provinceName);
}
});
} else { // 省份,添加双击 回退到全国
myChart.on("dblclick", function() {
initEcharts("china", "中国");
});
}
}
// 展示对应的省
function showProvince(pName, Chinese_) {
//这写省份的js都是通过在线构建工具生成的,保存在本地,需要时加载使用即可,最好不要一开始全部直接引入。
loadBdScript('$' + pName + 'JS', './js/map/province/' + pName + '.js', function() {
initEcharts(Chinese_);
});
}
// 加载对应的JS
function loadBdScript(scriptId, url, callback) {
var script = document.createElement("script");
script.type = "text/javascript";
if(script.readyState) { //IE
script.onreadystatechange = function() {
if(script.readyState === "loaded" || script.readyState === "complete") {
script.onreadystatechange = null;
callback();
}
};
} else { // Others
script.onload = function() {
callback();
};
}
script.src = url;
script.id = scriptId;
document.getElementsByTagName("head")[0].appendChild(script);
};
</script>
</body>
</html>
第二个页面
<!DOCTYPE html>
<html style="height: 90%">
<head>
<meta charset="utf-8">
</head>
<body style="width: 60%;height: 100%;margin-left: calc(20% - 2px);padding: 10px;border: 1px solid #e3e3e3;-webkit-border-radius: 4px;-moz-border-radius: 4px;border-radius: 4px;-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);">
<div id="main" style="height: 100%"></div>
<script type="text/javascript" src="./js/echarts.min.js"></script>
<!-- 引用echarts 3.0版本 -->
<script type="text/javascript" src="./js/map/china.js"></script>
<script type="text/javascript">
//初始化ECharts实例
var myChart = echarts.init(document.getElementById('main'));
var option = {
title: {
text: '北京',
link: 'http://echarts.baidu.com/',
subtext: '各区、县地图'
},
tooltip: {
trigger: 'item',
formatter: function(params) {
var res = '';
res += params['data'].name + '</br>';
res += '用户数' + ' : ' + params['data'].value2 + '</br>';
return res;
}
//formatter: '{b}<br/>{c}'
}, //鼠标放在地图上时,显示的内容及样式控制
visualMap: {
show: false, //色系条是否显示
min: 0,
max: 45000, //取其区间值就代表色系inRange中的一种颜色
left: 'left',
top: 'bottom',
text: ['高', '低'], // 文本,默认为数值文本
calculable: true,
inRange: {
color: ['#42a8be', '#00a69c', '#95ea95'], //上色范围
}
}, //给地图上色
series: [{
name: '北京市',
type: 'map',
mapType: 'beijing',
selectedMode: 'single',
label: {
normal: {
show: true
},
emphasis: {
show: true
}
},
itemStyle: {
normal: {
borderColor: '#fff',
areaColor: '#fff',
borderWidth: 2,
}, //正常样式
emphasis: {
areaColor: 'red',
borderWidth: 1,
borderColor: 'yellow',
} //鼠标事件区块样式
},
data: [{
name: '怀柔区',
value: 40500,
value2: 1024
},
{
name: '延庆区',
value: 23000,
value2: 1024
},
{
name: '顺义区',
value: 22500,
value2: 1024
},
{
name: '通州区',
value: 40500,
value2: 1024
},
{
name: '朝阳区',
value: 45000,
value2: 1024
},
{
name: '昌平区',
value: 37000,
value2: 1024
},
{
name: '门头沟区',
value: 40500,
value2: 1024
},
{
name: '石景山区',
value: 0,
value2: 1024
},
{
name: '海淀区',
value: 11161,
value2: 1024
},
{
name: '丰台区',
value: 15000,
value2: 1024
},
{
name: '房山区',
value: 20000,
value2: 1024
},
{
name: '密云区',
value: 25000,
value2: 1024
},
{
name: '平谷区',
value: 30000,
value2: 1024
},
{
name: '西城区',
value: 35000,
value2: 1024
},
{
name: '东城区',
value: 36000,
value2: 1024
},
{
name: '大兴区',
value: 45000,
value2: 1024
},
], //value的值是上面visualMap属性中设置的颜色色系区间的值,即0~45000
label: {
normal: {
show: true,
formatter: function(val) {
var area_content = '{a|' + val.name + '}' + '-' + '{b|' + val.data.value2 + '}';
return area_content.split("-").join(" ");
}, //让series 中的文字进行换行
rich: {
a: {
color: 'black'
},
b: {
color: 'yellow',
fontFamily: 'Microsoft YaHei',
fontSize: 14,
}
}, //富文本样式,就是上面的formatter中'{a|'和'{b|'
},
emphasis: {
show: true
}
}, //地图中文字内容及样式控制
}]
};
myChart.setOption(option, true);
myChart.on('click', function(params) {
alert(1);
console.log(params); //此处写点击事件内容
}); //点击事件,此事件还可以用到柱状图等其他地图
</script>
</body>
</html>
这期就到这里,下期见
今天我们来分享一款js写的转盘抽奖程序:先看效果图
只能支持效果图。那么下面我们来分享代码部分:首先页面部分
<!DOCTYPE html>
<html >
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<link href="css/zzsc.css" rel="stylesheet" type="text/css">
<style>
.container {
margin: 0 auto;
}
.content {
position: absolute;
width: 100%;
height: auto;
margin: 0px auto;
margin-top: 58%;
}
</style>
</head>
<body>
<img src="images/game-bg.jpg" alt="" style="position: absolute;width: 100%;height: 100%;">
<div class="content" style="margin-top: 58%;">
<img src="images/8.png" id="fen-img" style="display:none;" />
<img src="images/6.png" id="zuliao-img" style="display:none;" />
<img src="images/3.png" id="baowen-img" style="display:none;" />
<img src="images/5.png" id="yundou-img" style="display:none;" />
<img src="images/7.png" id="ganlan-img" style="display:none;" />
<img src="images/4.png" id="yusan-img" style="display:none;" />
<div class="banner">
<div class="turnplate" style="background-image:url(images/turnplate-bg.png);background-size:100% 100%;">
<canvas class="item" id="wheelcanvas" width="422px" height="422px"></canvas>
<img class="pointer" src="images/turnplate-pointer.png">
</div>
</div>
</div>
<div id="sbtn">
<a href="#">
<img src="images/btn-rule.png" alt="">
</a>
<a href="#">
<img src="images/btn-zjcx.png" alt="">
</a>
</div>
<script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript" src="js/awardRotate.js"></script>
<script type="text/javascript">
var turnplate = {
restaraunts: [], //大转盘奖品名称
colors: [], //大转盘奖品区块对应背景颜色
outsideRadius: 192, //大转盘外圆的半径
textRadius: 155, //大转盘奖品位置距离圆心的距离
insideRadius: 68, //大转盘内圆的半径
startAngle: 0, //开始角度
bRotate: false //false:停止;ture:旋转
};
$(document).ready(function() {
//动态添加大转盘的奖品与奖品区域背景颜色
turnplate.restaraunts = ["20积分", "足疗机", "20积分", "保温杯", "20积分", "手持式电熨斗", "20积分 ", "橄榄油", "20积分", "晴雨伞"];
turnplate.colors = ["#FFF4D6", "#FFFFFF", "#FFF4D6", "#FFFFFF", "#FFF4D6", "#FFFFFF", "#FFF4D6", "#FFFFFF", "#FFF4D6", "#FFFFFF"];
var rotateTimeOut = function() {
$('#wheelcanvas').rotate({
angle: 0,
animateTo: 2160,
duration: 8000,
callback: function() {
alert('网络超时,请检查您的网络设置!');
}
});
};
//旋转转盘 item:奖品位置; txt:提示语;
var rotateFn = function(item, txt) {
var angles = item * (360 / turnplate.restaraunts.length) - (360 / (turnplate.restaraunts.length * 2));
if(angles < 270) {
angles = 270 - angles;
} else {
angles = 360 - angles + 270;
}
$('#wheelcanvas').stopRotate();
$('#wheelcanvas').rotate({
angle: 0,
animateTo: angles + 1800,
duration: 8000,
callback: function() {
//中奖提示
alert(txt);
turnplate.bRotate = !turnplate.bRotate;
}
});
};
$('.pointer').click(function() {
if(turnplate.bRotate) return;
turnplate.bRotate = !turnplate.bRotate;
//获取随机数(奖品个数范围内)
var item = rnd(1, turnplate.restaraunts.length);
//奖品数量等于10,指针落在对应奖品区域的中心角度[252, 216, 180, 144, 108, 72, 36, 360, 324, 288]
rotateFn(item, turnplate.restaraunts[item - 1]);
// switch (item) {
// case 1:
// rotateFn(252, turnplate.restaraunts[0]);
// break;
// case 2:
// rotateFn(216, turnplate.restaraunts[1]);
// break;
// case 3:
// rotateFn(180, turnplate.restaraunts[2]);
// break;
// case 4:
// rotateFn(144, turnplate.restaraunts[3]);
// break;
// case 5:
// rotateFn(108, turnplate.restaraunts[4]);
// break;
// case 6:
// rotateFn(72, turnplate.restaraunts[5]);
// break;
// case 7:
// rotateFn(36, turnplate.restaraunts[6]);
// break;
// case 8:
// rotateFn(360, turnplate.restaraunts[7]);
// break;
// case 9:
// rotateFn(324, turnplate.restaraunts[8]);
// break;
// case 10:
// rotateFn(288, turnplate.restaraunts[9]);
// break;
// }
console.log(item);
});
});
function rnd(n, m) {
n = 1; //最小随机数
m = 100; //最大随机数(概率范围最大值)
//最大数数不超过最大随机数
var ransluck = [50, 60, 65, 70, 75, 80, 85, 90, 95, 100]; //概率为比自己小的第一个数之间的差
var randoms = Math.floor(Math.random() * (m - n + 1) + n);
if(randoms <= ransluck[0]) {
var random = 1;
} else if(randoms <= ransluck[1]) {
var random = 2;
} else if(randoms <= ransluck[2]) {
var random = 3;
} else if(randoms <= ransluck[3]) {
var random = 4;
} else if(randoms <= ransluck[4]) {
var random = 5;
} else if(randoms <= ransluck[5]) {
var random = 6;
} else if(randoms <= ransluck[6]) {
var random = 7;
} else if(randoms <= ransluck[7]) {
var random = 8;
} else if(randoms <= ransluck[8]) {
var random = 9;
} else if(randoms <= ransluck[9]) {
var random = 10;
}
//alert(randoms);
//alert(random);
return random;
}
//页面所有元素加载完毕后执行drawRouletteWheel()方法对转盘进行渲染
window.onload = function() {
drawRouletteWheel();
};
function drawRouletteWheel() {
var canvas = document.getElementById("wheelcanvas");
if(canvas.getContext) {
//根据奖品个数计算圆周角度
var arc = Math.PI / (turnplate.restaraunts.length / 2);
var ctx = canvas.getContext("2d");
//在给定矩形内清空一个矩形
ctx.clearRect(0, 0, 422, 422);
//strokeStyle 属性设置或返回用于笔触的颜色、渐变或模式
ctx.strokeStyle = "#FFBE04";
//font 属性设置或返回画布上文本内容的当前字体属性
ctx.font = '16px Microsoft YaHei';
for(var i = 0; i < turnplate.restaraunts.length; i++) {
var angle = turnplate.startAngle + i * arc;
ctx.fillStyle = turnplate.colors[i];
ctx.beginPath();
//arc(x,y,r,起始角,结束角,绘制方向) 方法创建弧/曲线(用于创建圆或部分圆)
ctx.arc(211, 211, turnplate.outsideRadius, angle, angle + arc, false);
ctx.arc(211, 211, turnplate.insideRadius, angle + arc, angle, true);
ctx.stroke();
ctx.fill();
//锁画布(为了保存之前的画布状态)
ctx.save();
//----绘制奖品开始----
ctx.fillStyle = "#E5302F";
var text = turnplate.restaraunts[i];
var line_height = 17;
//translate方法重新映射画布上的 (0,0) 位置
ctx.translate(211 + Math.cos(angle + arc / 2) * turnplate.textRadius, 211 + Math.sin(angle + arc / 2) * turnplate.textRadius);
//rotate方法旋转当前的绘图
ctx.rotate(angle + arc / 2 + Math.PI / 2);
/** 下面代码根据奖品类型、奖品名称长度渲染不同效果,如字体、颜色、图片效果。(具体根据实际情况改变) **/
if(text.indexOf("M") > 0) { //流量包
var texts = text.split("M");
for(var j = 0; j < texts.length; j++) {
ctx.font = j == 0 ? 'bold 20px Microsoft YaHei' : '16px Microsoft YaHei';
if(j == 0) {
ctx.fillText(texts[j] + "M", -ctx.measureText(texts[j] + "M").width / 2, j * line_height);
} else {
ctx.fillText(texts[j], -ctx.measureText(texts[j]).width / 2, j * line_height);
}
}
} else if(text.indexOf("M") == -1 && text.length > 6) { //奖品名称长度超过一定范围
text = text.substring(0, 6) + "||" + text.substring(6);
var texts = text.split("||");
for(var j = 0; j < texts.length; j++) {
ctx.fillText(texts[j], -ctx.measureText(texts[j]).width / 2, j * line_height);
}
} else {
//在画布上绘制填色的文本。文本的默认颜色是黑色
//measureText()方法返回包含一个对象,该对象包含以像素计的指定字体宽度
ctx.fillText(text, -ctx.measureText(text).width / 2, 0);
}
//添加对应图标
if(text.indexOf("分") > 0) {
var img = document.getElementById("fen-img");
img.onload = function() {
ctx.drawImage(img, -15, 10);
};
ctx.drawImage(img, -15, 10);
} else if(text.indexOf("足疗") >= 0) {
var img = document.getElementById("zuliao-img");
img.onload = function() {
ctx.drawImage(img, -15, 10);
};
ctx.drawImage(img, -15, 10);
} else if(text.indexOf("保温") >= 0) {
var img = document.getElementById("baowen-img");
img.onload = function() {
ctx.drawImage(img, -15, 10);
};
ctx.drawImage(img, -15, 10);
} else if(text.indexOf("电熨斗") >= 0) {
var img = document.getElementById("yundou-img");
img.onload = function() {
ctx.drawImage(img, -15, 10);
};
ctx.drawImage(img, -15, 10);
} else if(text.indexOf("橄榄") >= 0) {
var img = document.getElementById("ganlan-img");
img.onload = function() {
ctx.drawImage(img, -15, 10);
};
ctx.drawImage(img, -15, 10);
} else if(text.indexOf("雨伞") >= 0) {
var img = document.getElementById("yusan-img");
img.onload = function() {
ctx.drawImage(img, -15, 10);
};
ctx.drawImage(img, -15, 10);
}
//把当前画布返回(调整)到上一个save()状态之前
ctx.restore();
//----绘制奖品结束----
}
}
}
</script>
</body>
</html>
然后样式部分:
body, ul, ol, li, p, h1, h2, h3, h4, h5, h6, form, fieldset, table, td, img, div {
margin: 0;
padding: 0;
border: 0;
}
/*body{color:#333; font-size:12px;font-family:"Microsoft YaHei"}*/
ul, ol {
list-style-type: none;
}
select, input, img, select {
vertical-align: middle;
}
input {
font-size: 12px;
}
.clear {
clear: both;
}
/* 大转盘样式 */
.banner {
display: block;
width: 79%;
margin-left: auto;
margin-right: auto;
margin-bottom: 20px;
}
.banner .turnplate {
display: block;
width: 100%;
position: relative;
}
.banner .turnplate canvas.item {
width: 100%;
}
.banner .turnplate img.pointer {
position: absolute;
width: 31.5%;
height: 42.5%;
left: 34.1%;
top: 23%;
}
#sbtn {
position: absolute;
top: 85%;
text-align: center;
width: 100%;
}
#sbtn img {
width: 24%;
margin: 0 20px;
}
然后插件脚本部分:
(function($) {
var supportedCSS,styles=document.getElementsByTagName("head")[0].style,toCheck="transformProperty WebkitTransform OTransform msTransform MozTransform".split(" ");
for (var a=0;a<toCheck.length;a++) if (styles[toCheck[a]] !== undefined) supportedCSS = toCheck[a];
var IE = eval('"v"=="v"');
jQuery.fn.extend({
rotate:function(parameters)
{
if (this.length===0||typeof parameters=="undefined") return;
if (typeof parameters=="number") parameters={angle:parameters};
var returned=[];
for (var i=0,i0=this.length;i<i0;i++)
{
var element=this.get(i);
if (!element.Wilq32 || !element.Wilq32.PhotoEffect) {
var paramClone = $.extend(true, {}, parameters);
var newRotObject = new Wilq32.PhotoEffect(element,paramClone)._rootObj;
returned.push($(newRotObject));
}
else {
element.Wilq32.PhotoEffect._handleRotation(parameters);
}
}
return returned;
},
getRotateAngle: function(){
var ret = [];
for (var i=0,i0=this.length;i<i0;i++)
{
var element=this.get(i);
if (element.Wilq32 && element.Wilq32.PhotoEffect) {
ret[i] = element.Wilq32.PhotoEffect._angle;
}
}
return ret;
},
stopRotate: function(){
for (var i=0,i0=this.length;i<i0;i++)
{
var element=this.get(i);
if (element.Wilq32 && element.Wilq32.PhotoEffect) {
clearTimeout(element.Wilq32.PhotoEffect._timer);
}
}
}
});
Wilq32=window.Wilq32||{};
Wilq32.PhotoEffect=(function(){
if (supportedCSS) {
return function(img,parameters){
img.Wilq32 = {
PhotoEffect: this
};
this._img = this._rootObj = this._eventObj = img;
this._handleRotation(parameters);
}
} else {
return function(img,parameters) {
this._img = img;
this._rootObj=document.createElement('span');
this._rootObj.style.display="inline-block";
this._rootObj.Wilq32 =
{
PhotoEffect: this
};
img.parentNode.insertBefore(this._rootObj,img);
if (img.complete) {
this._Loader(parameters);
} else {
var self=this;
// TODO: Remove jQuery dependency
jQuery(this._img).bind("load", function()
{
self._Loader(parameters);
});
}
}
}
})();
Wilq32.PhotoEffect.prototype={
_setupParameters : function (parameters){
this._parameters = this._parameters || {};
if (typeof this._angle !== "number") this._angle = 0 ;
if (typeof parameters.angle==="number") this._angle = parameters.angle;
this._parameters.animateTo = (typeof parameters.animateTo==="number") ? (parameters.animateTo) : (this._angle);
this._parameters.step = parameters.step || this._parameters.step || null;
this._parameters.easing = parameters.easing || this._parameters.easing || function (x, t, b, c, d) { return -c * ((t=t/d-1)*t*t*t - 1) + b; }
this._parameters.duration = parameters.duration || this._parameters.duration || 1000;
this._parameters.callback = parameters.callback || this._parameters.callback || function(){};
if (parameters.bind && parameters.bind != this._parameters.bind) this._BindEvents(parameters.bind);
},
_handleRotation : function(parameters){
this._setupParameters(parameters);
if (this._angle==this._parameters.animateTo) {
this._rotate(this._angle);
}
else {
this._animateStart();
}
},
_BindEvents:function(events){
if (events && this._eventObj)
{
// Unbinding previous Events
if (this._parameters.bind){
var oldEvents = this._parameters.bind;
for (var a in oldEvents) if (oldEvents.hasOwnProperty(a))
// TODO: Remove jQuery dependency
jQuery(this._eventObj).unbind(a,oldEvents[a]);
}
this._parameters.bind = events;
for (var a in events) if (events.hasOwnProperty(a))
// TODO: Remove jQuery dependency
jQuery(this._eventObj).bind(a,events[a]);
}
},
_Loader:(function()
{
if (IE)
return function(parameters)
{
var width=this._img.width;
var height=this._img.height;
this._img.parentNode.removeChild(this._img);
this._vimage = this.createVMLNode('image');
this._vimage.src=this._img.src;
this._vimage.style.height=height+"px";
this._vimage.style.width=width+"px";
this._vimage.style.position="absolute"; // FIXES IE PROBLEM - its only rendered if its on absolute position!
this._vimage.style.top = "0px";
this._vimage.style.left = "0px";
/* Group minifying a small 1px precision problem when rotating object */
this._container = this.createVMLNode('group');
this._container.style.width=width;
this._container.style.height=height;
this._container.style.position="absolute";
this._container.setAttribute('coordsize',width-1+','+(height-1)); // This -1, -1 trying to fix ugly problem with small displacement on IE
this._container.appendChild(this._vimage);
this._rootObj.appendChild(this._container);
this._rootObj.style.position="relative"; // FIXES IE PROBLEM
this._rootObj.style.width=width+"px";
this._rootObj.style.height=height+"px";
this._rootObj.setAttribute('id',this._img.getAttribute('id'));
this._rootObj.className=this._img.className;
this._eventObj = this._rootObj;
this._handleRotation(parameters);
}
else
return function (parameters)
{
this._rootObj.setAttribute('id',this._img.getAttribute('id'));
this._rootObj.className=this._img.className;
this._width=this._img.width;
this._height=this._img.height;
this._widthHalf=this._width/2; // used for optimisation
this._heightHalf=this._height/2;// used for optimisation
var _widthMax=Math.sqrt((this._height)*(this._height) + (this._width) * (this._width));
this._widthAdd = _widthMax - this._width;
this._heightAdd = _widthMax - this._height; // widthMax because maxWidth=maxHeight
this._widthAddHalf=this._widthAdd/2; // used for optimisation
this._heightAddHalf=this._heightAdd/2;// used for optimisation
this._img.parentNode.removeChild(this._img);
this._aspectW = ((parseInt(this._img.style.width,10)) || this._width)/this._img.width;
this._aspectH = ((parseInt(this._img.style.height,10)) || this._height)/this._img.height;
this._canvas=document.createElement('canvas');
this._canvas.setAttribute('width',this._width);
this._canvas.style.position="relative";
this._canvas.style.left = -this._widthAddHalf + "px";
this._canvas.style.top = -this._heightAddHalf + "px";
this._canvas.Wilq32 = this._rootObj.Wilq32;
this._rootObj.appendChild(this._canvas);
this._rootObj.style.width=this._width+"px";
this._rootObj.style.height=this._height+"px";
this._eventObj = this._canvas;
this._cnv=this._canvas.getContext('2d');
this._handleRotation(parameters);
}
})(),
_animateStart:function()
{
if (this._timer) {
clearTimeout(this._timer);
}
this._animateStartTime = +new Date;
this._animateStartAngle = this._angle;
this._animate();
},
_animate:function()
{
var actualTime = +new Date;
var checkEnd = actualTime - this._animateStartTime > this._parameters.duration;
// TODO: Bug for animatedGif for static rotation ? (to test)
if (checkEnd && !this._parameters.animatedGif)
{
clearTimeout(this._timer);
}
else
{
if (this._canvas||this._vimage||this._img) {
var angle = this._parameters.easing(0, actualTime - this._animateStartTime, this._animateStartAngle, this._parameters.animateTo - this._animateStartAngle, this._parameters.duration);
this._rotate((~~(angle*10))/10);
}
if (this._parameters.step) {
this._parameters.step(this._angle);
}
var self = this;
this._timer = setTimeout(function()
{
self._animate.call(self);
}, 10);
}
// To fix Bug that prevents using recursive function in callback I moved this function to back
if (this._parameters.callback && checkEnd){
this._angle = this._parameters.animateTo;
this._rotate(this._angle);
this._parameters.callback.call(this._rootObj);
}
},
_rotate : (function()
{
var rad = Math.PI/180;
if (IE)
return function(angle)
{
this._angle = angle;
this._container.style.rotation=(angle%360)+"deg";
}
else if (supportedCSS)
return function(angle){
this._angle = angle;
this._img.style[supportedCSS]="rotate("+(angle%360)+"deg)";
}
else
return function(angle)
{
this._angle = angle;
angle=(angle%360)* rad;
// clear canvas
this._canvas.width = this._width+this._widthAdd;
this._canvas.height = this._height+this._heightAdd;
// REMEMBER: all drawings are read from backwards.. so first function is translate, then rotate, then translate, translate..
this._cnv.translate(this._widthAddHalf,this._heightAddHalf); // at least center image on screen
this._cnv.translate(this._widthHalf,this._heightHalf); // we move image back to its orginal
this._cnv.rotate(angle); // rotate image
this._cnv.translate(-this._widthHalf,-this._heightHalf); // move image to its center, so we can rotate around its center
this._cnv.scale(this._aspectW,this._aspectH); // SCALE - if needed ;)
this._cnv.drawImage(this._img, 0, 0); // First - we draw image
}
})()
}
if (IE)
{
Wilq32.PhotoEffect.prototype.createVMLNode=(function(){
document.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)");
try {
!document.namespaces.rvml && document.namespaces.add("rvml", "urn:schemas-microsoft-com:vml");
return function (tagName) {
return document.createElement('<rvml:' + tagName + '>');
};
} catch (e) {
return function (tagName) {
return document.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml">');
};
}
})();
}
})(jQuery);
今天我们来分享是一款用jquery实现的手机解锁界面,非常好看,下面我们先看图
我们先来看html部分代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>jQuery手机屏幕手势解锁代码 </title>
<!--字体-->
<!--动画库-->
<link rel='stylesheet' href='css/animate.min.css'>
<!--图标库-->
<link rel='stylesheet' href='https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css'>
<!--解锁-->
<link rel='stylesheet' href='css/pattern-lock.min.css'>
<!--页面布局-->
<link rel="stylesheet" href="css/jiesuo.css">
</head>
<body>
<div class="mhn-ui-wrap">
<div class="mhn-ui-page page-lock">
<div class="mhn-ui-date-time">
<div class="mhn-ui-time">6:02 PM</div>
<div class="mhn-ui-day">星期三</div>
<div class="mhn-ui-date">10月 10, 2019</div>
</div>
<div class="mhn-lock-wrap">
<div class="mhn-lock-title" data-title="绘制图案解锁"></div>
<div class="mhn-lock"></div>
</div>
</div>
<div class="mhn-ui-page page-home">
<div class="mhn-ui-app-time"> </div>
<div class="mhn-ui-app-title-head">
<span class="mhn-ui-page-title">All Application</span>
<div class="mhn-ui-filter">
<span class="mhn-ui-btn ion-funnel"></span>
<div class="mhn-ui-filter-list">
<div data-filter="all" class="active">All Application</div>
<div data-filter="general">General Application</div>
<div data-filter="social">Social Application</div>
<div data-filter="credits">Credits Application</div>
</div>
</div>
</div>
<div class="mhn-ui-row mhn-ui-apps">
<div class="mhn-ui-col" data-filter="general">
<div class="mhn-ui-icon" data-open="page-author">
<span class="ion-person" data-color="#2980b9"></span>
<div class="mhn-ui-icon-title">Author</div>
</div>
</div>
<div class="mhn-ui-col" data-filter="general">
<div class="mhn-ui-icon" data-open="page-contact">
<span class="ion-chatbox" data-color="#8e44ad"></span>
<div class="mhn-ui-icon-title">Contact</div>
</div>
</div>
<div class="mhn-ui-col" data-filter="general">
<div class="mhn-ui-icon" data-href="#">
<span class="ion-ios-briefcase" data-color="#f39c12"></span>
<div class="mhn-ui-icon-title">Portfolio</div>
</div>
</div>
<div class="mhn-ui-col" data-filter="general">
<div class="mhn-ui-icon" data-open="page-credits">
<span class="ion-information-circled" data-color="#16a085"></span>
<div class="mhn-ui-icon-title">Credits</div>
</div>
</div>
<div class="mhn-ui-col" data-filter="social">
<div class="mhn-ui-icon" data-href="#">
<span class="ion-social-facebook" data-color="#3b5998"></span>
<div class="mhn-ui-icon-title">Facebook</div>
</div>
</div>
<div class="mhn-ui-col" data-filter="social">
<div class="mhn-ui-icon" data-href="#">
<span class="ion-social-twitter" data-color="#56a3d9"></span>
<div class="mhn-ui-icon-title">Twitter</div>
</div>
</div>
<div class="mhn-ui-col" data-filter="credits">
<div class="mhn-ui-icon" data-href="#">
<span class="ion-social-javascript" data-color="#6639b6"></span>
<div class="mhn-ui-icon-title">jQuery</div>
</div>
</div>
<div class="mhn-ui-col" data-filter="credits">
<div class="mhn-ui-icon" data-href="#">
<span class="ion-ionic" data-color="#3e50b4"></span>
<div class="mhn-ui-icon-title">Ionicons</div>
</div>
</div>
<div class="mhn-ui-col" data-filter="credits">
<div class="mhn-ui-icon" data-href="#">
<span class="ion-social-css3-outline" data-color="#785447"></span>
<div class="mhn-ui-icon-title">Animate</div>
</div>
</div>
<div class="mhn-ui-col" data-filter="credits">
<div class="mhn-ui-icon" data-href="#">
<span class="ion-android-camera" data-color="#000000"></span>
<div class="mhn-ui-icon-title">Unsplash</div>
</div>
</div>
<div class="mhn-ui-col" data-filter="credits">
<div class="mhn-ui-icon" data-href="#">
<span class="ion-android-unlock" data-color="#4bae4f"></span>
<div class="mhn-ui-icon-title">patternLock</div>
</div>
</div>
</div>
<div class="mhn-ui-bottom-link-bar">
<span class="mhn-ui-bottom-btn ion-ios-home"></span>
<span class="mhn-ui-bottom-btn ion-ios-locked" onClick="mhnUI.page.show('page-lock')"></span>
</div>
</div>
<div class="mhn-ui-page page-author">
<div class="mhn-ui-app-time"></div>
<div class="mhn-ui-app-title-head">
<span class="ion-person"></span> Author</div>
<div class="text-center">
<img class="flipInX animated" src="img/t.png">
</div>
<p class="text-center">Hi, It's me Mohan. I'm a web and graphics designer. Designing is my passion and I have been working on various designing projects.</p>
<div class="mhn-ui-bottom-link-bar">
<span class="mhn-ui-bottom-btn ion-ios-home" onClick="mhnUI.page.show('page-home')"></span>
<span class="mhn-ui-bottom-btn ion-ios-locked" onClick="mhnUI.page.show('page-lock')"></span>
</div>
</div>
<div class="mhn-ui-page page-contact">
<div class="mhn-ui-app-time"> </div>
<div class="mhn-ui-app-title-head">
<span class="ion-chatbox"></span> Contact</div>
<div class="mhn-ui-bottom-link-bar">
<span class="mhn-ui-bottom-btn ion-ios-home" onClick="mhnUI.page.show('page-home')"></span>
<span class="mhn-ui-bottom-btn ion-ios-locked" onClick="mhnUI.page.show('page-lock')"></span>
</div>
</div>
<div class="mhn-ui-page page-credits">
<div class="mhn-ui-app-time"> </div>
<div class="mhn-ui-app-title-head">
<span class="ion-information-circled"></span> Credits</div>
<div class="mhn-ui-credit-list">
<div class="mhn-ui-credit">
<p>jQuery is a fast, small, and feature-rich JavaScript library.</p>
</div>
</div>
<div class="mhn-ui-bottom-link-bar">
<span class="mhn-ui-bottom-btn ion-ios-home" onClick="mhnUI.page.show('page-home')"></span>
<span class="mhn-ui-bottom-btn ion-ios-locked" onClick="mhnUI.page.show('page-lock')"></span>
</div>
</div>
<div class="mhn-ui-dialog-wrap">
<div class="mhn-ui-dialog">
<div class="mhn-ui-dialog-title">Are you sure?</div>
<p>This application wants to open an external link. To confirm, please click on yes button.</p>
<a data-action="confirm" class="mhn-ui-dialog-btn">Yes</a>
<a data-action="cancel" class="mhn-ui-dialog-btn">No</a>
</div>
</div>
</div>
<div class="mhn-ui-info">绘制'Z'形状开始解锁:)</div>
<script src="js/jquery.min.js"></script>
<script src="js/jiesuo.js"></script>
</body>
</html>>
接下来放出css部分的代码
.patt-holder {
background: #3382c0;
-ms-touch-action: none
}
.patt-wrap {
position: relative;
cursor: pointer
}
.patt-wrap li, .patt-wrap ul {
list-style: none;
margin: 0;
padding: 0
}
.patt-circ {
position: relative;
float: left;
box-sizing: border-box;
-moz-box-sizing: border-box
}
.patt-circ.hovered {
border: 3px solid #090
}
.patt-error .patt-circ.hovered {
border: 3px solid #BA1B26
}
.patt-hidden .patt-circ.hovered {
border: 0
}
.patt-dots, .patt-lines {
border-radius: 5px;
height: 10px;
position: absolute
}
.patt-dots {
background: #FFF;
width: 10px;
top: 50%;
left: 50%;
margin-top: -5px;
margin-left: -5px
}
.patt-lines {
background: rgba(255, 255, 255, .7);
transform-origin: 5px 5px;
-ms-transform-origin: 5px 5px;
-webkit-transform-origin: 5px 5px
}
.patt-hidden .patt-lines {
display: none
}
.mhn-ui-date-time, .text-center {
text-align: center
}
*, :after, :before {
box-sizing: border-box
}
.pull-left {
float: left
}
.pull-right {
float: right
}
.clearfix:after, .clearfix:before {
content: '';
display: table
}
.clearfix:after {
clear: both;
display: block
}
body {
margin: 0;
color: #fff;
background-color: #FD6969;
font: 300 14px/18px Roboto, sans-serif
}
a {
color: inherit;
text-decoration: none
}
a:hover {
text-decoration: underline
}
.mhn-ui-wrap {
width: 300px;
height: 475px;
overflow: hidden;
position: relative;
margin: 30px auto 0;
background: url(../img/bg.png) center no-repeat #2c3e50;
box-shadow: 0 17px 50px 0 rgba(0, 0, 0, .19), 0 12px 15px 0 rgba(0, 0, 0, .24)
}
.mhn-ui-wrap:before {
top: 0;
left: 0;
right: 0;
bottom: 0;
content: '';
position: absolute;
background: rgba(0, 0, 0, .4)
}
.mhn-ui-date-time {
color: #eee;
z-index: 100;
position: relative
}
.mhn-ui-date-time .mhn-ui-time {
font-size: 28px;
font-weight: 400;
margin-bottom: 15px
}
.mhn-ui-date-time .mhn-ui-day {
font-size: 24px;
margin-bottom: 10px
}
.mhn-ui-date-time .mhn-ui-date {
font-size: 18px;
font-weight: 400
}
.mhn-ui-app-time {
padding: 0 5px;
font-size: 12px;
text-align: right;
margin: -15px -15px auto;
background: rgba(0, 0, 0, .6)
}
.mhn-lock-wrap {
left: 0;
right: 0;
bottom: 0;
z-index: 100;
position: absolute
}
.mhn-lock-wrap .mhn-lock-title {
text-align: center;
text-shadow: 0 1px 1px rgba(0, 0, 0, .5)
}
.mhn-lock-wrap .mhn-lock-success {
color: transparent;
text-shadow: none
}
.mhn-lock-wrap .mhn-lock-failure {
color: #f34235
}
.mhn-lock {
margin: auto;
background: 0 0
}
.patt-wrap {
margin: auto;
overflow: hidden
}
.patt-wrap li {
transition: all .4s ease-in-out 0s
}
.patt-dots, .patt-lines {
transition: background .1s ease-in-out 0s
}
.patt-circ {
border: 3px solid transparent
}
.patt-dots {
background: rgba(255, 255, 255, .8)
}
.patt-lines {
background: rgba(255, 255, 255, .4)
}
.patt-circ.hovered {
border-color: #ddd;
background: rgba(255, 255, 255, .2)
}
.patt-error .patt-circ.hovered {
background: rgba(243, 66, 53, .4);
border-color: rgba(243, 66, 53, .8)
}
.patt-error .patt-lines {
background: rgba(243, 66, 53, .5)
}
.patt-success .patt-circ.hovered {
background: rgba(75, 174, 79, .4);
border-color: rgba(75, 174, 79, .8)
}
.patt-success .patt-lines {
background: rgba(75, 174, 79, .5)
}
.mhn-ui-page {
height: 100%;
z-index: 200;
display: none;
padding: 15px;
position: relative
}
.mhn-ui-page.page-lock {
position: initial
}
.mhn-ui-page .mhn-ui-app-title-head {
padding: 15px;
font-size: 16px;
margin: 0 -15px;
background: rgba(0, 0, 0, .4)
}
.mhn-ui-page .mhn-ui-filter {
float: right;
position: relative
}
.mhn-ui-page .mhn-ui-filter .mhn-ui-btn {
right: 0;
top: -5px;
padding: 5px;
cursor: pointer;
position: absolute;
display: inline-block
}
.mhn-ui-page .mhn-ui-filter .mhn-ui-btn.active {
background: teal
}
.mhn-ui-page .mhn-ui-filter-list {
right: 0;
top: 20px;
padding: 5px;
width: 180px;
display: none;
position: absolute;
background: rgba(0, 0, 0, .8)
}
.mhn-ui-page .mhn-ui-filter-list>div {
display: block;
font-size: 14px;
cursor: pointer;
padding: 2px 4px
}
.mhn-ui-page .mhn-ui-filter-list>div.active {
color: teal
}
.mhn-ui-page .mhn-ui-filter-list>div:hover {
background: teal
}
.mhn-ui-page .mhn-ui-filter-list>div.active:hover {
background: 0 0
}
.mhn-ui-page .mhn-ui-row {
margin-top: 15px
}
.mhn-ui-page .mhn-ui-row:after, .mhn-ui-page .mhn-ui-row:before {
content: '';
display: table
}
.mhn-ui-page .mhn-ui-row:after {
clear: both;
display: block
}
.mhn-ui-page .mhn-ui-col {
width: 25%;
float: left;
margin-bottom: 15px
}
.mhn-ui-bottom-link-bar {
left: 0;
right: 0;
bottom: 0;
padding: 15px;
position: absolute;
text-align: center
}
.mhn-ui-bottom-link-bar .mhn-ui-bottom-btn {
width: 40px;
height: 40px;
cursor: pointer;
font-size: 28px;
line-height: 40px;
text-align: center;
border-radius: 50%;
display: inline-block
}
.mhn-ui-bottom-link-bar .mhn-ui-bottom-btn:nth-child(1) {
margin-right: 15px
}
.mhn-ui-bottom-link-bar .mhn-ui-bottom-btn:nth-child(2) {
margin-left: 15px
}
.mhn-ui-bottom-link-bar .mhn-ui-bottom-btn:hover {
color: #ccc;
background: rgba(0, 0, 0, .8)
}
.mhn-ui-icon {
text-align: center
}
.mhn-ui-icon span {
width: 55px;
height: 55px;
margin: auto;
display: block;
font-size: 28px;
cursor: pointer;
line-height: 55px;
text-align: center;
border-radius: 15px;
background: rgba(0, 0, 0, .3);
transition: background .4s ease-in-out 0s;
box-shadow: 0 -1px 0 rgba(255, 255, 255, .5) inset
}
.mhn-ui-icon .mhn-ui-icon-title {
margin-top: 5px;
cursor: default;
overflow: hidden;
font-size: 13px;
text-overflow: ellipsis;
text-shadow: 0 1px 1px rgba(0, 0, 0, .5)
}
.mhn-ui-page.page-author img {
padding: 8px;
margin-top: 15px;
border-radius: 50%;
background: rgba(255, 255, 255, .7)
}
.mhn-ui-credit {
padding: 5px;
font-size: 13px;
margin-top: 15px;
background: rgba(0, 0, 0, .2);
border: 1px solid rgba(0, 0, 0, .2)
}
.mhn-ui-credit p {
margin: 0;
color: #aaa
}
.mhn-ui-credit a {
font-weight: 500
}
.mhn-ui-dialog-wrap {
top: 0;
left: 0;
right: 0;
bottom: 0;
display: none;
z-index: 1000;
position: absolute;
background: rgba(0, 0, 0, .7)
}
.mhn-ui-dialog {
padding: 15px;
background: #000;
margin: 50% 0 auto
}
.mhn-ui-dialog .mhn-ui-dialog-title {
font-size: 18px;
font-weight: 500
}
.mhn-ui-dialog .mhn-ui-dialog-btn {
padding: 5px;
min-width: 65px;
cursor: pointer;
margin-right: 10px;
text-align: center;
display: inline-block;
border: 2px solid rgba(255, 255, 255, .8)
}
.mhn-ui-dialog .mhn-ui-dialog-btn:hover {
background: #009688;
text-decoration: none
}
.mhn-ui-info {
margin: 30px 0;
font-size: 16px;
text-align: center
}
.mhn-ui-date, .mhn-ui-time {
-webkit-animation: zoomIn 1s;
animation: zoomIn 1s
}
.mhn-ui-day {
-webkit-animation: rubberBand 1s;
animation: rubberBand 1s
}
.mhn-lock-failure {
-webkit-animation: zoomIn .4s;
animation: zoomIn .4s
}
.patt-circ:nth-child(1), .patt-circ:nth-child(2), .patt-circ:nth-child(3) {
-webkit-animation: fadeInUp .4s;
animation: fadeInUp .4s
}
.patt-circ:nth-child(4), .patt-circ:nth-child(5), .patt-circ:nth-child(6) {
-webkit-animation: fadeInUp .6s;
animation: fadeInUp .6s
}
.patt-circ:nth-child(7), .patt-circ:nth-child(8), .patt-circ:nth-child(9) {
-webkit-animation: fadeInUp .8s;
animation: fadeInUp .8s
}
.mhn-ui-icon span {
-webkit-animation: zoomIn .6s;
animation: zoomIn .6s
}
.mhn-ui-bottom-btn {
-webkit-animation: bounceInUp .8s;
animation: bounceInUp .8s
}
.mhn-ui-credit-list .mhn-ui-credit:nth-child(1) {
-webkit-animation: fadeInUp .4s;
animation: fadeInUp .4s
}
.mhn-ui-credit-list .mhn-ui-credit:nth-child(2) {
-webkit-animation: fadeInUp .5s;
animation: fadeInUp .5s
}
.mhn-ui-credit-list .mhn-ui-credit:nth-child(3) {
-webkit-animation: fadeInUp .6s;
animation: fadeInUp .6s
}
.mhn-ui-credit-list .mhn-ui-credit:nth-child(4) {
-webkit-animation: fadeInUp .7s;
animation: fadeInUp .7s
}
.mhn-ui-credit-list .mhn-ui-credit:nth-child(5) {
-webkit-animation: fadeInUp .8s;
animation: fadeInUp .8s
}
最后脚本部分代码
$(function() {
mhnUI.setup();
});
mhnUI = {
pattern: "",
setup: function() {
this.lock(), this.filter(), this.colors(), this.links.setup(), this.dialog.setup(), setInterval("mhnUI.datetime()", 1e3)
},
lock: function() {
mhnUI.page.hide(), pattern = new PatternLock(".mhn-lock", {
margin: 15
}), $(".mhn-lock-title").html($(".mhn-lock-title").data("title")), pattern.checkForPattern("1235789", function() {
$(".mhn-lock-title").html('<span>Yes! you unlocked pattern</span>'), $(".patt-holder").addClass("patt-success"), setTimeout(function() {
pattern.reset(), mhnUI.message()
}, 1e3), mhnUI.page.show()
}, function() {
$(".mhn-lock-title").html('<span>Opps! pattern is not correct</span>'), $(".patt-holder").removeClass("patt-success"), setTimeout(function() {
pattern.reset(), mhnUI.message()
}, 2e3)
})
},
message: function() {
$(".mhn-lock-title span").fadeOut(), setTimeout(function() {
$(".mhn-lock-title").html($(".mhn-lock-title").data("title")), $(".mhn-lock-title span").fadeIn()
}, 500)
},
datetime: function() {
var t = new Array("星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"),
e = new Array("一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"),
n = new Date,
i = n.getYear();
1e3 > i && (i += 1900);
var a = n.getDay(),
o = n.getMonth(),
s = n.getDate();
10 > s && (s = "0" + s);
var h = n.getHours(),
c = n.getMinutes(),
u = n.getSeconds(),
l = "AM";
h >= 12 && (l = "PM"), h > 12 && (h -= 12), 0 == h && (h = 12), 9 >= c && (c = "0" + c), 9 >= u && (u = "0" + u), $(".mhn-ui-date-time .mhn-ui-day").text(t[a]), $(".mhn-ui-date-time .mhn-ui-date").text(e[o] + " " + s + ", " + i), $(".mhn-ui-date-time .mhn-ui-time").text(h + ":" + c + " " + l), $(".mhn-ui-app-time").text(h + ":" + c + ":" + u + " " + l)
},
page: {
show: function(t) {
t = t ? t : "page-home", $(".mhn-ui-page").hide(), $(".mhn-ui-page." + t).show()
},
hide: function(t) {
t = t ? t : "page-lock", $(".mhn-ui-page").hide(), $(".mhn-ui-page." + t).show()
}
},
filter: function() {
$(".mhn-ui-filter .mhn-ui-btn").click(function() {
$(this).toggleClass("active"), $(".mhn-ui-filter-list").toggle(100)
}), $(".mhn-ui-filter-list>div").click(function() {
var t = $(this).data("filter");
$(this).siblings().removeAttr("class"), $(this).addClass("active");
var e = function(t) {
$(".mhn-ui-apps .mhn-ui-col").fadeOut(400), $('.mhn-ui-apps .mhn-ui-col[data-filter="' + t + '"]').fadeIn(400)
};
switch(t) {
case "all":
$(".mhn-ui-apps .mhn-ui-col").fadeIn(400);
break;
case "general":
e(t);
break;
case "social":
e(t);
break;
case "credits":
e(t)
}
$(".mhn-ui-filter-list").toggle(100), $(".mhn-ui-filter .mhn-ui-btn").removeClass("active"), $(".mhn-ui-page-title").text($(this).text())
})
},
colors: function() {
$(".mhn-ui-icon span").on("mouseover", function() {
$(this).css("background", $(this).data("color"))
}).on("mouseout", function() {
$(this).removeAttr("style")
})
},
links: {
setup: function() {
$(".mhn-ui-apps .mhn-ui-icon").click(function() {
var t = $(this).data("href"),
e = $(this).data("open");
t && mhnUI.links.href(t), e && mhnUI.page.show(e)
})
},
href: function(t) {
mhnUI.dialog.show(t)
}
},
dialog: {
setup: function() {
$('.mhn-ui-dialog-wrap,.mhn-ui-dialog-wrap a[data-action="cancel"]').click(function() {
mhnUI.dialog.hide()
}), $(".mhn-ui-dialog").click(function(t) {
t.stopPropagation()
}), $('.mhn-ui-dialog a[data-action="confirm"]').click(function() {
setTimeout(function() {
mhnUI.dialog.hide()
}, 400)
})
},
show: function(t) {
$('.mhn-ui-dialog-wrap a[data-action="confirm"]').attr("href", t), $(".mhn-ui-dialog-wrap").show()
},
hide: function() {
$('.mhn-ui-dialog-wrap a[data-action="confirm"]').removeAttr("href"), $(".mhn-ui-dialog-wrap").fadeOut(400)
}
}
};
/*
patternLock.js v 0.5.0
*/
! function(t, e, n, a) {
"use strict";
function r(t) {
for(var e = t.holder, n = t.option, a = n.matrix, r = n.margin, i = n.radius, o = ['<ul style="padding:' + r + 'px">'], s = 0, l = a[0] * a[1]; l > s; s++) o.push('<li style="margin:' + r + "px; width : " + 2 * i + "px; height : " + 2 * i + "px; -webkit-border-radius: " + i + "px; -moz-border-radius: " + i + "px; border-radius: " + i + 'px; "><div></div></li>');
o.push("</ul>"), e.html(o.join("")).css({
width: a[1] * (2 * i + 2 * r) + 2 * r + "px",
height: a[0] * (2 * i + 2 * r) + 2 * r + "px"
}), t.pattCircle = t.holder.find(".patt-circ")
}
function i(t, e, n, a) {
var r = e - t,
i = a - n;
return {
length: Math.ceil(Math.sqrt(r * r + i * i)),
angle: Math.round(180 * Math.atan2(i, r) / Math.PI)
}
}
function o() {}
function s(e, n) {
var a = this,
i = a.token = Math.random(),
h = p[i] = new o,
u = h.holder = t(e);
if(0 != u.length) {
h.object = a, n = h.option = t.extend({}, s.defaults, n), r(h), u.addClass("patt-holder"), "static" == u.css("position") && u.css("position", "relative"), u.on("touchstart mousedown", function(t) {
d.call(this, t, a)
}), h.option.onDraw = n.onDraw || l;
var c = n.mapper;
h.mapperFunc = "object" == typeof c ? function(t) {
return c[t]
} : "function" == typeof c ? c : l, h.option.mapper = null
}
}
var l = function() {},
p = {},
d = function(e, a) {
e.preventDefault();
var r = p[a.token];
if(!r.disabled) {
r.option.patternVisible || r.holder.addClass("patt-hidden");
var i = "touchstart" == e.type ? "touchmove" : "mousemove",
o = "touchstart" == e.type ? "touchend" : "mouseup";
t(this).on(i + ".pattern-move", function(t) {
h.call(this, t, a)
}), t(n).one(o, function() {
u.call(this, e, a)
});
var s = r.holder.find(".patt-wrap"),
l = s.offset();
r.wrapTop = l.top, r.wrapLeft = l.left, a.reset()
}
},
h = function(e, n) {
e.preventDefault();
var a = e.pageX || e.originalEvent.touches[0].pageX,
r = e.pageY || e.originalEvent.touches[0].pageY,
o = p[n.token],
s = o.pattCircle,
l = o.patternAry,
d = o.option.lineOnMove,
h = o.getIdxFromPoint(a, r),
u = h.idx,
c = o.mapperFunc(u) || u;
if(l.length > 0) {
var f = i(o.lineX1, h.x, o.lineY1, h.y);
o.line.css({
width: f.length + 10 + "px",
transform: "rotate(" + f.angle + "deg)"
})
}
if(u) {
if(-1 == l.indexOf(c)) {
var v, m = t(s[u - 1]);
if(o.lastPosObj) {
for(var g = o.lastPosObj, x = g.i, w = g.j, b = Math.abs(h.i - x), j = Math.abs(h.j - w);
(0 == b && j > 1 || 0 == j && b > 1 || j == b && j > 1) && (w != h.j || x != h.i);) {
x = b ? Math.min(h.i, x) + 1 : x, w = j ? Math.min(h.j, w) + 1 : w, b = Math.abs(h.i - x), j = Math.abs(h.j - w);
var M = (w - 1) * o.option.matrix[1] + x,
y = o.mapperFunc(M) || M; - 1 == l.indexOf(y) && (t(s[M - 1]).addClass("hovered"), l.push(y))
}
v = [], h.j - g.j > 0 ? v.push("s") : h.j - g.j < 0 ? v.push("n") : 0, h.i - g.i > 0 ? v.push("e") : h.i - g.i < 0 ? v.push("w") : 0, v = v.join("-")
}
m.addClass("hovered"), l.push(c);
var P = o.option.margin,
k = o.option.radius,
C = (h.i - 1) * (2 * P + 2 * k) + 2 * P + k,
O = (h.j - 1) * (2 * P + 2 * k) + 2 * P + k;
if(1 != l.length) {
var D = i(o.lineX1, C, o.lineY1, O);
o.line.css({
width: D.length + 10 + "px",
transform: "rotate(" + D.angle + "deg)"
}), d || o.line.show()
}
v && (o.lastElm.addClass(v + " dir"), o.line.addClass(v + " dir"));
var E = t('<div style="top:' + (O - 5) + "px; left:" + (C - 5) + 'px"></div>');
o.line = E, o.lineX1 = C, o.lineY1 = O, o.holder.append(E), d || o.line.hide(), o.lastElm = m
}
o.lastPosObj = h
}
},
u = function(t, e) {
t.preventDefault();
var n = p[e.token],
a = n.patternAry.join("");
n.holder.off(".pattern-move").removeClass("patt-hidden"), a && (n.option.onDraw(a), n.line.remove(), n.rightPattern && (a == n.rightPattern ? n.onSuccess() : (n.onError(), e.error())))
};
o.prototype = {
constructor: o,
getIdxFromPoint: function(t, e) {
var n = this.option,
a = n.matrix,
r = t - this.wrapLeft,
i = e - this.wrapTop,
o = null,
s = n.margin,
l = 2 * n.radius + 2 * s,
p = Math.ceil(r / l),
d = Math.ceil(i / l),
h = r % l,
u = i % l;
return p <= a[1] && d <= a[0] && h > 2 * s && u > 2 * s && (o = (d - 1) * a[1] + p), {
idx: o,
i: p,
j: d,
x: r,
y: i
}
}
}, s.prototype = {
constructor: s,
option: function(t, e) {
var n = p[this.token],
i = n.option;
return e === a ? i[t] : (i[t] = e, void(("margin" == t || "matrix" == t || "radius" == t) && r(n)))
},
getPattern: function() {
return p[this.token].patternAry.join("")
},
setPattern: function(t) {
var e = p[this.token],
n = e.option,
a = n.matrix,
r = n.margin,
i = n.radius;
if(n.enableSetPattern) {
this.reset(), e.wrapLeft = 0, e.wrapTop = 0;
for(var o = 0; o < t.length; o++) {
var s = t[o] - 1,
d = s % a[1],
u = Math.floor(s / a[1]),
c = d * (2 * r + 2 * i) + 2 * r + i,
f = u * (2 * r + 2 * i) + 2 * r + i;
h.call(null, {
pageX: c,
pageY: f,
preventDefault: l,
originalEvent: {
touches: [{
pageX: c,
pageY: f
}]
}
}, this)
}
}
},
enable: function() {
var t = p[this.token];
t.disabled = !1
},
disable: function() {
var t = p[this.token];
t.disabled = !0
},
reset: function() {
var t = p[this.token];
t.pattCircle.removeClass("hovered dir s n w e s-w s-e n-w n-e"), t.holder.find(".patt-lines").remove(), t.patternAry = [], t.lastPosObj = null, t.holder.removeClass("patt-error patt-success")
},
error: function() {
p[this.token].holder.addClass("patt-error")
},
checkForPattern: function(t, e, n) {
var a = p[this.token];
a.rightPattern = t, a.onSuccess = e || l, a.onError = n || l
}
}, s.defaults = {
matrix: [3, 3],
margin: 20,
radius: 25,
patternVisible: !0,
lineOnMove: !0,
enableSetPattern: !1
}, e.PatternLock = s
}(jQuery, window, document);
那么本期分享就到这里,我们下期再见
今天我们来分享一下最近很多的搬箱子游戏:
Html代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>JS皮卡丘推箱子游戏代码</title>
<style>
div{
background:url(img/wall.jpg);
}
</style>
</head>
<body>
<script type="text/javascript" src="js/main.js"></script>
</body>
</html>
js代码
var omap;
var move;
var step = 0;
function Map() {
this.mapstyle = [
[0, 0, 1, 1, 1, 1, 1, 0, 0],
[0, 0, 1, 0, 5, 0, 1, 0, 0],
[0, 0, 1, 0, 4, 0, 1, 0, 0],
[1, 1, 1, 0, 3, 0, 1, 1, 1],
[1, 0, 0, 0, 7, 0, 0, 0, 1],
[1, 0, 7, 7, 7, 7, 7, 0, 1],
[1, 0, 0, 0, 7, 0, 0, 0, 1],
[1, 1, 1, 4, 7, 4, 1, 1, 1],
[0, 0, 1, 0, 3, 0, 1, 0, 0],
[0, 0, 1, 0, 7, 0, 1, 0, 0],
[0, 0, 1, 0, 3, 0, 1, 0, 0],
[0, 0, 1, 1, 1, 1, 1, 0, 0]
]
//0:空地 1:墙 3:目的地 4:箱子 5:人 7:箱子和目的地重合 8:人和目的地重合
this.length1 = this.mapstyle.length;
this.length2 = this.mapstyle[0].length;
this.show = function() {
document.getElementsByTagName("body")[0].innerHTML = "";
for (var i = 0; i < this.length1; i++) {
for (var j = 0; j < this.length2; j++) {
switch (this.mapstyle[i][j]) {
case 0:
document.write('<div style="width:45px; height:45px; display:inline-block; "></div>');
break;
case 1:
document.write(
'<div style="width:45px; height:45px; background:url(img/wall.jpg); display:inline-block;"></div>');
break;
case 3:
document.write(
'<div style="width:44px; height:44px; border:0.5px solid limegreen; display:inline-block;"></div>');
break;
case 4:
document.write('<div style="width:45px; height:45px; background:url(img/box.png); display:inline-block;"></div>');
break;
case 5:
document.write(
'<div style="width:45px; height:45px; background:url(img/timg@0,1x.png); background-position:50% 50%; background-size:100%; display:inline-block;"></div>'
);
break;
case 7:
document.write(
'<div style="width:45px; height:45px; background:url(img/over_box.png); display:inline-block;"></div>');
break;
case 8:
document.write(
'<div style="width:45px; height:45px; background:url(img/timg@0,1x.png); background-position:50% 50%; background-size:100%; display:inline-block;"></div>'
);
break;
}
}
document.write("<br/>");
}
}
}
function Move() {
var flag = 0;
var result = 0;
for (var i = 0; i < omap.length1; i++) {
for (var j = 0; j < omap.length2; j++) {
if (omap.mapstyle[i][j] == 3) {
flag++;
}
}
}
console.log(flag);
this.play = function(code) {
for (var i = 0; i < omap.length1; i++) {
for (var j = 0; j < omap.length2; j++) {
if (omap.mapstyle[i][j] == 5 || omap.mapstyle[i][j] == 8) {
break;
}
}
if (omap.mapstyle[i][j] == 5 || omap.mapstyle[i][j] == 8) {
break;
}
}
switch (code) {
case 37: //左
if (omap.mapstyle[i][j - 1] == 0 || omap.mapstyle[i][j - 1] == 3) {
omap.mapstyle[i][j - 1] += 5;
omap.mapstyle[i][j] -= 5;
} else if (omap.mapstyle[i][j - 1] == 7 || omap.mapstyle[i][j - 1] == 4) {
if (omap.mapstyle[i][j - 2] == 0 || omap.mapstyle[i][j - 2] == 3) {
omap.mapstyle[i][j - 2] += 4;
omap.mapstyle[i][j - 1] += 1;
omap.mapstyle[i][j] -= 5;
}
}
break;
case 38: //上
if (omap.mapstyle[i - 1][j] == 0 || omap.mapstyle[i - 1][j] == 3) {
omap.mapstyle[i - 1][j] += 5;
omap.mapstyle[i][j] -= 5;
} else if (omap.mapstyle[i - 1][j] == 7 || omap.mapstyle[i - 1][j] == 4) {
if (omap.mapstyle[i - 2][j] == 0 || omap.mapstyle[i - 2][j] == 3) {
omap.mapstyle[i - 2][j] += 4;
omap.mapstyle[i - 1][j] += 1;
omap.mapstyle[i][j] -= 5;
}
}
break;
case 40: //下
if (omap.mapstyle[i + 1][j] == 0 || omap.mapstyle[i + 1][j] == 3) {
omap.mapstyle[i + 1][j] += 5;
omap.mapstyle[i][j] -= 5;
} else if (omap.mapstyle[i + 1][j] == 7 || omap.mapstyle[i + 1][j] == 4) {
if (omap.mapstyle[i + 2][j] == 0 || omap.mapstyle[i + 2][j] == 3) {
omap.mapstyle[i + 2][j] += 4;
omap.mapstyle[i + 1][j] += 1;
omap.mapstyle[i][j] -= 5;
}
}
break;
case 39: //右
if (omap.mapstyle[i][j + 1] == 0 || omap.mapstyle[i][j + 1] == 3) {
omap.mapstyle[i][j + 1] += 5;
omap.mapstyle[i][j] -= 5;
} else if (omap.mapstyle[i][j + 1] == 7 || omap.mapstyle[i][j + 1] == 4) {
if (omap.mapstyle[i][j + 2] == 0 || omap.mapstyle[i][j + 2] == 3) {
omap.mapstyle[i][j + 2] += 4;
omap.mapstyle[i][j + 1] += 1;
omap.mapstyle[i][j] -= 5;
}
}
break;
}
omap.show();
for (var i = 0; i < omap.length1; i++) {
for (var j = 0; j < omap.length2; j++) {
if (omap.mapstyle[i][j] == 7) {
result++;
}
}
}
if (flag == result) {
alert("恭喜通关,你总共走了" + step + "步");
} else {
result = 0;
}
}
}
window.onload = function() {
omap = new Map();
omap.show();
move = new Move();
function play() {
if (window.event) {
code = window.event.keyCode;
} else {
code = event.keyCode;
}
move.play(code);
step++;
}
console.time("timer");
for (var i = 0; i < 10000; i++) {}
console.timeEnd("timer");
document.onkeydown = play;
}
那么这期就到这里,下期再见
今天我们来分享一款很火的小游戏,1024数独,先看看图
那么我先分享一下html5代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<link rel="stylesheet" href="css/shudu.css" />
<script src="js/jquery.min.js"></script>
<script src="js/shudu.js"></script>
</head>
<body>
<!--构建游戏框架S-->
<div>
<!--得分区 S-->
<div>您现在得分 <span>0</span> 分 </div>
<!--得分区 E-->
<div>
<div>
<ul>
<li class="gameFrameCell "></li>
<li></li>
<li>
</li>
<li>
</li>
</ul>
</div>
<div>
<ul>
<li>
</li>
<li>
</li>
<li>
</li>
<li>
</li>
</ul>
</div>
<div>
<ul>
<li>
</li>
<li>
</li>
<li>
</li>
<li>
</li>
</ul>
</div>
<div>
<ul>
<li>
</li>
<li>
</li>
<li>
</li>
<li>
</li>
</ul>
</div>
</div>
<!--游戏开始按键S-->
<div>
<button class="gameControlButton gameStart">开始</button>
<!--上下左右操作键S-->
<div>
<div>
<span>↑</span>
</div>
<div>
<span>←</span>
<span>↓</span>
<span>→</span>
</div>
</div>
<!--上下左右操作键E-->
</div>
<!--游戏开始按键E0-->
</div>
<!--构建游戏框架E-->
</body>
</html>
接下来css代码:
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
ul {
list-style: none;
}
.color0 {
background: #FFCCCC;
}
.color1 {
background: #FFCCCC;
}
.color2 {
background: #FF9999;
}
.color3 {
background: #FF99CC;
}
.color4 {
background: #CCCCFF;
}
.color5 {
background: #CC99CC;
}
.color6 {
background: #996699;
}
.color7 {
background: #663366;
}
.color8 {
background: #9999CC;
}
.color9 {
background: #CC6699;
}
.color10 {
background: #CC3399;
}
.color11 {
background: #993366;
}
.color12 {
background: #0099CC;
}
.color13 {
background: #666699;
}
.color14 {
background: #999933;
}
.color15 {
background: #003366;
}
.gameFrame {
border: 1px solid #fdfafa;
width: 300px;
height: 300px;
background: #CCCCCC;
margin: 20px auto 50px;
}
.gameFrame>.gameFrameRow {
width: 300px;
height: 75px;
}
.gameFrame>.gameFrameRow .gameFrameUl {
width: 300px;
height: 100%;
}
.gameFrame>.gameFrameRow .gameFrameUl .gameFrameCell {
display: inline-block;
border: 1px solid #fdfafa;
height: 100%;
width: 75px;
float: left;
padding: 2px;
}
.gameFrame>.gameFrameRow .gameFrameUl .gameFrameCell>span {
display: inline-block;
width: 100%;
height: 100%;
line-height: 75px;
text-align: center;
color: #fff;
}
.scoringArea {
text-align: center;
height: 60px;
line-height: 60px;
}
.operateKey {
text-align: center;
}
.operateKey .gameControlButton {
height: 30px;
padding: 0 8px;
display: inline-block;
vertical-align: middle;
background: #66CC99;
border: none;
border-radius: 4px;
color: #fff;
cursor: pointer;
}
.operateKey .directionsKey {
display: inline-block;
width: 100px;
text-align: center;
margin: 0 auto;
}
.operateKey .directionsKey>div {
width: 100px;
}
.operateKey .directionsKey>div>span {
display: inline-block;
width: 30px;
height: 30px;
line-height: 30px;
margin-top: 10px;
background: #339999;
border-radius: 4px;
color: #fff;
cursor: pointer;
}
最后js代码:
$(function() {
var row = 4,
line = 4;
var cellObject = {}, //初始化所有的位置数据对象
cellArray = [], //初始化所有的位置数据数组
isStartGame = false, //判断是否开始游戏
scoreNum = 0; //初始化分数
var sellPiont = {}; //获取所有已经有点位的数据信息
function gameInit(row, line) {
$(".gameFrameCell").html("");
/*初始化参数*/
/*获取行列数*/
row = row, line = line, cellObject = {}, cellArray = [], sellPiont = {}; //初始化所有的位置数据
for (var i = 0; i < row; i++) {
for (var j = 0; j < line; j++) {
var cellKey = i + "" + j;
//cellArray.push(cellKey);//组装位置数组格式,00 01 02 03 11 ...
cellObject[cellKey] = {}; //组建对象里面的对象,达到多层的效果
cellObject[cellKey].row = i; //排
cellObject[cellKey].line = j; //列
}
}
}
function randomRendering() {
/*随机位置随机赋值,赋值局限(2,4)*/
var num = Math.floor(Math.random() * 2), //取随机值,0 产生2 的块,1产生4的块。
cellNum = 2; //初始化为2的块
var createCell = "", //新建的节点
power = 1; //因为js没有计算固定的开根次的方法,只有 sqrt() ,开平方,
if (num == 0) {
createCell = $('<span power="1">2</span>');
power = 1;
cellNum = 2;
} else {
createCell = $('<span power="2">4</span>');
cellNum = 4;
power = 2;
}
cellArray = Object.keys(cellObject); //组装位置数组格式,00 01 02 03 11 ...
/*console.log(cellArray.length);*/
/*获取长度的随机数,随机获取位置块的数据*/
var pointsLength = Object.keys(cellObject).length;
if (pointsLength <= 0) {
alert("游戏结束!")
return false;
}
var pointNum = Math.floor(Math.random() * pointsLength);
/*获取位置块在对象里面的内容*/
var point = cellObject[cellArray[pointNum]];
/*找到对应的位置块,添加内容*/
$(".gameFrameUl").eq(point.row).find(".gameFrameCell").eq(point.line).addClass("active").append(createCell);
/*删除已经被占据位置的坐标块 需要在对象和数据里面同步删除*/
delete cellObject[cellArray[pointNum]];
sellPiont[cellArray[pointNum]] = {};
sellPiont[cellArray[pointNum]].cellNum = cellNum;
sellPiont[cellArray[pointNum]].power = power;
cellArray.splice(pointNum, 1);
}
/*绑定游戏开始时间*/
$(".gameStart").bind("click", function() {
gameInit(4, 4);
isStartGame = true;
/*恢复分数初始化*/
scoreNum = 0;
score(scoreNum);
randomRendering();
});
/*上下左右键盘事件*/
$(document).keydown(function(event) {
var e = event || window.event || arguments.callee.caller.arguments[0];
/*上*/
if (e.keyCode == 38) {
$(".upBtn").click();
}
/*左*/
if (e.keyCode == 37) {
$(".leftBtn").click();
}
/*右*/
if (e.keyCode == 39) {
$(".rightBtn").click();
}
/*下*/
if (e.keyCode == 40) {
$(".downBtn").click();
}
});
/*操作键绑定事件*/
/*上键事件*/
$(".upBtn").bind("click", function() {
if (!isStartGame) {
alert("你还没有开始游戏。")
return false;
}
/*获取所有已存在的位置*/
var cellArrayActive = Object.keys(sellPiont);
/*对已有位置数据在对应方向上的比较 包括判断方向上是否存在其他块数据 是否能够相加,暴扣方向上最顶端处理之后中间是否留有空余位置用于二次移动但不靠口练习相加,
* 如果存在 2 2 4 ,前面两个2相加得4 这时两个4之间有一个空格,所以不能做相加处理,只能把后者4往方向端移动一步*/
cellArrayActive.sort(function(a, b) {
return a - b;
})
var cellArrayLen = cellArrayActive.length;
for (var i = 0; i < cellArrayLen; i++) {
var rowSubscript = Math.floor(cellArrayActive[i] / 10); //获取所在的排数,便于比较
if (rowSubscript > 0) {
var upRowNum = rowSubscript - 1;
var upLineNum = cellArrayActive[i] % 10;
createNewRow(upRowNum);
}
}
function createNewRow(upRowNum) {
if (upRowNum >= 0) {
var upCellnmu = upRowNum + "" + upLineNum; //获取方向端前列的一个方块的坐标,用于判断是否有值 sellPiont[upCellnmu]==true 有值
/*console.log(upCellnmu);*/
if (sellPiont[upCellnmu]) {
/*邻近的方向端块有值*/
if (sellPiont[upCellnmu].cellNum == sellPiont[cellArrayActive[i]].cellNum) {
/*如果方向位置有有数据且数据相同,则可以相加处理*/
sellPiont[upCellnmu].cellNum = sellPiont[cellArrayActive[i]].cellNum * 2;
sellPiont[upCellnmu].power = sellPiont[cellArrayActive[i]].power + 1;
scoreNum = scoreNum + 1;
score(scoreNum);
/*sellPiont[upCellnmu].power+=1;
console.log(sellPiont[cellArray[i]].power);*/
var upCell = $('<span class="color' + sellPiont[upCellnmu].power + '">' + sellPiont[upCellnmu].cellNum +
'</span>');
$(".gameFrameUl").eq(upRowNum).find(".gameFrameCell").eq(upLineNum).addClass("active").html(upCell);
$(".gameFrameUl").eq(rowSubscript).find(".gameFrameCell").eq(upLineNum).addClass("active").html("");
/*释放空格*/
/*delete cellObject[sellPiont[cellArray[i]]];*/
cellArrayActive.push(cellArrayActive[i]);
cellObject[cellArrayActive[i]] = {}; //排
cellObject[cellArrayActive[i]].row = rowSubscript; //排
cellObject[cellArrayActive[i]].line = upLineNum; //列
delete sellPiont[cellArrayActive[i]];
return false;
} else {
upRowNum = upRowNum + 1;
upCellnmu = upRowNum + "" + upLineNum;
if (upCellnmu != cellArrayActive[i]) {
/*方向端没有数据的时候开始移动*/
/*console.log(upCellnmu);
console.log(cellArrayActive[i]);*/
/*方向端没有数据的时候开始移动*/
sellPiont[upCellnmu] = {};
sellPiont[upCellnmu].cellNum = sellPiont[cellArrayActive[i]].cellNum;
sellPiont[upCellnmu].power = sellPiont[cellArrayActive[i]].power;
cellObject[cellArrayActive[i]] = {}; //排
cellObject[cellArrayActive[i]].row = rowSubscript; //排
cellObject[cellArrayActive[i]].line = upLineNum; //列
/*页面上删除展示效果*/
$(".gameFrameUl").eq(rowSubscript).find(".gameFrameCell").eq(upLineNum).addClass("active").html("");
var upCell = $('<span class="color' + sellPiont[cellArrayActive[i]].power + '">' + sellPiont[cellArrayActive[
i]].cellNum + '</span>');
$(".gameFrameUl").eq(upRowNum).find(".gameFrameCell").eq(upLineNum).addClass("active").html(upCell);
delete sellPiont[cellArrayActive[i]];
delete cellObject[upCellnmu];
return false;
}
}
} else {
if (upRowNum <= 0) {
/*console.log("空空如也");*/
/*方向端没有数据的时候开始移动*/
sellPiont[upCellnmu] = {};
sellPiont[upCellnmu].