拖动+响应式效果:http://v.youku.com/v_show/id_XMTM0MjQyMTI0OA==.htmljavascript
两边对齐布局,即图片间间距一致,但左右两边的图片与边界的间距不必定等于图片间间距,兼容ie7,8,firefox,chrome.css
浏览器尺寸变化,在大于必定尺寸时,每行自动增长或减小图片,自动调整图片间间距,以知足两边对齐布局,这时每张图片尺寸固定(这里是200*200px);而小于必定尺寸时,每行图片数量固定(这里最小列数是3),这时图片老是等比例拉伸或缩放。html
浏览器不一样尺寸下,仍然能够拖动排序。java
图片,拖动代理里的图片始终保持等比例且水平垂直居中。git
拖动到相应位置时,位置左右的图片发生必定偏移。若是在最左边或最右边,则只是该行的第一张图片或最后一张图片发生偏移。github
支持多张图片拖动排序。web
<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>
这里要兼容低版本浏览器,因此列表li布局用的是inline-block.而两边对齐布局
-低版本:inline-block+text-align:justify
-现代:inline-block+flex-box
具体参见本屌的模拟flexbox justify-content的space-between
这里没有用flex-box的align-content:space-around
是由于没法经过text-align:justify
兼容低版本浏览器。text-align:justify
没法让最左边,最右边文字自动调整与box边的间距。即便在外面box添加padidng,好比:chrome
li{ margin:0 1%; ... } #wrap{ padding:0 1%; }
看起来好像是最左边,最右边与box边界的间距和li之间的间距同样,都是2%了。实际上,外面box设置的padding是永远不会变的,而li之间的margin是它们之间间距的最小值。若是全部li之间的间距都是1%,这时,一行上仍然有多余的空白,这些li会把空白均分了,这时它们之间的间距会大于1%.
具体的实现segmentfault
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
.后面能够看到单元格里面都是百分比,须要在外面限定最大尺寸。数组
具体参见本屌的css图片响应式+垂直水平居中
简单说,就是
添加一个“多余”的div,padding-top: 100%
,使得整个box响应式而且宽高比始终是1.
若是不考虑ie7,直接图片
img{ top: 0; bottom: 0; left: 0; right: 0; position:absolute; margin: auto; padding: auto; }
若是考虑ie7,
<p><img><i></i></p>
将上一点img样式添加到这里的p,而后
p{ top: 0; bottom: 0; left: 0; right: 0; position:absolute; margin: auto; padding: auto; } img{ display: inline-block; *display: inline; zoom:1; vertical-align: middle; } i{ display: inline-block; *display: inline; zoom:1; vertical-align: middle; height:100%; }
图片响应式
img{ max-width: 100%; max-height: 100%; }
google plus是按住ctrl,点击图片,完成多选,这里是点击"方框"(这里的<a class='no_selected'></a>
)。
点击后,把当前图片的index传给保存选中图片index的数组(这里的selected_index)。若是该index不存在,则添加;已存在,则删除。而"方框"此时根据数组中是否存在该index调整样式。
<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_photo.indexOf(el.src)>-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, selected_photo=photo_sort.selected_photo,//存储选中图片的名字(id) photo=photo_sort.photo_list[i].$model.src;//这里以图片的src为标志 if(selected_photo.indexOf(photo)==-1){//选中图片的index列表不存在,添加 selected_index.ensure(i); selected_photo.ensure(photo); }else{ selected_index.remove(i); selected_photo.remove(photo); } } });
图片的选中状态必须用selected_photo.indexOf(photo)==-1判断,最后会解释为何这样作.
这里用了遮罩层,并在上面绑定mousedown事件。
<a href="javascript:;" class="no_selected" ms-class-selected_icon='selected_photo.indexOf(el.src)>-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_photo:[],//选中图片的id列表 selected_index:[],//选中图片的index列表 drag_flag:false, sort_array:[],//范围列表, cell_size:0,//每一个单元格尺寸,这里宽高比为1 target_index:-1,//最终目标位置的index col_num:0,//列数 x_index:-1,//当前拖动位置的x方向index ... });
start_drag:function(e,index){ if(photo_sort.selected_index.size()){//有选中的图片 photo_sort.target_index=index;//避免用户没有拖动图片,但点击了图片,设置默认目标即当前点击图片 photo_sort.cell_size=this.clientWidth; var xx=e.clientX-photo_sort.cell_size/2,yy=e.clientY-photo_sort.cell_size/2;//点下图片,设置代理位置以点击点为中心 $('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;//设置代理中选择图片的数量 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;//将选中的图片中最后一张做为代理对象的"封面" 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++)//把一行图片里的每张图片中心坐标x方向的值做为分割点,添加到范围列表 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下拖动顺滑 e.stopPropagation(); e.preventDefault(); } }
鼠标点下,选中的图片的遮罩出现,这里是对其添加.photo_maskon
<div class='photo_mask' ms-class-photo_maskon='drag_flag&&selected_index.indexOf($index)>-1' ms-mousedown='start_drag($event,$index)'></div>
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);//把当前鼠标位置添加的范围列表 photo_sort.sort_array.sort(function(a,b){//对范围列表排序 return parseInt(a)-parseInt(b);//转为数值类型,不然会出现'1234'<'333' }); //从已排序的范围列表中找出当前鼠标位置的index,即目标位置水平方向的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;//目标在全部图片中的index if(photo_sort.target_index>size)//目标位置越界 photo_sort.target_index=size; photo_sort.sort_array.remove(offsetX);//移除当前位置 $('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(); }
几点说明
关于当前拖动到的位置断定
图中每一个单元格的竖线,在水平方向把单元格分为两边。每一个竖线把一行分为5部分,判断的时候,看鼠标当前的e.clientX
在5个部分里的哪一部分。
这里在判断的时候用了排序。具体的,把每一个竖线的x坐标和当前鼠标位置的x坐标保存到数组(这里的sort_array
),排好序,而后indexOf
看当前鼠标位置的x坐标在数组中的位置,便可获得当前拖动的目标位置。
若是不用排序的话,代码会像这样
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;
后面删除当前鼠标位置的x坐标,空出位置,留给下一次mousemove事件的x坐标。
关于当前拖动的目标位置左右的图片发生必定偏移,无非就是对目标位置左右的图片加上相应的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>
这里须要注意,当代理拖动到最左边或最右边时,因为布局是inline-block
,此时目标位置所在行的上一行(若是有)的最后一个单元格或下一行(若是有)的第一个单元格也会发生偏移。
解决方法是设置变量x_index
,表示单元格在x方向的index.在添加偏移class的时候,增长断定条件。
<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>
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){//目标位置在选中图片以后 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{//目标位置在选中图片以前 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.photo_list=data;//更新数据 photo_sort.target_index=-1;//各类重置,初始化 photo_sort.sort_array=[]; photo_sort.col_num=0; photo_sort.x_index=-1; photo_sort.selected_photo.clear(); photo_sort.selected_index.clear(); $('drag_proxy').style.display='none'; photo_sort.drag_flag=false; avalon.unbind(document,'mouseup'); if(isIE) target.releaseCapture(); } }
这里主要就是对图片列表的重排。
目标位置在选中图片以前
先把原始图片保存在temp
,而后把从目标位置图片到原始图片前一位置的图片,依次后移一个位置,最后把temp
放到目标位置。
目标位置在选中图片以后
和上面差很少,只不过这里是把从目标位置图片到原始图片后一位置的图片,依次前移一个位置。
说明
不能像data[j]=data[j+1]
这样赋值,进而更新视图。由于avalon不支持单个转换,若是想更新,须要将整个子VM从新赋以一个新的对象。也就是photo_sort.photo_list=sortedData
从新赋值,更新视图。
前面判断图片选中状态为何用selected_photo.indexOf(photo)
,而不是selected_index.indexOf(i)
,是由于更新视图后,avalon不能自动更新当前图片的index,也就是说若是图片一出来就是第一张,那它的index就永远是0,不会跟着它的位置改变。
这里为了方便查看顺序,稍微修改了下html.
能够看到,拖动第2,3张
图片到第8,9张
图片之间,结果应该是第8张
图片在第二行最左边,而后向右依次是第2,3张
图片,最后是第9张
图片.而这里显然不是想要的结果。
出现这个问题的缘由在于
依次移动目标图片到目标位置,对多张目标图片,会有一个移动前后的考量。具体的,
假设上图要移动的目标图片是第2,3张
图片,先移动第2张
图片到目标位置,这时第3张
图片会在第2张
原来的位置上,这时成为第2张
。可是前面,
function onMouseUp(target){ if(photo_sort.drag_flag){ for(var i=0,len=photo_sort.selected_index.size();i<len;i++){//遍历选中图片 ... } ... } }
仍是在依次遍历目标图片,selected_index=[2,3];
,下一个会遍历如今的第3张
图片.
解决方法很容易想到,就是先移动第3张
图片,后移动第2张
图片.
上面的例子是目标位置在选中图片以后
,固然目标位置在选中图片以前
也存在这个问题。
具体到代码上
function onMouseUp(target){ if(photo_sort.drag_flag){ photo_sort.selected_index.sort(function(a,b){//对范围列表排序 return parseInt(a)-parseInt(b); }); var size=photo_sort.selected_index.size(); var data=photo_sort.photo_list,target_index=photo_sort.target_index, pos_arr=photo_sort.selected_index.$model, result=data.slice(0,data.size()); pos_arr.push(target_index);//pos_arr存放选中的目标图片+目标位置,并排好序 pos_arr.sort(function(a,b){//对范围列表排序 return parseInt(a)-parseInt(b); }); var target_pos=pos_arr.indexOf(target_index),temp; //目标位置在选中图片以后,从目标位置开始,依次向前遍历目标图片 for(var i=target_pos-1;i>=0;i--){ var item_index=pos_arr[i]; temp=data[item_index].$model; for(var j=item_index;j<target_index;j++){ data[j].$model=data[j+1].$model; } data[target_index-1].$model=temp; } //目标位置在选中图片以前,从目标位置开始,依次向后遍历目标图片 for(var i=target_pos+1;i<pos_arr.length;i++){ var item_index=pos_arr[i]; temp=data[item_index].$model; for(var j=item_index;j>target_index;j--) data[j].$model=data[j-1].$model; data[target_index].$model=temp; } photo_sort.photo_list=data;//更新数据 ... } }
实际上就是以目标位置为中心,向左右两边遍历选中图片
。具体的
选中图片是第1,2,8,9张图片
,目标位置是4
.这时pos_arr=[1,2,4,8,9];
,而后先遍历4
以前的选中图片,2->1
,而后是4
以后的选中图片,8->9
.这样就避免了移动顺序问题。
事实上,google plus在细节上还作了
框选图片
若是有滚动条,且拖动位置快要超出当前界面,滚动条会自动上移或下移。
这两个本屌就不作了,原理也是很简单的。