murri
github: https://github.com/haltu/muuri
安装
npm install murri —save
如果需要drag,需要依赖hammer.js,可以通过js script引入:
<script src="https://unpkg.com/hammerjs@2.0.8/hammer.min.js"></script>
使用
HTML
最少需要class为grid、item、item-content的div,其中grid的作用是自适应布局,item是区分每个元素,item-content用于动画设置;
<div class="grid">
<div class="item">
<div class="item-content">
<!-- Safe zone, enter your custom markup -->
This can be anything.
<!-- Safe zone ends -->
</div>
</div>
<div class="item">
<div class="item-content">
<!-- Safe zone, enter your custom markup -->
<div class="my-custom-content">
Yippee!
</div>
<!-- Safe zone ends -->
</div>
</div>
<div>
CSS
grid必须为relative,item必须为block以及absolute;另外item之间可以设置gap
.grid {
position: relative;
}
.item {
display: block;
position: absolute;
width: 48%;
height: 200px;
padding: 5px;
margin: 10px;
z-index: 1;
background: #ccc;
color: #fff;
}
.item.muuri-item-dragging {
z-index: 3;
}
.item.muuri-item-releasing {
z-index: 2;
}
.item.muuri-item-hidden {
z-index: 0;
}
.item-content {
position: relative;
width: 100%;
height: 100%;
}
Javascript
Muuri是一个构造函数,第一个参数接受一个元素或者字符串(内置了querySelector):
var grid = new Muuri('.grid');
如上是最简单的方式,这样就可以看到效果了,如果希望拖拽,可以做如下配置:
var grid = new Muuri('.grid’, {
dragEnabled: true
});
即Muuri的第二个参数是一个object,默认的dragEnabled为false,所以如果希望拖动,需要设置为true。
配置
如果不做任何配置,则默认配置为:
{
// Item elements
items: '*',
// Default show animation
showDuration: 300,
showEasing: 'ease',
// Default hide animation
hideDuration: 300,
hideEasing: 'ease',
// Item's visible/hidden state styles
visibleStyles: {
opacity: '1',
transform: 'scale(1)'
},
hiddenStyles: {
opacity: '0',
transform: 'scale(0.5)'
},
// Layout
layout: {
fillGaps: false,
horizontal: false,
alignRight: false,
alignBottom: false,
rounding: true
},
layoutOnResize: 100,
layoutOnInit: true,
layoutDuration: 300,
layoutEasing: 'ease',
// Sorting
sortData: null,
// Drag & Drop
dragEnabled: false,
dragContainer: null,
dragStartPredicate: {
distance: 0,
delay: 0,
handle: false
},
dragAxis: null,
dragSort: true,
dragSortInterval: 100,
dragSortPredicate: {
threshold: 50,
action: 'move'
},
dragReleaseDuration: 300,
dragReleaseEasing: 'ease',
dragHammerSettings: {
touchAction: 'none'
},
// Classnames
containerClass: 'muuri',
itemClass: 'muuri-item',
itemVisibleClass: 'muuri-item-shown',
itemHiddenClass: 'muuri-item-hidden',
itemPositioningClass: 'muuri-item-positioning',
itemDraggingClass: 'muuri-item-dragging',
itemReleasingClass: 'muuri-item-releasing'
}
布局:
layout: {
fillGaps: false,
horizontal: false,
alignRight: false,
alignBottom: false,
rounding: true
},
如上配置指定的布局的默认排列方式 - 从左上方开始排还是从右上方开始排、从上往下排还是从左往右排,在这里都是可以指定的;
排序:
HTML
<div class="grid">
<div class="item" data-sort="4">
<div class="item-content">
one
</div>
</div>
<div class="item" data-sort=“3">
<div class="item-content">
two
</div>
</div>
<div class="item" data-sort=“2">
<div class="item-content">
three
</div>
</div>
<div class="item" data-sort=“1">
<div class="item-content">
four
</div>
</div>
</div>
Javascript
sortData: {
foo: function (item, element) {
let sortId = element.getAttribute('data-sort');
return Number(sortId);
}
},
grid.sort('foo');
如上所示我们可以在item上设置自定义属性data-sort,然后依次作为排序的依据;
拖动:
dragStartPredicate: {
distance: 0,
delay: 0,
handle: '.drag-btn'
},
distance表示开始拖动前移动的距离,默认为0;delay表示拖动的延时;handle默认为false,认为整个item都是可以拖动的,但也可以接受一个字符串,使用querySelecor查询,这样我们就可以在每个item右上角设置一个按钮,只有点击这个按钮才能拖动;
拖动方向:
dragAxis: ‘x'
表示只能在x方向拖动,也可以设置为’y’,默认为null,表示x和y方向都可以拖动;
拖动排序:
dragSort: true,
表示是否允许拖动之后重新排序,默认是true,如果设置为false,那么就只能排序而不能更改位置;
拖动排序间隔:
dragSortInterval: 200
默认是200ms,这个值表示拖动之后每隔多少时间进行判断是否能够排序,显然这个时间越长,则排序进行的越缓慢,所以可以设置为0;
拖动排序断定:
dragSortPredicate: {
threshold: 50,
action: 'swap'
},
默认的action是move,但是对于拖动排序来说这种效果并不好,而swap的效果往往会更好一些 - 比如看板有两列,某列的上下两个元素之间可以直接拖动替换位置,即为swap;而threshold表示交换阈值,取值在0 - 100,这里的50表示当两个item之间交叉的部分超过50%时,就触发swap;
拖动释放动画时间:
dragReleaseDuration: 0,
这里的默认值是300,即在拖动释放之后从释放位置到正确位置移动的时间,如果设置为0表示释放之后会立即回到原位,这种效果在交互上也是非常不错的;
拖动释放动画:
dragReleaseEasing: 'ease',
就释放拖动之后回到原位的动画效果,默认为’ease’,也可设置为 ‘ease-out’ 等;
一些默认的class名称:
containerClass: 'muuri',
itemClass: 'muuri-item',
itemVisibleClass: 'muuri-item-shown',
itemHiddenClass: 'muuri-item-hidden',
itemPositioningClass: 'muuri-item-positioning',
itemDraggingClass: 'muuri-item-dragging',
itemReleasingClass: 'muuri-item-releasing'
这些名称都是可以根据自己的需要进行修改的;
我所需要的最终的配置为:
var grid = new Muuri('.grid', {
layoutDuration: 600,
sortData: {
foo: function (item, element) {
let sortId = element.getAttribute('data-sort');
return Number(sortId);
}
},
dragEnabled: true,
dragStartPredicate: {
distance: 0,
delay: 0,
handle: '.drag-btn'
},
dragAxis: null,
dragSort: true,
dragSortInterval: 0,
dragSortPredicate: {
threshold: 50,
action: 'swap'
},
dragReleaseDuration: 200,
dragReleaseEasing: 'ease',
});
grid.sort('foo');
Grid方法
Grid即通过 new Muuri() 或得到的实例,该实例默认有一些方法可以调用,下面一一介绍:
获取grid
var elem = grid.getElement()
获取grid这个element
获取item
var allItems = grid.getItems() // 获取到所有的item
var activeItems = grid.getItems().filter(function (item) {
return item.isActive();
}); // 获取到active的item,即显示出来的item,这个active是在配置项中 items 中指定的,默认是 ‘*’
var firstItem = grid.getItems()[0]; // 获取到第一个item
更新item
grid.refreshItems(); // 更新所有的Item,主要是缓存的维度
grid.refreshItems([0, someElem, someItem]) // 更新特定的Item
同步DOM
grid.synchronize()
同步DOM元素,保证DOM的顺序结构和页面显示的顺序结构一致。如果我们使用了排序函数使得item顺序变化,但这时看DOM实际上是没有变的,所以如果希望保持一致就可以使用grid.synchronize()函数;
布局
grid.layout() // 根据配置项布局
grid.layout(true) // 根据配置项无动画的直接布局
grid.layout(function (items) {
console.log(‘layout done');
}); // 布局,并定义回调函数,如果所有item布局结束即调用该回调函数
添加Item
Item可以通过grid.add(elements, [options])的方式添加到grid中,添加的过程中默认是添加到最后,添加之后会自动触发grid.layout(),另外如果你希望同步DOM可以使用grid.synchronize()来同步:
grid.add([elemA, elemB]) // 添加两个新的items到grid末尾
grid.add([elemA, elemB], { index: 0 }) // 添加两个新的items到grid开头
grid.add([elemA, elemB], { layout: false }) // 跳过自动layout
移除Item
grid.remove(0); // 移除第一个Item,但是该元素保持在DOM中
grid.remove([elemA, elemB], { removeElements: true }); // 移除Items以及相关的元素
grid.remove([elemA, elemB], { layout: false }); // 移除Items,并禁止layout
显示和隐藏
grid.show();
grid.hide();
过滤Item
grid.filter(function (item) {
return item.getElement().hasAttribute(‘data-foo');
}); // 展示所有拥有data-foo属性的item
grid.filter(‘[data-foo]’); // 上述简写
grid.filter(‘.good’); // 展示有class为good的Items
Items排序
对Items排序, 有三种方式可以对Items进行排序;
//第一种排序方式: Sort items by data-id attribute value (ascending).
grid.sort(function (itemA, itemB) {
var aId = parseInt(itemA.getElement().getAttribute('data-id'));
var bId = parseInt(itemB.getElement().getAttribute('data-id'));
return aId - bId;
});
//第二种排序方式 Sort items using the sort data keys (ascending).
grid.sort('foo bar’, {descending: true});
//第三种排序方式 Sort items with a presorted array of items.
grid.sort(presortedItems);
移动Items
grid.move(elemA, elemB, {action: ’swap'}); // 将elemA移动到elemB的位置,使用交换的方式
grid.move(0, -1); 将第一个Item移动到最后一个位置
移动Items到另外一个grid
gridA.send(0, gridB, -1); // 将gridA中的第一个Item移动到gridB中的最后位置,移动之后将会append到document.body后
销毁grid
grid.destroy(); // 销毁grid
grid.destroy(true); // 销毁grid并移除Item元素
Grid事件
在操作grid时会触发各种各样的事件,这里我们需要一一介绍:
同步:
在grid.synchronize();调用之后会触发下面的回调函数:
grid.on('synchronize', function () {
console.log('Synced')
})
布局开始/结束:
在grid.layout()调用开始和之后触发下面的回调函数:
grid.on('layoutStart', function () {
console.log('start')
})
grid.on('layoutEnd', function () {
console.log('end');
});
添加Items触发下面的回调函数:
grid.on('add', function (items) {
console.log(items);
});
移除Items触发下面的回调函数:
grid.on('remove', function (items, indices) {
console.log(items, indices);
});
在grid开始show和结束show之后触发回调函数:
grid.on('showStart', function (items) {
console.log(items);
});
grid.on('showEnd', function (items) {
console.log(items);
});
在grid开始hide和结束hide之后触发回调函数:
grid.on('hideStart', function (items) {
console.log(items);
});
grid.on('hideEnd', function (items) {
console.log(items);
});
在grid.filter()函数调用之后触发:
grid.on('filter', function(shownItems, hiddenItems) {
console.log('shown', shownItems)
console.log('hidden', hiddenItems)
});
grid.filter('.table');
在grid.sort()函数调用之后触发:
grid.on('sort', function (currentOrder, previousOrder) {
console.log('目前的顺序', currentOrder);
console.log('之前的顺序', previousOrder);
});
grid.sort('foo');
在grid.move()函数调用之后触发:
grid.on('move', function (data) {
console.log(data);
});
与grid.send()函数调用触发的相关事件:
grid.on('send', function (data) {
console.log(data);
});
grid.on('beforeSend', function (data) {
console.log(data);
});
grid.on('receive', function (data) {
console.log(data);
});
grid.on('beforeReceive', function (data) {
console.log(data);
});
drag相关事件
grid.on('dragInit', function (item, event) {
console.log('拖拽初始化', event, item);
});
grid.on('dragStart', function (item, event) {
console.log('拖拽开始', event, item);
});
grid.on('dragMove', function (item, event) {
console.log('拖拽移动', event, item);
});
grid.on('dragScroll', function (item, event) {
console.log('拖拽Scroll', event, item);
});
grid.on('dragInit', function (item, event) {
console.log('拖拽初始化', event, item);
});
grid.on('dragEnd', function (item, event) {
console.log('拖拽结束', event, item);
});
如果要记录拖拽的顺序,下面的事件尤为重要:
grid.on('dragStart', function (item, event) {
console.log('拖拽开始 grid items', grid.getItems());
});
grid.on('dragEnd', function (item, event) {
console.log('拖拽结束 grid items', grid.getItems());
});
drag释放
grid.on('dragReleaseStart', function (item) {
console.log(item);
});
grid.on('dragReleaseEnd', function (item) {
console.log(item);
});
drag销毁
grid.on('destroy', function () {
console.log('Muuri is no more...');
});
Item 方法
每个Item都有相关的方法可以调用,下面一一介绍:
获取grid
var grid = item.getGrid();
获取Item对应的Element
var elem = item.getElement();
获取Item的宽度
var width = item.getWidth();
获取Item的高度
var height = item.getHeight();
获取Item的margin
var margin = item.getMargin();
获取Item相对于container元素的left和top值
var position = item.getPosition();
其中position是一个对象,有left和top属性,都是number类型的数字;
判断某个Item是否active
var isActive = item.isActive();
判断某个Item是否可见:
var isVisible = item.isVisible();
判断某个Item是否正在被拖拽:
var isDragging = item.isDragging();
判断某个Item是否已经被释放:
var isReleasing = item.isReleasing();
判断某个Item是否被销毁:
var isDestroyed = item.isDestroyed();