avalon js实现仿google plus图片多张拖动排序附源码下载

源码下载:http://xiazai.jb51.net/201509/yuanma/drag_sort1(jb51.net).rar

效果展示如下:

google plus

拖动+响应式效果:

要求

1. 两边对齐布局,即图片间间距一致,但左右两边的图片与边界的间距不一定等于图片间间距,兼容ie7,8,firefox,chrome.
2. 浏览器尺寸变化,在大于一定尺寸时,每行自动增加或减少图片,自动调整图片间间距,以满足两边对齐布局,这时每张图片尺寸固定(这里是200*200px);而小于一定尺寸时,每行图片数量固定(这里最小列数是3),这时图片总是等比例拉伸或缩放。
3. 浏览器不同尺寸下,仍然可以拖动排序。
4. 图片,拖动代理里的图片始终保持等比例且水平垂直居中。
5. 拖动到相应位置时,位置左右的图片发生一定偏移。如果在最左边或最右边,则只是该行的第一张图片或最后一张图片发生偏移。
6. 支持多张图片拖动排序。

实现

布局及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.而两边对齐布局

-低版本: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,比如:

li{
 margin:0 1%;
 ...
}
#wrap{
 padding:0 1%;
}

看起来好像是最左边,最右边与box边界的间距和li之间的间距一样,都是2%了。实际上,外面box设置的padding是永远不会变的,而li之间的margin是它们之间间距的最小值。如果所有li之间的间距都是1%,这时,一行上仍然有多余的空白,这些li会把空白均分了,这时它们之间的间距会大于1%.
具体的实现

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图片响应式+垂直水平居中

选中图片

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_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,//每个单元格尺寸,这里宽高比为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>

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);//把当前鼠标位置添加的范围列表
  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>

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){//目标位置在选中图片之后
      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.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();
   }
  }

这里主要就是对图片列表的重排。
- 目标位置在选中图片之前

先把原始图片保存在`temp`,然后把从目标位置图片到原始图片前一位置的图片,依次后移一个位置,最后把`temp`放到目标位置。
- 目标位置在选中图片之后

和上面差不多,只不过这里是把从目标位置图片到原始图片后一位置的图片,依次前移一个位置。

注意

不能像`data[j]=data[j+1]`这样赋值,因为avalon不支持单个转换,如果想更新,需要将整个子VM重新赋以一个新的对象。也就是定义一个arr,然后从头开始向里面添加model,最后`photo_sort.photo_list.clear()`删除所有图片,`photo_sort.photo_list=arr`重新赋值,更新视图。

后记

事实上,google plus在细节上还做了
- 框选图片
- 如果有滚动条,且拖动位置快要超出当前界面,滚动条会自动上移或下移。
这两个本屌就不做了,原理也是很简单的。

(0)

相关推荐

  • js实现图片拖动改变顺序附图

    在web页面中,需要改变多个元素的位置,可以通过元素拖动来实现.HTML5中加入了一个全局属性draggable,通过设置true/false来控制元素是否可拖动. 下面以图片拖动为例,用jQuery来实现:页面上有多个图片,把一个图片拖动到其他两个图片中间,就可以将这个图片的位置插入到两图之间. 复制代码 代码如下: <!DOCTYPE html> <html> <head> <style> .img-div img { width:200px; heig

  • js用拖动滑块来控制图片大小的方法

    本文实例讲述了js用拖动滑块来控制图片大小的方法.分享给大家供大家参考.具体实现方法如下: 复制代码 代码如下: <!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <meta http-equiv=Content-Type content="text/html;charset=gb2312"> <title&g

  • js解决movebox移动问题

    本文为大家分享了js解决movebox移动问题,并且取消图片默认拖动事件的相关操作,供大家参考,具体内容如下 html: <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <link href="../Content/StyleSheet5.css"

  • avalon js实现仿微博拖动图片排序

    下文针对仿微博图片随意拖动,调整图片的顺序,讲解的很详细,文章肯定还有欠缺的地方,欢迎提出批评改正.废话不多说了,看具体内容吧. 点击此处进入源码下载 什么是拖动图片排序? 就像微博这种,上传后允许用户通过拖动图片,调整几张图片的顺序. 可以看到微博在这里把每张图片固定了尺寸,稍微严谨点的话,就需要像上一篇文章那样,外面是响应式的等高等宽的若干div容器,里面则是等比例缩放的响应式图片. 下面说下要求. 1.当然首先图片要可以拖动. 2.图片移出其原本的位置,拖动到目标位置并且未松开鼠标完成拖动

  • js实现鼠标拖动图片并兼容IE/FF火狐/谷歌等主流浏览器

    注意使用preventDefault防止浏览器的默认事件操作发生 复制代码 代码如下: <script language="javascript" type="text/javascript"> var isDrag = false; function isIE(){ if(navigator.userAgent.indexOf("MSIE")>0){return true;} else{return false;} } fun

  • avalonjs实现仿微博的图片拖动特效

    效果: HTML: <div id='post_img' ms-controller='post_img'> <ul id='post_img_inner' ms-mousemove='onmousemove'> <li ms-repeat-el="post_img_list" class='inline-block' ms-mousedown='onmousedown($event,$index,el)' ms-attr-id='post_img_ite

  • div拖拽插件——JQ.MoveBox.js(自制JQ插件)

    有一段时间没更新博客了,都不知道忙些什么,学习也没什么进展,惭愧. 这一周空闲的时间学着自己写一下JQ插件. 以前用原生的JS做过类似拖拽div的效果,现在按原思路改做成一个JQ的小插件,当作制作JQ插件的一个小练习. html为 复制代码 代码如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transit

  • avalon js实现仿google plus图片多张拖动排序附源码下载

    源码下载:http://xiazai.jb51.net/201509/yuanma/drag_sort1(jb51.net).rar 效果展示如下: google plus 拖动+响应式效果: 要求 1. 两边对齐布局,即图片间间距一致,但左右两边的图片与边界的间距不一定等于图片间间距,兼容ie7,8,firefox,chrome. 2. 浏览器尺寸变化,在大于一定尺寸时,每行自动增加或减少图片,自动调整图片间间距,以满足两边对齐布局,这时每张图片尺寸固定(这里是200*200px):而小于一定

  • JS实现快速的导航下拉菜单动画效果附源码下载

    这是一个带变形动画特效的下拉导航菜单特效.该导航菜单在菜单项之间切换时,下拉菜单会快速的根据菜单内容的大小来动态变形,显示合适的下拉菜单大小,效果非常棒. 查看演示     下载源码 HTML 该导航菜单的HTML结构如下: <header class="cd-morph-dropdown"> <a href="#0" class="nav-trigger">Open Nav<span aria-hidden=&qu

  • 仿Aspnetpager的一个PHP分页类代码 附源码下载

    基本逻辑思路和.net的一样,就是将通过实体类来进行配置换成了通过数组进行配置,逻辑比较简单,根据条件判断拼接分页html. 有以下几个简单的功能: 1:支持相关按钮的显示与否配置 2:支持每页数目,文本名称,html标签类名称的自由配置 3:支持url重写过的页面(需自己在配置数组中添加重写规则) 简单吧,还是直接上代码: 核心代码:pager.class.php 复制代码 代码如下: <?php class pager{ //分页的参数配置 private $config=array( //

  • JS实现移动端按首字母检索城市列表附源码下载

    我们常见的手机通讯录或微信通讯录,联系人信息是按字母顺序排列的列表,通过点击右侧的字母,会迅速定位检索到首字母对应的联系人.那么我今天给大家介绍的是按首字母快速定位到城市列表,效果和通讯录一样的.  查看演示 下载源码 准备 查看演示     下载源码 准备 首先我们需要用到全国的城市数据,这些城市数据来源于网络,我已经将数据格式化成JSON形式了,大家可以直接拿去用. 我们还需要用到一个叫better-scroll的滚动插件,它能帮我们将超长的页面原生的滚动条处理掉,优化滚动效果. 接着我们准

  • JS实现的简单拖拽购物车功能示例【附源码下载】

    本文实例讲述了JS实现的简单拖拽购物车功能.分享给大家供大家参考,具体如下: <html> <head> <meta charset="utf-8" /> <title>使用拖放API将商品拖入购物车</title> <style> body { font-size:12px } .liT{ border-bottom:solid 1px #ccc; background-color:#eee; font-weig

  • jQuery插件ImageDrawer.js实现动态绘制图片动画(附源码下载)

    ImageDrawer.js是一款可以实现动态绘制图片动画的jQuery插件.通过ImageDrawer.js插件,你可以制作在页面中绘制图片的动态过程,你可以控制绘制动画的持续时间等参数,非常有趣. 效果展示       源码下载 使用方法 使用该动态绘制图片插件需要在页面中引入imagedrawer.css,jquery和imagedrawer.js文件. <link rel="stylesheet" href="css/imagedrawer.css"

  • 基于Jquery制作图片文字排版预览效果附源码下载

    基于jQuery图文排版图片预览特效.这是一款基于jQuery+CSS3实现的鼠标点击图片弹出画廊切换特效. 效果图如下所示,怎么样感兴趣吗,感兴趣的朋友继续往下学习哦. 效果演示    源码下载 html代码: <div id="fullscreen"> <div id="fullscreen-inner"> <div id="fullscreen-inner-left" class="fullscree

  • 一款简单的jQuery图片标注效果附源码下载

    为了提高用户体验度,在一些电商网站和家居网站上我们会看到,一张图片中显示多种商品,点击每个商品可以弹出对应商品的简单介绍,包括价格等等,设计非常的人性化,今天我们通过实例给大家分享使用jQuery插件来实现这一效果. 先给大家展示下效果图: 效果演示          源码下载 HTML 首先,我们加载jQuery库和easypin插件. <script src="jquery.min.js"></script> <script src="jqu

  • ASP.NET图片上传实例(附源码)

    由于需要图片上传的功能,所以花了一些时间网上找相关资料终于搞定,效果图如下: 下面的是解决方案截图和上传的图片截图: 下面是代码: 1.界面代码 <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="UploadPic.aspx.cs" Inherits="Pic_Try.UploadPic" %> <!DOCTYPE html PUBLIC &q

  • 基于JS快速实现导航下拉菜单动画效果附源码下载

    这是一个带变形动画特效的下拉导航菜单特效.该导航菜单在菜单项之间切换时,下拉菜单会快速的根据菜单内容的大小来动态变形,显示合适的下拉菜单大小,效果非常棒. 快速的导航下拉菜单动画效果如下所示: 效果演示         源码下载 HTML 该导航菜单的HTML结构如下: <header class="cd-morph-dropdown"> <a href="#0" class="nav-trigger">Open Nav&

随机推荐