整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

HTML5实战-签到功能

篇文章讲到HTML炫酷的主流框架,今天小明就介绍一个HTML5功能的实现代码

Introduce(介绍)

用户签到的H5例子(css+jquery,无图片),由于网上找的的用户签到例子都不好,要不就是好多图片组成的,要不就大量冗余代码,所以特意做了个签到界面(移动端)。

User sign sample page for mobile using h5 which only use css + jquery + html.

一些关键的地方

这个功能的编写思路是,先构建日期和签到相关数据,然后从服务端获取数据,并对原有数据进行更改,最后进行渲染。

这样子很好的摆脱了逻辑比较凌乱的问题,并且可以直接将这些数据用 vue.js 来挂载(本文没有这样做)。

生成日期数据

//生成日期数据
 function buildData() {
 var da = {
 dates: [],//日期数据,从1号开始
 current: '',//当前日期
 monthFirst: 1,//获取当月的1日等于星期几
 month: 0,//当前月份
 days: 30,//当前月份共有多少天
 day: 0,//今天几号了
 isSigned: false,//今天是否已经签到
 signLastDays:3,//连续签到日子
 signToday: function () {
 this.isSigned = true;
 this.dates[this.day].isSigned = true;
 },
 };
 var ds = [];
 //初始化日期数据
 var dt = new Date();
 da.current = dt.ToString('yyyy年M月d日');
 da.monthFirst = new Date(dt.getFullYear(), dt.getMonth(), 1).getDay();
 da.month = dt.getMonth() + 1;
 da.days = new Date(dt.getFullYear(), parseInt(da.month), 0).getDate();//获取当前月的天数
 da.day = dt.getDate();
 for (var i = 1; i < da.days + 1; i++) {
 var o = {
 isSigned: false,//是否签到了
 num: i,//日期
 isToday: i == da.day,//是否今天
 isPass: i < da.day,//时间已过去
 };
 ds[i] = o;
 }
 da.dates = ds;
 return da;
 }

有了数据之后,就可以将数据转换为界面了

//渲染数据
 function renderData(da) {
 var signDays = document.getElementById('spSignDays');
 signDays.innerText = da.signLastDays;
 var root = document.getElementById("signTable");
 root.innerHTML = '';
 var tr, td;
 var st = da.monthFirst;
 var dates = da.dates;
 var rowcount = 0;
 //最多6行
 for (var i = 0; i < 42; i++) {
 if (i % 7 == 0) {
 //如果没有日期了,中断
 if (i > (st + da.days))
 break;
 tr = document.createElement('tr');
 tr.className = 'darkcolor trb';
 root.appendChild(tr);
 rowcount++;
 }
 //前面或后面的空白
 if (i < st || !dates[i - st + 1]) {
 td = document.createElement('td');
 td.innerHTML = '<div class="sign-blank"><span></span></div>';
 tr.appendChild(td);
 continue;
 }
 //填充数字部分
 var d = dates[i - st + 1];
 td = document.createElement('td');
 var tdcss = '';
 if (d.isToday)
 tdcss = 'sign-today';
 else if (d.isPass)
 tdcss = 'sign-pass';
 else
 tdcss = 'sign-future';
 if (d.isSigned) {
 tdcss = 'sign-signed ' + tdcss;
 td.innerHTML = '<div class="' + tdcss + '"><span>' + d.num + '</span><svg xmlns="http://www.w3.org/2000/svg" version="1.1" class="sign-pin svg-triangle "><polygon points="0,0 35,0 0,35" /></svg></div>';
 } else {
 tdcss = 'sign-unsign ' + tdcss;
 td.innerHTML = '<div class="' + tdcss + '"><span>' + d.num + '</span></div>';
 }
 tr.appendChild(td);
 }
 //计算是否需要添加最后一行
 if ((st + da.days + 1) / 7 > rowcount)
 root.appendChild(tr);
 }
 //构建日期数据
 var da = buildData();
 //渲染
 renderData(da);

以上就是本篇文章的全内容了

学习从来不是一个人的事情,要有个相互监督的伙伴,想要学习或交流前端问题的小伙伴可以私信回复小明“学习” 获取资料,一起学习!

于互联网发展的今天,IT行业慢慢变成大多数年轻人发展的目标,不仅前景好,薪资也是越来越高的,而web前端是行业中需要的技术,也促进了大多数朋友在学习html5,今天小猿圈讲师给你分享基于iview的router常用控制方式,在学的过程中不浪费时间少走弯路。

1、iview的router控制需求

最近在使用iview框架写项目,遇到了一些路由控制上的问题,解决过程中也有一些心得,故在此记录下来.

每个项目在开发时,对于类似tags(标签页)的控制需求都不尽相同,故以下先列出本文所述项目对标签页的控制要求(如有不同需求,本文当也可提供一些思路):

对于同名(name)的路由标签页,不能打开多个.譬如说从商品列表中打开商品展示标签页,如果已经有在打开的商品编辑页面,则替换之.新打开的,未保存,已保存的标签页,同时只能存在一个(即不同params相同name的route只能有一个);

替换掉一个新的页面时,通过切换的方式切换回来(先切到其他标签页再切换回来),仍是原来页面的内容(即实际记录的params在替换后应变化).类似的情况,还应包含单据从未保存到已保存,以及保存并新增功能;

2、基于vue的router控制

iview是基于vue的框架,故vue本身自带的router控制方法是必然可行的.

vue变更路由的常用方式参考以下(该方法在官方api中有更详细的介绍):

//变更当前路由(有历史记录,建议使用此方式)

this.$router.push({

name:'routerName',

params:routerParam

})

//变更当前路由(无历史记录)

this.$router.replace({

name:'routerName',

routerParam

})

官方路由变更确实可以正常打开标签页,但在实现1中所提到的各种需求的时候,就有些不满足需求了.为此,需要参考3中,如何基于iview的outer控制.

3、基于iview的router控制

iview在控制路由的时候,使用vuex中的app.js来记录标签页路由信息,如果对vuex还是很了解的话,可以通过这篇博文来先打一下基础.

3.1如何实现需求1.1

想要实现不同params相同name的route在iview中只能有一个,关键是改变iview对路由相等的判断方法,即'/src/libs/util.js'里的routeEqual方法:

/**

* @description 根据name/params/query判断两个路由对象是否相等

* @param {*} route1 路由对象

* @param {*} route2 路由对象

*/

export const routeEqual = (route1, route2) => {

return route1.name === route2.name

// 此处改变相同路由的判断方式,改为name相同即认为相同

// const params1 = route1.params || {}

// const params2 = route2.params || {}

// const query1 = route1.query || {}

// const query2 = route2.query || {}

// return (route1.name === route2.name) && objEqual(params1, params2) && objEqual(query1, query2)

}

这里稍微解释下(如果不关注原因,可以直接看3.2).当改变路由时,'src\components\main\main.vue'作为近乎顶层的组件控制着近乎所有的全局逻辑,其中就有对路由的监控:

...

<side-menu

accordion

ref="sideMenu"

:active-name="$route.name"

:collapsed="collapsed"

@on-select="turnToPage"

:menu-list="menuList"

>

...

//此方法隶属于methods,用以监控side-menu的选择事件,即平时从左侧菜单打开标签页的逻辑

turnToPage (route) {

let { name, params, query } = {}

if (typeof route === 'string') name = route

else {

name = route.name

params = route.params

query = route.query

}

if (name.indexOf('isTurnByHref_') > -1) {

window.open(name.split('_')[1])

return

}

this.$router.push({

name,

params,

query

})

},

...

watch: {

// 检测route的变化

$route (newRoute) {

const { name, query, params, meta } = newRoute

this.addTag({

route: { name, query, params, meta },

type: 'push'

})

this.setBreadCrumb(newRoute)

this.setTagNavList(getNewTagList(this.tagNavList, newRoute))

this.$refs.sideMenu.updateOpenName(newRoute.name)

}

},

...

从以上代码可推测出,main.vue通过turnToPage方法实现打开标签页的逻辑,但方法内部并没有体现便签页显示效果变化(包含内部数据变化,以下同)的逻辑,这是由于显示效果变化的逻辑,由对$router的监控实现.

这样,不止从左侧菜单打开新标签页可以实现显示变化效果,其他只要使用vue的原版push等方法改变router的方法,均可监测到.

逐步查看下各个方法,其中影响当前标签页显示效果的,是'src/store/module/app.js'的addTag方法.

addTag (state, { route, type = 'unshift' }) {

let router = getRouteTitleHandled(route)

if (!routeHasExist(state.tagNavList, router)) {

if (type === 'push') state.tagNavList.push(router)

else {

if (router.name === homeName) state.tagNavList.unshift(router)

else state.tagNavList.splice(1, 0, router)

}

setTagNavListInLocalstorage([...state.tagNavList])

}

},

尽管方法内部仍调用了很多,其中一个很重要的判断,就是routeHasExist(路由是否存在),这个方法也是判断是否为相同标签页的一个关键节点(该方法同样在util.js):

/**

* 判断打开的标签列表里是否已存在这个新添加的路由对象

*/

export const routeHasExist = (tagNavList, routeItem) => {

let len = tagNavList.length

let res = false

doCustomTimes(len, (index) => {

if (routeEqual(tagNavList[index], routeItem)) res = true

})

return res

}

明显可以看出,这个方法内调用routeEqual,就是用以判断是否为相同路由的实际方法(当然是通过比较新路由与已有路由进行比较),如此,仅需改变routeEqual即可.

以防万一,全局搜索下调用这个routeEqual的所有方法,发现所有调用的地方再routeEqual在改变后不会出现新的问题.

3.2如何实现需求1.2

在进行3.1的操作后,问题得到了部分解决.余下的问题在于需求1.2没有得到实现和解决.

首先是,如何实现从列表中打开或新建的,替换原来的标签页,在来回切换后不会回到原来的标签页.只需在app.js中注册改变标签页参数的方法:

// 变更指定路由的参数

changeTagParams (state, route) {

let routeOldIndex = state.tagNavList.findIndex(m => routeEqual(m, route))

if (routeOldIndex !== -1) {

let routeOld = state.tagNavList[routeOldIndex]

routeOld.params = route.params

state.tagNavList.splice(routeOldIndex, 1, routeOld)

setTagNavListInLocalstorage([...state.tagNavList])

}

},

然后在main.vue中对$route的监控最后引用即可.

watch: {

// 检测route的变化

$route (newRoute) {

const { name, query, params, meta } = newRoute

this.addTag({

route: { name, query, params, meta },

type: 'push'

})

this.setBreadCrumb(newRoute)

this.setTagNavList(getNewTagList(this.tagNavList, newRoute))

this.$refs.sideMenu.updateOpenName(newRoute.name)

// 增加路由参数变更环节

this.changeTagParams(newRoute)

}

},

其次,如果出现像保存并新增,或者从未保存到已保存,这两种情况来回切换后不会回到原来的情况.

保存并新增,关键是"新增"效果:

// 清空数据,该方法在保存后调用

clearData () {

//该部分是用来清除当前route的参数

this.$router.push({

params: Object.assign(this.$route.params, { id: undefined })

})

//这部分代码是用来清空当前页面内容,每个模块都不尽相同,不必模仿

this.mOtherExpense = JSON.parse(JSON.stringify(this.mOtherExpenseInitial))

this.tableData = [{}]

this.loadCode()

this.mOtherExpense.openingDate = new Date()

},

从未保存到已保存,关键同样是如何让route记住新的id(或其他参数):

// 设置路由id,该方法在第一次保存后调用

setData (id) {

//这里的id是保存后从后台传来的新id

this.$router.push({

params: Object.assign(this.$route.params, { id })

})

}

文中已将本人常用的iviewrouter控制方式提出,或有未涉及者,根据以下了解大概也可解决:

app.js中的state.tagNavList是标签页中显示的标签集合;如果要改变一些内容,main.vue中对$route的监控是事件发起的开端,可考虑从这里修改;

以上就是小猿圈web前端讲师对于基于iview的router常用控制方式的介绍,记住一定要练习,多学多看多练这才是学习一门新技术好的开始,如果没有系统的视频可以观看小猿圈,里面有更完善更全的视频。

面是测试html5重力感应的demo

http://bbs.qietu.com/html/zhongli/

http://www.qietu.com/html/f2/qqqianbao/

demo2是切图网为腾讯网提供的web前端技术支持的项目,采用的就是html5的重力感应。

重力感应主要用到两种事件:

1 orientationchange

这个事件在屏幕发生翻转时触发

window.orientation可获得设备的方向,一共有三个值0:竖直, 90:右旋, -90:左旋

2 deviceorientation 和 MozOrientation(firefox专用)

deviceorientation事件可获得三个值alpha,beta,gamma,分别代表绕Z轴的旋转角度(0~360),绕X轴的旋转角度(-180~180),绕Y轴的旋转角度(-90~90)

MozOrientation事件中可获得三个值z,x,y,分别代表垂直加速度,左右的倾斜角度,前后的倾斜角度(取值范围:-1~1)

坐标系见下图

下面是示例游戏用到重力感应的代码:

  1. window.onorientationchange = function(e){

  2. game.hideNavBar(); //屏幕翻转时隐藏地址栏

  3. if(game.stage) game.stage.updatePosition(); //更新舞台位置

  4. };

  5. window.ondeviceorientation = function(e)

  6. {

  7. var ang;

  8. var o = window.orientation; //获取设备方向

  9. if(o == 90){

  10. ang = e.beta; //设备横向1

  11. }

  12. else if(o == -90){

  13. ang = -e.beta; //设备横向2

  14. }

  15. else if(o == 0){

  16. ang = e.gamma; //设备纵向

  17. }

  18. if(ang > 5)

  19. {

  20. keyState[Q.KEY.RIGHT] = true;

  21. }

  22. else if(ang < -5)

  23. {

  24. keyState[Q.KEY.LEFT] = true;

  25. }

  26. else

  27. {

  28. keyState[Q.KEY.RIGHT] = false;

  29. keyState[Q.KEY.LEFT] = false;

  30. }

  31. }

无附件,源码面前,了无秘密,作为web前端工程师,我们需要具备查看源码的能力。

原文:http://bbs.qietu.com/forum.php?mod=viewthread&tid=15036