面向对象是一种优秀的编程思想,在JS中亦是如此,在大规模的项目中,体现的愈加突出。对于一些常用功能来说,我们完全可以将其开发成一个组件,比如JQuery中的弹窗组件等,我们可以很方便使用它们,使用时只需要按照API要求填写对应配置参数,即可迅速使用。本文给大家分享一个简单的照片墙组件。该照片墙组件完全原生JS实现,功能比较简单,但实现了一般照片墙的基本效果。我们可以通过构造函数PhotoWall()实例化一个对象,并调用init()方法进行初始化。对于布局样式要求极低,甚至完全不用设置。用户可以按照自身需求喜好随意放置照片墙的位置。预览最终效果以及下载文件,请点击这里。以下是使用该组件的具体实现方法:
首先进行一些简单的布局:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>动态照片墙</title>
<style type="text/css">
#photoList{width: 660px;position: relative;margin: 10px auto;}
#photoList li{float: left;width: 200px;height: 150px;margin: 10px;list-style: none;}
</style>
<script src="photoWall.js"></script>
<script type="text/javascript">
window.onload=function(){
//创建对象
var p1=new PhotoWall();
//初始化方法
p1.init({ //配置参数
ulId:'photoList',
btnId:'randomBtn',
speed:12
});
}
</script>
</head>
<body>
<!--随机按钮id取为'randomBtn',照片ul的id取为'photoWall'-->
<ul id="photoList">
<li><img src="img/1.png"/></li>
<li><img src="img/2.png"/></li>
<li><img src="img/3.png"/></li>
<li><img src="img/4.png"/></li>
<li><img src="img/5.png"/></li>
<li><img src="img/1.png"/></li>
<li><img src="img/2.png"/></li>
<li><img src="img/3.png"/></li>
<li><img src="img/4.png"/></li>
</ul>
<input type="button" name="btn1" id="randomBtn" value="随机" />
</body>
</html>
然后我们来看一下具体的JS实现代码:
/*
* 面向对象照片墙
* 使用构造函数PhotoWall()来创建一个对象
* 使用init()方法初始化
* init()发放接收默认配置参数形式为:
* init({
* ulId:'xxx',必填项,需初始化的对象ul的id
* btnId:null,选填项,随机按钮的id,多个照片墙不能绑定同一按钮
* speed:8 选填项,图片交换速度
* })
*/
//构造函数PhotoWall()
function PhotoWall(){
//用以设置每张图片的zIndex
this.izIndex=2;
//用以存放每张图片的初始位置
this.arr=[];
//用以保存鼠标在图片上的位置
this.disX=null;
this.disY=null;
//默认参数
this.settings={
ulId:'photoList',
btnId:null,
speed:8
};
}
/*
* init()初始化方法:
* 由于存在点击事件,所以使用This提前保存对象,用以修正this指向
* 调用exchange()方法用配置参数项修改默认参数
* 使用修改后的参数获取页面布局元素(ul、li)
* 如果随机按钮存在,则为随机按钮添加点击事件
* 为每一个li循环添加拖拽事件
*/
PhotoWall.prototype.init=function(opt){
var This=this;
this.exchange(this.settings,opt);
this.oUl=document.getElementById(this.settings.ulId);
this.aLi=this.oUl.getElementsByTagName('li');
if(this.settings.btnId){
this.oBtn=document.getElementById(this.settings.btnId);
this.oBtn.onclick=function(){This.randomBtn();};
}
for(var i=0;i<this.aLi.length;i++){
this.setList();
this.aLi[i].index=i;
this.drag(this.aLi[i]);
}
}
/*
* exchange()方法:
* for-in循环遍历配置参数
* 相同项将会使用配置参数进行覆盖
* 未设置的参数选项将会保持默认参数
*/
PhotoWall.prototype.exchange=function(obj1,obj2){
for(var attr in obj2){
obj1[attr]=obj2[attr];
}
}
/*
* setList()方法:
* CSS中初始为浮动定位,为每一个图片确定一个初始位置
* 第一个for循环,将每张图的初始位置以[left,top]的形式存于arr数组
* 第二个for循环,将每张图的浮动形式的位置转化为绝对定位的位置
* 由于转化时的值已经包含了margin值,故清除CSS的margin值
*/
PhotoWall.prototype.setList=function(){
for(var i=0;i<this.aLi.length;i++){
this.arr[i]=[this.aLi[i].offsetLeft,this.aLi[i].offsetTop];
}
for(var i=0;i<this.aLi.length;i++){
this.aLi[i].style.position='absolute';
this.aLi[i].style.left=this.arr[i][0]+'px';
this.aLi[i].style.top=this.arr[i][1]+'px';
this.aLi[i].style.margin=0;
}
}
/*
* randomBtn()方法:
* 定义数组arr2存放index索引,判断图片个数,进行push操作
* 目的是保证最终图片依旧按索引顺序排放,即1、2、3、4、5···
* 将数组索引随机排序,然后使用for循环按照数组顺序排序,并修正索引值
*/
PhotoWall.prototype.randomBtn=function(){
var arrRandom=new Array();
for(var i=0;i<this.aLi.length;i++){
arrRandom.push(i);
}
arrRandom.sort(function(){return Math.random()-0.5;})
for(var i=0;i<this.aLi.length;i++){
this.startMove(this.aLi[i],{ left: this.arr[arrRandom[i]][0] ,top:this.arr[arrRandom[i]][1]});
this.aLi[i].index=arrRandom[i];
}
}
/*
* drag()、mouseDown()、docMove()、docUp()
* 共同构成拖拽事件处理办法
*/
PhotoWall.prototype.drag=function(obj){
//为onmousedown事件修正this指向
var This=this;
//为对象添加onmousedown事件
obj.onmousedown=function(ev){
var ev=ev||event;
This.mouseDown(ev,obj);
return false;
};
}
//onmousedown处理方法
PhotoWall.prototype.mouseDown=function(ev,obj){
//为onmousemove事件修正this指向
var This=this;
//保存鼠标在图片上点击的位置
this.disX=ev.clientX-obj.offsetLeft;
this.disY=ev.clientY-obj.offsetTop;
//当前点击图片index加一,保证点击的图片处于最上层
obj.style.zIndex=this.izIndex++;
//点击对象后,为文档添加onmousemove事件
document.onmousemove=function(ev){
var ev=ev||event;
This.docMove(ev,obj);
};
//点击对象后,为文档添加onmouseup事件
document.onmouseup=function(){
This.docUp(obj);
};
}
//onmousemove处理方法
PhotoWall.prototype.docMove=function(ev,obj){
var ev=ev||event;
var n1=this.nears(obj);
//点击对象后,如果文档上发生onmousemove事件时,随之更改图片位置
obj.style.left=ev.clientX-this.disX+'px';
obj.style.top=ev.clientY-this.disY+'px';
//清除所有的图片红框
for(var i=0;i<this.aLi.length;i++){
this.aLi[i].style.border="";
}
//如果最近的图片存在,则为其添加红框
if(n1){
n1.style.border="1px solid red";
}
}
//onmouseup处理方法
PhotoWall.prototype.docUp=function(obj){
//鼠标抬起时,清除文档的onmousemove与onmouseup事件
document.onmousemove=null;
document.onmouseup=null;
//获取所有与当前对象发生碰撞中的距离最近的对象
var n1=this.nears(obj);
var temp=null;
//如果存在一个发生碰撞并且距离最近的元素,则与其交换位置,并交换索引值,清除红框
if(n1){
this.startMove(obj,{ left: this.arr[n1.index][0] ,top:this.arr[n1.index][1]});
this.startMove(n1,{ left: this.arr[obj.index][0] ,top:this.arr[obj.index][1]});
temp=obj.index;
obj.index=n1.index;
n1.index=temp;
n1.style.border="";
}
//否则回到自己原来位置
else{
this.startMove(obj,{left:this.arr[obj.index][0],top:this.arr[obj.index][1]});
}
}
/*
* nears()方法:
* 用于寻找距离最近的一个图片对象
* 所有距离值与value比较,一直保存最小的值,保存最小值的索引值
* 如果存在最小距离,则返回最小距离的这个图片对象
*/
PhotoWall.prototype.nears=function(obj){
var value=99999;
var iIndex=-1;
for(var i=0;i<this.aLi.length;i++){
if(this.pz(obj,this.aLi[i])&&obj!=this.aLi[i]){
var c=this.jl(obj,this.aLi[i]);
if(c<value){
value=c;
iIndex=i;
}
}
}
if(iIndex!=-1){
return this.aLi[iIndex];
}
else{
return false;
}
}
/*
* jl()方法:
* 勾股定理计算并返回两图片对象的中心距离
*/
PhotoWall.prototype.jl=function(obj1,obj2){
var a=obj1.offsetLeft-obj2.offsetLeft;
var b=obj1.offsetTop-obj2.offsetTop;
return Math.sqrt(a*a+b*b);
}
/*
* pz()方法
* 用以检测两对象是否发生碰撞,若碰撞则返回true
*/
PhotoWall.prototype.pz=function(obj1,obj2){
var t1=obj1.offsetTop;
var b1=obj1.offsetTop+obj1.offsetHeight;
var l1=obj1.offsetLeft;
var r1=obj1.offsetLeft+obj1.offsetWidth;
var t2=obj2.offsetTop;
var b2=obj2.offsetTop+obj2.offsetheight;
var l2=obj2.offsetLeft;
var r2=obj2.offsetLeft+obj2.offsetWidth;
if(r1<l2||l1>r2||t1>b2||b1<t2){
return false;
}
else{
return true;
}
}
/*
* startMove()与getStyle()方法为运动框架
* 接收两个个参数
* obj:运动的对象
* json:一个json值对,保存运动的目标点(left,top)
*/
PhotoWall.prototype.startMove=function(obj,json){
var This=this;
clearInterval(obj.timer);
obj.timer = setInterval(function(){
var bBtn = true;
for(var attr in json){
var iCur = 0;
if(attr == 'opacity'){
if(Math.round(parseFloat(This.getStyle(obj,attr))*100)==0){
iCur = Math.round(parseFloat(This.getStyle(obj,attr))*100);
}
else{
iCur = Math.round(parseFloat(This.getStyle(obj,attr))*100) || 100;
}
}
else{
iCur = parseInt(This.getStyle(obj,attr)) || 0;
}
var iSpeed = (json[attr] - iCur)/This.settings.speed;
iSpeed = iSpeed >0 ? Math.ceil(iSpeed) : Math.floor(iSpeed);
if(iCur!=json[attr]){
bBtn = false;
}
if(attr == 'opacity'){
obj.style.filter = 'alpha(opacity=' +(iCur + iSpeed)+ ')';
obj.style.opacity = (iCur + iSpeed)/100;
}
else{
obj.style[attr] = iCur + iSpeed + 'px';
}
}
if(bBtn){
clearInterval(obj.timer);
}
},30);
}
PhotoWall.prototype.getStyle=function(obj,attr){
if(obj.currentStyle){
return obj.currentStyle[attr];
}
else{
return getComputedStyle(obj,false)[attr];
}
}
这样一个简化的照片墙就算完成了,可以根据自己的需要进行添加方法。如有任何疑问,欢迎提出!