avalon js實(shí)現(xiàn)仿google plus圖片多張拖動(dòng)排序附源碼下載
源碼下載:http://xiazai.jb51.net/201509/yuanma/drag_sort1(jb51.net).rar
效果展示如下:
google plus


拖動(dòng)+響應(yīng)式效果:

要求
1. 兩邊對(duì)齊布局,即圖片間間距一致,但左右兩邊的圖片與邊界的間距不一定等于圖片間間距,兼容ie7,8,firefox,chrome.
2. 瀏覽器尺寸變化,在大于一定尺寸時(shí),每行自動(dòng)增加或減少圖片,自動(dòng)調(diào)整圖片間間距,以滿足兩邊對(duì)齊布局,這時(shí)每張圖片尺寸固定(這里是200*200px);而小于一定尺寸時(shí),每行圖片數(shù)量固定(這里最小列數(shù)是3),這時(shí)圖片總是等比例拉伸或縮放。
3. 瀏覽器不同尺寸下,仍然可以拖動(dòng)排序。
4. 圖片,拖動(dòng)代理里的圖片始終保持等比例且水平垂直居中。
5. 拖動(dòng)到相應(yīng)位置時(shí),位置左右的圖片發(fā)生一定偏移。如果在最左邊或最右邊,則只是該行的第一張圖片或最后一張圖片發(fā)生偏移。
6. 支持多張圖片拖動(dòng)排序。
實(shí)現(xiàn)
布局及css
<div id='wrap'>
<ul class='justify'>
<li>
<a href="javascript:;" class="no_selected"></a>
<div class='photo_mask'></div>
<div>
<div class="dummy"></div>
<p><img><i></i></p>
</div>
</li>
<li class='justify_fix'></li>
</ul>
</div>
inline-block+flex-box+text-align:justify
這里要兼容低版本瀏覽器,所以列表li布局用的是inline-block.而兩邊對(duì)齊布局
-低版本:inline-block+`text-align:justify`
-現(xiàn)代:inline-block+`flex-box`
具體參見本屌的模擬flexbox justify-content的space-between
這里沒有用flex-box的`align-content:space-around`是因?yàn)闊o法通過`text-align:justify`兼容低版本瀏覽器。
`text-align:justify`無法讓最左邊,最右邊文字自動(dòng)調(diào)整與box邊的間距。即使在外面box添加padidng,比如:
li{
margin:0 1%;
...
}
#wrap{
padding:0 1%;
}
看起來好像是最左邊,最右邊與box邊界的間距和li之間的間距一樣,都是2%了。實(shí)際上,外面box設(shè)置的padding是永遠(yuǎn)不會(huì)變的,而li之間的margin是它們之間間距的最小值。如果所有l(wèi)i之間的間距都是1%,這時(shí),一行上仍然有多余的空白,這些li會(huì)把空白均分了,這時(shí)它們之間的間距會(huì)大于1%.
具體的實(shí)現(xiàn)
li{
list-style-type: none;
display:inline-block;
*display: inline;
zoom:1;
max-width: 200px;
max-height: 200px;
width: 28%;
border:1px solid red;
position: relative;
overflow: hidden;
margin:10px 2%;
}
li[class='justify_fix']{
border:none;
}
.justify {
display: flex;
align-items: flex-start;
flex-flow: row wrap;
justify-content: space-between;
text-align: justify;
text-justify: inter-ideograph;
*zoom: 1;
-moz-text-align-last: justify;
-webkit-text-align-last: justify;
text-align-last: justify;
}
@media (-webkit-min-device-pixel-ratio:0) {
.justify:after {
content: "";
display: inline-block;
width: 100%;
}
}
這里要加上`max-width`,`max-height`.后面可以看到單元格里面都是百分比,需要在外面限定最大尺寸。
圖片響應(yīng)式+水平垂直居中
具體參見本屌的css圖片響應(yīng)式+垂直水平居中
選中圖片
google plus是按住ctrl,點(diǎn)擊圖片,完成多選,這里是點(diǎn)擊"方框"(這里的`<a class='no_selected'></a>`)。
點(diǎn)擊后,把當(dāng)前圖片的index傳給保存選中圖片index的數(shù)組(這里的selected_index)。如果該index不存在,則添加;已存在,則刪除。而"方框"此時(shí)根據(jù)數(shù)組中是否存在該index調(diào)整樣式。
<div id='wrap' ms-controller='photo_sort'>
<ul class='justify'>
<li ms-repeat='photo_list'>
<a href="javascript:;" class="no_selected" ms-class-selected_icon='selected_index.indexOf($index)>-1' ms-click='select($index)'></a>
...
</li>
<li class='justify_fix'></li>
</ul>
</div>
var photo_sort=avalon.define({
selected_index:[],//選中圖片的index列表,
...
select:function(i){
var selected_index=photo_sort.selected_index;
if(selected_index.indexOf(i)==-1)//選中圖片的index列表不存在,添加
photo_sort.selected_index.ensure(i);
else
photo_sort.selected_index.remove(i);
}
});
mousedown
這里用了遮罩層,并在上面綁定mousedown事件。
<a href="javascript:;" class="no_selected" ms-class-selected_icon='selected_index.indexOf($index)>-1' ms-click='select($index)'></a>
<div class='photo_mask' ms-mousedown='start_drag($event,$index)'></div>
var photo_sort=avalon.define({
$id:'photo_sort',
photo_list:[],//圖片列表
selected_index:[],//選中圖片的index列表
drag_flag:false,
sort_array:[],//范圍列表,
cell_size:0,//每個(gè)單元格尺寸,這里寬高比為1
target_index:-1,//最終目標(biāo)位置的index
col_num:0,//列數(shù)
x_index:-1,//當(dāng)前拖動(dòng)位置的x方向index
...
});
start_drag:function(e,index){
if(photo_sort.selected_index.size()){//有選中的圖片
photo_sort.target_index=index;//避免用戶沒有拖動(dòng)圖片,但點(diǎn)擊了圖片,設(shè)置默認(rèn)目標(biāo)即當(dāng)前點(diǎn)擊圖片
photo_sort.cell_size=this.clientWidth;
var xx=e.clientX-photo_sort.cell_size/2,yy=e.clientY-photo_sort.cell_size/2;//點(diǎn)下圖片,設(shè)置代理位置以點(diǎn)擊點(diǎn)為中心
$('drag_proxy').style.top=yy+avalon(window).scrollTop()+'px';
$('drag_proxy').style.left=xx+'px';
$('drag_proxy').style.width=photo_sort.cell_size+'px';
$('drag_proxy').style.height=photo_sort.cell_size+'px';
drag_proxy.select_num=photo_sort.selected_index.length;//設(shè)置代理中選擇圖片的數(shù)量
if(drag_proxy.select_num>0){
var drag_img=photo_sort.photo_list[photo_sort.selected_index[drag_proxy.select_num-1]];
drag_proxy.src=drag_img.src;//將選中的圖片中最后一張作為代理對(duì)象的"封面"
photo_sort.drag_flag=true;
$('drag_proxy').style.display='block';
}
//cell_gap:圖片間間距,first_gap:第一張圖片和外部div間間距
var wrap_width=avalon($('wrap')).width(),wrap_offset=$('wrap').offsetLeft,first_left=$('wrap_photo0').offsetLeft,
second_left=$('wrap_photo1').offsetLeft,first_gap=first_left-wrap_offset,cell_gap=second_left-first_left;
photo_sort.col_num=Math.round((wrap_width-2*first_gap+(cell_gap-photo_sort.cell_size))/cell_gap);
for(var i=0;i<photo_sort.col_num;i++)//把一行圖片里的每張圖片中心坐標(biāo)x方向的值作為分割點(diǎn),添加到范圍列表
photo_sort.sort_array.push(first_gap+cell_gap*i+photo_sort.cell_size/2);
var target=this.parentNode;
avalon.bind(document,'mouseup',function(e){
onMouseUp(target);
});
if(isIE)
target.setCapture();//讓ie下拖動(dòng)順滑
e.stopPropagation();
e.preventDefault();
}
}
鼠標(biāo)點(diǎn)下,選中的圖片的遮罩出現(xiàn),這里是對(duì)其添加`.photo_maskon`
<div class='photo_mask' ms-class-photo_maskon='drag_flag&&selected_index.indexOf($index)>-1' ms-mousedown='start_drag($event,$index)'></div>
mousemove
drag_move:function(e){
if(photo_sort.drag_flag){
var xx=e.clientX,yy=e.clientY,offset=avalon($('wrap')).offset();
var offsetX=xx-offset.left,offsetY=yy-offset.top;
photo_sort.sort_array.push(offsetX);//把當(dāng)前鼠標(biāo)位置添加的范圍列表
photo_sort.sort_array.sort(function(a,b){//對(duì)范圍列表排序
return parseInt(a)-parseInt(b);//轉(zhuǎn)為數(shù)值類型,否則會(huì)出現(xiàn)'1234'<'333'
});
//從已排序的范圍列表中找出當(dāng)前鼠標(biāo)位置的index,即目標(biāo)位置水平方向的index
var x_index=photo_sort.sort_array.indexOf(offsetX),y_index=Math.floor(offsetY/(photo_sort.cell_size+20)),
size=photo_sort.photo_list.size();
photo_sort.x_index=x_index;
photo_sort.target_index=photo_sort.col_num*y_index+x_index;//目標(biāo)在所有圖片中的index
if(photo_sort.target_index>size)//目標(biāo)位置越界
photo_sort.target_index=size;
photo_sort.sort_array.remove(offsetX);//移除當(dāng)前位置
$('drag_proxy').style.top=avalon(window).scrollTop()+yy-photo_sort.cell_size/2+'px';
$('drag_proxy').style.left=xx-photo_sort.cell_size/2+'px';
}
e.stopPropagation();
}
幾點(diǎn)說明
- 關(guān)于當(dāng)前拖動(dòng)到的位置判定

圖中每個(gè)單元格的豎線,在水平方向把單元格分為兩邊。每個(gè)豎線把一行分為5部分,判斷的時(shí)候,看鼠標(biāo)當(dāng)前的`e.clientX`在5個(gè)部分里的哪一部分。
- 這里在判斷的時(shí)候用了排序。具體的,把每個(gè)豎線的x坐標(biāo)和當(dāng)前鼠標(biāo)位置的x坐標(biāo)保存到數(shù)組(這里的`sort_array`),排好序,然后`indexOf`看當(dāng)前鼠標(biāo)位置的x坐標(biāo)在數(shù)組中的位置,即可得到當(dāng)前拖動(dòng)的目標(biāo)位置。
如果不用排序的話,代碼會(huì)像這樣
var target;
if(x>50+50){
if(x>3*100+3*100+50+50){//最后一部分
target=4;
}else{
target=(x-50-50)/(50+100+50);
}
}else
target=0;
- 后面刪除當(dāng)前鼠標(biāo)位置的x坐標(biāo),空出位置,留給下一次mousemove事件的x坐標(biāo)。
- 關(guān)于當(dāng)前拖動(dòng)的目標(biāo)位置左右的圖片發(fā)生一定偏移,無非就是對(duì)目標(biāo)位置左右的圖片加上相應(yīng)的class.
.prev{
right: 40px;
}
.next{
left: 40px;
}
<div id='wrap' ms-controller='photo_sort'>
<ul class='justify' ms-mousemove='drag_move($event)'>
<li ms-repeat='photo_list' ms-attr-id='wrap_photo{{$index}}' ms-class-prev='$index==target_index-1'
ms-class-next='$index==target_index'>
...
</li>
<li class='justify_fix'></li>
</ul>
</div>
這里需要注意,當(dāng)代理拖動(dòng)到最左邊或最右邊時(shí),由于布局是`inline-block`,此時(shí)目標(biāo)位置所在行的上一行(如果有)的最后一個(gè)單元格或下一行(如果有)的第一個(gè)單元格也會(huì)發(fā)生偏移。

解決方法是設(shè)置變量`x_index`,表示單元格在x方向的index.在添加偏移class的時(shí)候,增加判定條件
<li ms-repeat='photo_list' ms-attr-id='wrap_photo{{$index}}' ms-class-prev='$index==target_index-1&&x_index>0'
ms-class-next='$index==target_index&&x_index<col_num'>
...
</li>
mouseup
function onMouseUp(target){
if(photo_sort.drag_flag){
for(var i=0,len=photo_sort.selected_index.size();i<len;i++){//遍歷選中圖片
var item_index=photo_sort.selected_index[i],data=photo_sort.photo_list,
target_index=photo_sort.target_index,temp;
if(item_index<target_index){//目標(biāo)位置在選中圖片之后
temp=data[item_index].src;
for(var j=item_index;j<target_index;j++)
data[j].src=data[j+1].src;
data[target_index-1].src=temp;
}else{//目標(biāo)位置在選中圖片之前
temp=data[item_index].src;
for(var j=item_index;j>target_index;j--)
data[j].src=data[j-1].src;
data[target_index].src=temp;
}
}
photo_sort.target_index=-1;//各種重置,初始化
photo_sort.sort_array=[];
photo_sort.col_num=0;
photo_sort.x_index=-1;
photo_sort.selected_index=[];
$('drag_proxy').style.display='none';
photo_sort.drag_flag=false;
avalon.unbind(document,'mouseup');
if(isIE)
target.releaseCapture();
}
}
這里主要就是對(duì)圖片列表的重排。
- 目標(biāo)位置在選中圖片之前

先把原始圖片保存在`temp`,然后把從目標(biāo)位置圖片到原始圖片前一位置的圖片,依次后移一個(gè)位置,最后把`temp`放到目標(biāo)位置。
- 目標(biāo)位置在選中圖片之后

和上面差不多,只不過這里是把從目標(biāo)位置圖片到原始圖片后一位置的圖片,依次前移一個(gè)位置。
注意
不能像`data[j]=data[j+1]`這樣賦值,因?yàn)閍valon不支持單個(gè)轉(zhuǎn)換,如果想更新,需要將整個(gè)子VM重新賦以一個(gè)新的對(duì)象。也就是定義一個(gè)arr,然后從頭開始向里面添加model,最后`photo_sort.photo_list.clear()`刪除所有圖片,`photo_sort.photo_list=arr`重新賦值,更新視圖。
后記
事實(shí)上,google plus在細(xì)節(jié)上還做了
- 框選圖片
- 如果有滾動(dòng)條,且拖動(dòng)位置快要超出當(dāng)前界面,滾動(dòng)條會(huì)自動(dòng)上移或下移。
這兩個(gè)本屌就不做了,原理也是很簡單的。
相關(guān)文章
JS實(shí)現(xiàn)當(dāng)前頁居中分頁效果的方法
這篇文章主要介紹了JS實(shí)現(xiàn)當(dāng)前頁居中分頁效果的方法,涉及javascript操作頁面元素與樣式的相關(guān)技巧,需要的朋友可以參考下2015-06-06
javascript 動(dòng)態(tài)樣式添加的簡單實(shí)現(xiàn)
下面小編就為大家?guī)硪黄猨avascript 動(dòng)態(tài)樣式添加的簡單實(shí)現(xiàn)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-10-10
JavaScript中的回調(diào)函數(shù)實(shí)例講解
今天小編就為大家分享一篇關(guān)于JavaScript中的回調(diào)函數(shù)實(shí)例講解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-01-01
javascript創(chuàng)建對(duì)象的3種方法
這篇文章主要介紹了javascript創(chuàng)建對(duì)象的3種方法,對(duì)比分析js創(chuàng)建對(duì)象三種方式的優(yōu)缺點(diǎn),感興趣的小伙伴們可以參考一下2016-11-11
JavaScript對(duì)象與JSON格式的轉(zhuǎn)換及JSON.stringify和JSON.parse的使用方法
這篇文章主要介紹了JavaScript對(duì)象與JSON格式的轉(zhuǎn)換及JSON.stringify和JSON.parse的使用方法,JSON是JavaScript表達(dá)值和對(duì)象的通用數(shù)據(jù)格式,其本質(zhì)就是符合一定規(guī)范的字符串2022-07-07
深入學(xué)習(xí)JavaScript執(zhí)行上下文
這篇文章主要介紹了深入學(xué)習(xí)JavaScript執(zhí)行上下文,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下,希望對(duì)你的學(xué)習(xí)有所幫助2022-08-08
JavaScript面試開發(fā)常用的知識(shí)點(diǎn)總結(jié)
這篇文章主要為大家詳細(xì)總結(jié)了JavaScript面試開發(fā)常用的知識(shí)點(diǎn),感興趣的小伙伴們可以參考一下2016-08-08
js實(shí)現(xiàn)鼠標(biāo)經(jīng)過時(shí)圖片滾動(dòng)停止的方法
這篇文章主要介紹了js實(shí)現(xiàn)鼠標(biāo)經(jīng)過時(shí)圖片滾動(dòng)停止的方法,可實(shí)現(xiàn)js滾動(dòng)特效中的鼠標(biāo)懸停停止圖片滾動(dòng)的功能,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-02-02

