纯Ajax实现滚动条拖动图片懒加载【经验总结】
其实之前也没仔细去动手实现这个效果,是之前好友跟我说他想实现如图这样的图片懒加载效果,具体是什么效果听我描述还不如你们自己动手去体验下就知道了,地址栏输入bing点com回车,bing首页选项卡里选择搜索“图片”,输入搜索关键字回车就可以查看到我说的图片懒加载效果了。如图:


知道了是什么效果了,就要分析它的实现原理,很明显是一个ajax应用。拖动滚动条的时候,有一个定时器在不停的监听scrollTop是不是接近页面底部即图片是不是即将显示完,显示完后再ajax方式发送请求异步获取后台数据,然后前端js去解析数据,动态往页面创建img元素。到此,数据动态读取和展示就没有任何问题了,但是,当时我动手做这个效果的时候,就一时不知道怎么实现图片未完全加载完毕前显示一个loading的效果,因为万一图片大小体积很大,加载很慢,那时候图片所占的位置会出现短暂的一片空白的现象,给用户体验会很不好。所以最好是在浏览器未完全下载完一张图片前,先暂时显示一张loading的小图片,待图片加载完毕,再显示它,这样就不会给用户一种等待图片下载的无趣的过程。为了实现这个效果,我斟酌思考了很久,终于是实现了这个效果,上效果图先:

至于我具体是怎么实现的,我直接上代码吧:
package com.lxw.spring.dao;
import java.util.ArrayList;
import java.util.List;
import com.lxw.spring.entity.Image;
/**
* @author Lanxiaowei
* @createTime 2011-10-30 上午10:55:48
*/
public class ImageDao {
/**
* 默认初始化20条
*/
private static final int DEFAULT_AMOUNT = 20;
private static final String DEFAULT_IMAGEURL = "images/qqxw.png";
/**
* 这里为了简便只是模拟下数据获取,实际需要进行数据库操作
*/
public List getImageList(int pageNum){
if(pageNum < 0){
pageNum = 0;
}
int startNum = (pageNum - 1)*DEFAULT_AMOUNT;
int endNum = pageNum*DEFAULT_AMOUNT;
List<Image> images = new ArrayList<Image>();
for (int i = startNum; i < endNum; i++) {
images.add(
new Image(new Long(i+1),"白洋荷色" + (i+1),DEFAULT_IMAGEURL)
);
}
return images;
}
}
ImageDao里我是直接手动构建List,没有从数据库查询,因为数据获取方式不是重点,重点是后台怎么返回数据给前端,而前端又是怎么解析数据并展示它的。
package com.lxw.spring.util;
import java.util.Iterator;
import java.util.List;
import com.lxw.spring.entity.Image;
/**
* @author Lanxiaowei
* @createTime 2011-10-30 上午10:38:02
*/
public class JsonUtils {
public static <T> String listToJsonString(List<T> list){
StringBuffer json = new StringBuffer("{\"total\":").append(list.size()).append(",");
json.append("\"data\":[");
if(null != list && list.size() != 0){
int count = 0;
for (T t : list) {
if(t instanceof Image){
count++;
Image image = (Image)t;
json.append("{\"id\":").append(image.getId()).append(",");
json.append("\"title\":\"").append(image.getTitle()).append("\",");
json.append("\"imgUrl\":\"").append(image.getImgUrl());
if(count == list.size()){
json.append("\"}");
}else{
json.append("\"},");
}
}
}
json.append("]}");
}
return json.toString();
}
}
JsonUtils类是用于辅助生成json字符串的。
package com.lxw.spring.entity;
/**
* @author Lanxiaowei
* @createTime 2011-10-30 上午10:35:16
*/
public class Image {
private Long id;
private String title;
private String imgUrl;
}
这个是Image的实体类,很简单,为了节省篇幅,get set我省略没贴出来。
package com.lxw.spring.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.lxw.spring.dao.ImageDao;
import com.lxw.spring.entity.Image;
import com.lxw.spring.util.DaoFactory;
import com.lxw.spring.util.JsonUtils;
/**
* @author Lanxiaowei
* @createTime 2011-10-30 上午11:11:21
*/
public class ImageServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
String pn = request.getParameter("pageNum");
if(null == pn || pn.equals("") || pn.equals("0")){
pn = "1";
}
int pageNum = Integer.parseInt(pn); //当前页码
//System.out.println("当前页码:" + pageNum);
ImageDao imageDao = DaoFactory.getImageDao("imageDao");
String json = JsonUtils.<Image>listToJsonString(imageDao.getImageList(pageNum));
response.setCharacterEncoding("UTF-8");
response.setContentType("application/x-json");
System.out.println(json);
response.getWriter().write(json);
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
ImageServlet是用于接收前端的ajax请求,并返回json数据的。ImageServlet里dao的获取我模拟了Spring里的控制反转,这个也不是重点,直接new个dao出来也可以。
重点是前端的json数据解析了,看仔细咯,哈哈!!!
<%@ page import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
...
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href="css/style.css"/>
<script src="js/browser.js"></script>
<script src="js/xmlhttprequest.js"></script>
<script src="js/pageHeight.js"></script>
<script>
var iHeight = 0;
var iTop = 0;
var iIntervalId = null; //定时器
var pageNo = 1; // 当前页数,默认设为第 1 页
// 判断滚动条是否到达底部
function reachBottom() {
var scrollTop = 0;
if(document.documentElement && document.documentElement.scrollTop) {
scrollTop = document.documentElement.scrollTop;
} else if (document.body) {
scrollTop = document.body.scrollTop;
}
if((scrollTop > 0) && (scrollTop + clientHeight == iHeight)) {
return true;
} else {
return false;
}
}
//发送Ajax请求获取服务器端数据
function getResponseText(url){
xmlHttp = createXmlHttpRequest();
xmlHttp.open('get', url, false);
xmlHttp.send(null);
if (xmlHttp.readyState == 4){
if (xmlHttp.status == 200){
var data = xmlHttp.responseText;
pageNo++;
parseResponse(data);
}
}
}
//解析返回的json数据
function parseResponse(responseData){
var json = eval('(' + responseData + ')');
var count = json.total;
var c = document.getElementById("main");
for(var i = 0; i < count; i++){
var image = json.data[i];
var li1 = document.createElement("li");
var li2 = document.createElement("li");
loadImage(image.imgUrl,image.title, "loading",li1,imgLoaded);
li2.innerText = image.title;
var ul = document.createElement("ul");
ul.appendChild(li1);
ul.appendChild(li2);
c.appendChild(ul);
}
}
//监听滚动条滚动事件
function _onScroll() {
iTop = document.documentElement.scrollTop + document.body.scrollTop;
getPageHeight();
if(((iTop+clientHeight)>parseInt(iHeight*0.99))||reachBottom()) {
var requestUrl = "<%=basePath%>ImageServlet?pageNum=" + pageNo + "&d=" + Math.random();
getResponseText(requestUrl);
}
};
//每2秒检测一次(具体自己调,之所以没用Window.scroll事件,
//是因为它没变化1px就触发一次,性能是个问题,
//而人为拖动控制不到每次只拖动1px,从而会出现有时候事件不触发的现象,所以我改成定时器实现
iIntervalId = setInterval("_onScroll();", 2000);
//图片预加载
function loadImage(url,alt, className,parent,callback) {
var img = new Image();
img.src = url;
img.alt = alt;
img.className = className;
if (img.complete) { //如果图片已经存在于浏览器缓存,直接调用回调函数
callback.call(img);
} else{
img.onload = function () { //图片下载完毕时异步调用callback函数。
callback.call(img);//将回调函数的this替换为Image对象
};
}
parent.appendChild(img);
return img;
};
//图片加载完毕后移除样式
function imgLoaded(){
this.className = "";
}
//页面初次加载时请求数据
window.onload = function(){
var requestUrl = "<%=basePath%>ImageServlet?pageNum=1&d=" + Math.random();
getResponseText(requestUrl);
}
</script>
<title>show</title>
</head>
<body>
<div class="wrap clearfix"></div>
<div style="width:960px;height:30px;text-align:center;">
<a href="javascript:void(0);">更多</a>
</div>
</body>
</html>
xmlhttprequest.js封装了获取xmlhttprequest对象的一个考虑浏览器兼容性函数,
//创建xmlHttpRequest对象
var xmlHttp;
function createXmlHttpRequest(){
if(window.ActiveXObject) {
//如果是IE,循环遍历版本
var xmlHttps = ["MSXML2.XMLHttp.5.0","MSXML2.XMLHttp4.0","MSXML2.XMLHttp3.0","MSXML2.XMLHttp","Microsoft.XMLHTTP"];
for(var i = 0; i < xmlHttps.length; i++)
{
try{
var xl = new ActiveXObject(xmlHttps[i]);
return xl;
}catch(error){
throw new Error("Create XmlHttpRequest failure!");
}
}
} else {
//火狐等其他浏览器
var xl = new XMLHttpRequest();
return xl;
}
}
Browser.js是用于判断浏览器类型的:
//判断浏览器
var Browser= new Object();
Browser.userAgent=window.navigator.userAgent.toLowerCase();
Browser.ie=/msie/.test(Browser.userAgent);
Browser.Moz=/gecko/.test(Browser.userAgent);
pageHeight.js用于计算当前页面元素所占的高度
// 取得当前页面显示所占用的高度
var clientHeight = 0;
var iHeight = 0;
function getPageHeight() {
if(document.body.clientHeight && document.documentElement.clientHeight) {
clientHeight = (document.body.clientHeight < document.documentElement.clientHeight) ? document.body.clientHeight : document.documentElement.clientHeight;
} else {
clientHeight = (document.body.clientHeight > document.documentElement.clientHeight) ? document.body.clientHeight : document.documentElement.clientHeight;
}
iHeight = Math.max(document.body.scrollHeight,document.documentElement.scrollHeight);
}
下面是index.jsp页面应用到的样式:
style.css文件
body{ padding:0px; margin:0px; background-color:#CCC;}
ul,li{ list-style:none; padding:0px; margin:0px;}
.clearfix:after{ content:"."; height:0; display:block; visibility:hidden; clear:both}
.clearfix{-height:1%; *min-height:1%}
.wrap{ width:960px; height:auto; margin:0px auto;}
.wrap ul{ width:200px; overflow:hidden; float:left; padding:10px 20px; background-color:#eeeeee; font-size:12px;}
.wrap ul li{ text-align:center; line-height:30px;}
img{
width:200px;
height:200px;
border:0px;
margin:0px;
}
/* ----------图片加载中样式begin---------- */
.loading{
background-image: url("../images/imgloading.gif");
background-repeat: no-repeat;
background-position: center 50%;
}
/* ----------图片加载中样式end---------- */
整个实现过程大概就这样,其实比较多的还是js操作。之所以没应用jquery,主要是考虑,为了应用它里面ajax函数就引入一个jquery类库,有点浪费,纯ajax写起来代码是多了点,但是多了解底层还是有帮助,不是么?代码贴完了,我该说的也说了,该贴的也贴出来了。
本文来源 我爱IT技术网 http://www.52ij.com/jishu/101.html 转载请保留链接。
- 评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)
-
