移动微社区----附手机端滑动分页源码
背景:楼主目前在一家做电子商务的公司上班,公司已有成熟的商城系统,社区系统,以及CMS框架。
而做社区的,最大的就是腾讯。今年大概三月份的时候,腾讯推出了微社区。推出了就推出了吧,这也没啥。关键是需要使用的人微信服务号超过10000人关注这就受不了了。我们不能为了使用这个平台而去刷关注吧。而巧的是,这个平台让我们老总给看见了。老总用了几次,甚是欢喜啊。说是未来是移动手机端的时代,手机用户会大大多于PC客户端用户。而我们主要又是卖产品的,总不能叫客户去用腾讯的产品吧,而且人腾讯还不让一般人用。这可如何是好呢?在我们技术团队面前念叨了好几次之后,终于忍不住了,咱们自己照着它开发一套吧。
咱们自己开发一套?有没有搞错?腾讯那么多人的团队开发出来的产品,要我们这几个人去搞?图1是拉钩网的微社区。
以上是通过Chrome模拟的Iphone5的界面,Chrome的这一功能真的很强大啊,可以通过浏览器直接模拟各种移动端设备。设置方法:先按F12打开调试面板,然后按Esc启动以下画面,然后就可以切换设备了,完成之后点击Emulate,这样浏览器就可以直接当移动设备了。如图2:
说到Chrome,之前有段时间服务一直无法访问,现在又可以了,IP:74.125.205.113。
这个事情从三月份就提出来了啊,由于期间各种任务,都抽不出时间,所以一直拖到了6月中旬。那时候恰好我手上的工作忙得差不多了,于是老总发话了,你来吧。
由于此时项目组其他同事都还在忙别的活,而老总又觉得这个东西没啥功能,所以我就只能硬着头皮来了。在网上好好的走了一下他的流程。发现具体的功能如下:
加入小组,发表发话,评论话题,分享话题,赞和取消赞,个人主页,他人主页,Ta的话题,我的话题,我的消息,帖子详情,话题分类,话题列表……应我们老总的强烈要求,还得加上个人中心,更改头像,更改密码,登录功能,注册功能。
还好之前PC端SNS部分做得还算熟练,仔细分析了一下技术上基本没什么难点。而数据库表和底层代码都可复用之前PC端的,所以我更是信誓旦旦的承诺,没问题!就这样单枪匹马的就上阵了,后来那个悔啊……
实现的详细过程就不细说了,有问题的几个地方这里记录下。
第一:发布话题,下图(图3)是发布话题页面,这是腾讯官网的。
从这个图中我们可以看到,这个发表话题是可以发表表情,图片(腾讯官网可以发8张),以及文字的,并且对文字的长度做了限制。做发话题的时候遇到了三个问题,一个是我们之前数据库涉及和底层代码发表话题时仅支持1张图片,这部分由于时间问题就没做修改;一个是这个字数限制:我们都知道textBox可以通过MaxLength属性来设置最大输入字符数。可是通过这个属性设置的一个最大问题就是,不区分中英文。而我们体验一下腾讯的官网就可以知道,他这个字数限制是区分中英文的。之后的解决方案是:绑定这个textBox的input propertychange事件,同时通过脚本计算可输入长度,以下脚本是计算输入字符的长度(区别中英文),但是这样无法避免粘贴时字数超过限制,上图就出现了负数;另外一个问题至今还未解决,哪位同僚有解决方案提供将感激不尽。我们看到那个表情是分不同页的,也就是说可以滑动的!就是这个滑动,至今为找到解决方案。在网上有人说监测TouchStart、TouchMove和TouchEnd事件可以解决,可是试过之后发现结果不太理想啊。所以我这边暂时的解决方案就是点击下边小圆点来实现切换。以下是监测输入字符的长度:
//字符长度判断 function textareastrlen(str) { var len; var i; len = 0; for (i = 0; i < str.length; i++) { if (str.charCodeAt(i) > 255) { len += 2; } else { len++; } } if (len % 2 != 0) { len = len + 1; } return parseInt(len / 2); }
第二个就是查看图片:(以下左图是腾讯官网的图片查看效果,右图是我做出的效果)
开始也是想模拟腾讯的这种效果,直接在上边加一个遮罩层。后来发现这样有问题,腾讯这个只要细看也可以看出问题,遮罩层下边的内容其实在移动。另外一个缺陷就是图片点击放大后,位置控制不好,在PC端位置居中了可是到了手机上位置硬是不能居中,后来无奈之下用了之前用的一款神器插件:PhotoSwipe,这是一款很强大的图片查看器,还可以自动滑动,详细介绍请看Demo。
第三个就是今天要给源码的分页问题。一般在PC上我们要分页都是通过上一页和下一页来实现的,可是在手机端如果还通过这种方式分页的话用户的体验会大大的下降,所以就通过当下滑到一定程度的时候自动加载下一页面。而那个程度怎么监测呢,我们可以通过页面上的某个元素来检查。实现思路:首先加载部分数据,当下滑到某个元素可见的时候,如果还有数据,则新发送请求,然后追加在当前页面。以下是页面代码:
@foreach (var item in Model.TopicList) { <div class="topicBox" id="t_@(Model.Topic.TopicID)"> </div> }
注意,由于这里是一个foreach循环,所以我可以监测最后一个class为topicBox的div元素,如果他出现在当前可视区域了,则说明该向后台发请求了。监测是否在可视区域代码:
//判断元素是否进入可视区域 function see(objLiLast) { //浏览器可视区域的高度 var see = document.documentElement.clientHeight; //滚动条滑动的距离 var winScroll = $(this).scrollTop(); //距离浏览器顶部的 var lastLisee = $(objLiLast).offset().top; return lastLisee < (see + winScroll) ? true : false; }
发送请求代码:
var page = 1; var pageTotal = parseInt($("#allpage").val()); //是否请求出AJAX的“开关”; var onOff = true; $(window).scroll(function () { //拖动滚条时,是否发送AJAX的一个“开关” $(".topicBox").each(function () { //引用最后一个div var lastLi = $(".topicBox:last"); //调用是否进入可视区域函数 var isSee = see(lastLi); if (isSee && onOff && page < pageTotal) {//最底部元素可见,开关开启而且还有下拉 $("#loadNext").show(); //显示正在加载图标 onOff = false; $.ajax({ url: "@(ViewBag.BasePath)Home/GetPageData", type: "POST", dataType: "Json", data: { page: page+1 }, asyc: false, success: function (result) { if (result.status == "success") { var data = result.result; for (var i = 0; i < data.length; i++) { if (data[i].imageUrl) {//有图片 $("#allThreadList").append('xxx'); } else {//无图片 $("#allThreadList").append('xxx'); } $("#t_" + data[i].topicId).append('<div class="topicList" id="topic_' + data[i].topicId + '"><ul id="replyList_' + data[i].topicId + '" class="replyUl" itemid="' + data[i].topicId + '"> </ul> </div>'); var replyList = data[i].replyList; if (replyList) { if (replyList.length > 3) {//3条评论以上 for (var j = 0; j < 3; j++) { $("#replyList_" + data[i].topicId).append('<li id="p_6_13" uid="6419340" author="ff。"><a href="javascript:;" class="sW fl"><span>' + data[i].replyList[j].replynickName + ':</span>' + data[i].replyList[j].replyContent + '</a></li>'); } $("#topic_" + data[i].topicId).append('<p id="rCount_6" class="more"><a href="@(ViewBag.BasePath)MSNS/Home/TopicReply/' + data[i].topicId + '?viewName=TopicDetail">更多</a>共<span class="replyCount" itemid="' + data[i].topicId + '">' + data[i].replyList[j].replyCount + '</span>条评论</p>'); } else {//评论数小于三条 for (var h = 0; h < replyList.length; h++) { $("#replyList_" + data[i].topicId).append('<li id="p_6_13" uid="6419340" author="ff。"><a href="javascript:;" class="sW fl"><span>' + data[i].replyList[h].replynickName + ':</span>' + data[i].replyList[h].replyContent + '</a></li>'); } } } }; } $("#loadNext").hide(); //隐藏正在加载 onOff = true; page ++; } }); } }); });
这是前台请求的代码,注:因为请求回来需要追加的元素较多,所以这里代码会有点乱,但是不影响功能。后台代码:
groupInfo.TopicList = new PagedList<Model.SNS.GroupTopics>( bllTopic.GetTopicListPageByGroup(GroupId, startIndex, endIndex, false) , page, pagesize, toalcount); if (groupInfo.TopicList != null && groupInfo.TopicList.Count > 0)//有话题 { Model.SNS.GroupTopics groupTopics; JsonObject jsonObject; JsonObject reply; HttpCookie cookie; List<JsonObject> replyList=new List<JsonObject>(); List<JsonObject> resultList=new List<JsonObject>(); foreach (Model.SNS.GroupTopics item in groupInfo.TopicList)//遍历话题列表 { groupTopics = groupTopicsBll.GetModelByCache(item.TopicID); jsonObject=new JsonObject(); if (null != groupTopics) { if (null != CurrentUser) { cookie = Request.Cookies["topicFav_" + groupTopics.TopicID + CurrentUser.UserID]; if (null != cookie) //取消赞 { jsonObject.Put("support", "support"); } else { jsonObject.Put("support", "nosupport"); } } jsonObject.Put("uid", groupTopics.CreatedUserID); jsonObject.Put("topicId", groupTopics.TopicID); jsonObject.Put("nickName",groupTopics.CreatedNickName); jsonObject.Put("createdDate", Web.Components.DateTimeHelper.ConvertDateToTime(groupTopics.CreatedDate)); jsonObject.Put("description",Server.HtmlDecode(ViewModel.ViewModelBase.ReplaceFace(groupTopics.Description))); jsonObject.Put("imageUrl", Web.Components.FileHelper.GeThumbImage(groupTopics.ImageUrl, "T180X120_")); jsonObject.Put("ref", groupTopics.ImageUrl); jsonObject.Put("favCount",groupTopics.FavCount); List<Model.SNS.GroupTopicReply> list = bllReply.GetTopicReplyByTopic(groupTopics.TopicID, startIndex, endIndex); if (null != list && list.Count > 0) { foreach (Model.SNS.GroupTopicReply topicReply in list) { reply = new JsonObject(); reply.Put("replynickName", topicReply.ReplyNickName); reply.Put("replyContent", Server.HtmlDecode(ViewModel.ViewModelBase.ReplaceFace(topicReply.Description))); reply.Put("replyCount", list.Count); replyList.Add(reply); } jsonObject.Put("replyList", replyList); } } resultList.Add(jsonObject); } // return new Result(ResultStatus.Success,resultList); json.Put("status", "success"); json.Put("result", resultList); return Json(json); }
至此,整个滑动分页就完成了。其他的功能开发起来都还算顺利,如果对功能实现有疑问的欢迎大家一起交流,谢谢。