haltu / muuri Goto Github PK
View Code? Open in Web Editor NEWInfinite responsive, sortable, filterable and draggable layouts
Home Page: https://muuri.dev
License: MIT License
Infinite responsive, sortable, filterable and draggable layouts
Home Page: https://muuri.dev
License: MIT License
Originally I picked velocity for this project as I was familiar with it and I knew it to be fast, but lately I've been exploring other alternatives. The v0.3.0 is refactored in way that allows you to change the animation engine, although it is still a bit of a hassle, so in that sense there's no need to change the default animation engine. However, I really like what Popmotion, mo.js and anime are doing.
The plan is to try out each engine, do a little benchmarking and possibly change the default animation engine for the v1.0.0 release. Or better yet, make plugins for each engine so the user can choose which engine to use ;)
Have to say, it looks very promising! Would be interesting to see a selected event for the items. Also, being tied to hammer, not providing your own touch and drag engine is in my opinion, a downside.
Currently Muuri counts item margins as part of the item when dragging and sorting, which is a bug. The margins should not be considered part of the item when calculating how much an item overlaps another item.
Currently there are some basic smoke tests in place that check the existence of the methods, but we need to do a bit better than that. Now is time to add basic tests for all the methods and events.
Would be great to have detailed usage examples with working demos.
If the item element or the item child element is an anchor element or an image element Muuri's dragging breaks since browsers provide native dragging (the ghost image) for those elements. It would be good if we prevented native drag for those elements automatically so users don't need to apply the hacks.
Problem
When an item is dragged slowly on the grid the items sometimes bounce back and forth wildly when the layout method is triggered repeatedly before the element reaches the position it's been dragged to. There should definitely be smarter algorithm to handle these kind of scenario. Due to this problem the only really usable dragOverlapTolerance
values are between 60 and 40.
Solution
When dragging check the direction and velocity of the event and take them into account when deciding on which element is targeted. Needs some testing to and tinkering to figure out a proper algorithm.
The gutter between the items is fully controlled via CSS, so if you wanted to have a gutter of 1px between the items you would probably do something like this:
.grid {
border-right: 1px solid transparent;
border-bottom: 1px solid transparent;
}
.item {
margin: 1px 0 0 1px;
}
If we would give padding to the grid element it would do us no good since absolute positioned elements are always positioned relative to the outer edge of the containing element's padding. So using border is needed here. The problem is, however, that if we wanted the grid to have a visual border we would need wrap it in another element.
So, to overcome this annoyance I think the padding could be made useful by making the item elements position themselves relative to the inner edge of the containing element's padding. This way the CSS could be written like this:
.grid {
padding: 0 1px 1px 0;
}
.item {
margin: 1px 0 0 1px;
}
And we could also use border for other purposes on the grid element :)
I really like the way staggered animation works here: http://chenglou.github.io/react-motion/demos/demo1-chat-heads/. Maybe we should try adding an option to define staggered layout animations somehow and check out if they look and feel nice.
muuri.move(itemA, itemB)
-> muuri.move(itemA, itemB, 'move')
muuri.swap(itemA, itemB)
-> muuri.move(itemA, itemB, 'swap')
I'm not sure if there exists yet a dynamic grid library which can handle a massive amount of items (10000+). Kinda like what SlickGrid is doing, but with dynamic grids. I have high hopes that I can make Muuri handle tons of items with decent responsiveness.
The rendering part will actually be the easiest part (although not easy to implement by any means). The simplest solution is just to add "display:none" to all items that are outside the viewport, which should be enough to keep the UI responsive. Sure, there are going to be problems with animating and it's going to require a lot of hair-pulling to get it right, but it's doable.
The hard part is getting the layout algorithm work fast in this kind of scenario. I have no clue how I'm gonna pull that off at the moment, but I think we can pull a rabbit or two out of the hat if we really try ;)
This kind of functionality is needed in infinitely scrolling grid layouts with lots of data, like Instagram or Pinterest for example.
Let's see if we can pull this off. Help and ideas are appreciated!
Hi,
I see your demo has items that are multiple sizes. Can you explain how you could do this in markup?
Since the touch events are handled by Hammer, I am unable to scroll the container that I have muurified, on mobile safari on iOS.
I believe it has to do with touch-actions = none.
One solution I can think of for this problem is to require that the user taps and holds a post before the dragging starts. That way swiping a post will continue to scroll the container per normal, and a tap+hold would activate dragging.
Let's add muuri to npm registry.
At least With Chrome this already happens automatically if the dragged element is within a scrollable element. However, it would be nice if Muuri allowed scrolling element(s) specified by the user when dragging occurs.
Currently Muuri only allows defining the duration and easing for hide and show animations, but it would be nice if one could also define the animated styles.
I was trying to achieve the "Muuri kind of" effect in Materialize.
GitHub Repos - https://github.com/samwillson/muuri-materialize
GitHub Pages - https://samwillson.github.io/muuri-materialize/index.html
Visit the GitHub Pages link and you will understand the problem.
Can anyone explain which grid framework to use alongside muuri or how to integrate the other pre-developed frameworks?
Does anyone know how to fix the page?
From a performance perspective, translateX and Y are better, but adding transforms to elements wreaks havoc on the stacking order.
In the upcoming v0.3.0 version filtering can be done by first getting all the items and then looping them and calling the muuri.hideItems() and muuri.showItems() methods. As an example let's imagine we want to show all the items which has class "foo". In it's most simplistic form it would be done something like this:
muuri.getItems().forEach(function (item) {
if (item.getElement().classList.contains('foo')) {
muuri.showItems(item);
}
else {
muuri.hideItems(item);
}
});
Not too bad, but what if we had a filter method? It would probably look something like this:
muuri.filterItems(function (item, element) {
return element.classList.contains('foo');
});
Much simpler and nicer. Although filtering can be easily accomplished without a specific filter method I think having it in the library would make filtering more approachable to Muuri's users. Also there is the fact that we can add optimizations to the filter method under the hood.
Going a bit further with the idea and example we could also add built-in support for filtering by class names and data-attributes. So the above example could be also written as:
muuri.filterItems('.foo');
Nice and short!
So, let's add the filter method to the v0.3.0 API shall we? =)
Try to make layout/overlap check as fast as possible by using smart heuristics. For example change the algorithm based on the item sizes. If all items are same size the algorithm can be simplified. Do heavy lifting only when necessary.
Optimizations
please add bower.json
If no hide/show animation duration is defined things will break.
Future performance optimizations in mind, I think it would be a good idea to keep the currently active Muuri.Layout
instance stored in the Muuri
instance so we can diff it with the next layout and possibly make some layout speed optimizations.
Currently Muuri.Item
has an undocumented prototype method .inspect()
which returns data about the item. I though it would be useful to split the returned data into smaller methods and document them. So here's the new item methods:
item.getElement()
item.getWidth()
item.getHeight()
item.getMargin()
item.getPosition()
item.isActive()
item.isVisible()
item.isShowing()
item.isHiding()
item.isPositioning()
item.isDragging()
item.isReleasing()
Sorry, JS noob and serial js plugin leverager here...I can usually write enough JS to gain access to a plugin, but I am having a ton of trouble creating filter buttons for Muuri. I have the plugin installed just fine, just need some examples to customize my grid based on button interactions.
Any help would be greatly appreciated!
LOVE MURRI BTW...it is a great piece of code.
It would be nice to toggle something in css to force something to remain a divider when it is 100% width
This one is probably not necessary to have in the API. Getting the index is not too hard and I don't see this being used so often.
I just released v0.3.0 and it was a big release: API rewrite, performance optimizations, tons of bug fixes a lot of new features (including dragging items between grids). I hope the API won't change too much for the upcoming 1.0.0 version, but let's see.
In any case I'll start working on the 1.0.0 version very soon and was hoping that I would get as much feedback on the 0.3.0 version as possible so when you have the time please give me some feedback. Also, I'm very open to any feature ideas you guys have =)
Currently the drag sort predicate is hard coded and can only be configured using the dragSortAction
and dragSortTolerance
options. However, there might be cases where the default drag predicate is not sufficient, for example if the user provides a custom layout algorithm. With this in mind I though it might be a good idea to provide a way to allow the user to define a custom drag sort predicate.
The idea is to provide a new option called dragSortPredicate
, which accepts either an object or a function. When an object is provided Muuri uses the default drag sort predicate, which can be configured by adding threshold
and action
properties to the configuration object. By default the object would be something like {threshold: 50, action: 'move'}
.
When a function is provided Muuri calls the function during each drag event with the dragged Muuri.Item
instance as it's first (and only) argument and expects the function to return a falsy value if no sorting is wanted and an object if sorting is wanted. The returned object should contain target item's current and new index and how information on how to move it (move or swap). Basically the data should be something like this: {action: 'move', from: 0, to: 1}
.
This is a big feature, we need to make it possible to connect Muuri
instances to allow dragging items from a container to another.
Initial specs:
Muuri
instances.Muuri
instance that from which the item is moved to another Muuri
instance should trigger a "send" event.Muuri
instance that receives an item from another Muuri
instance should trigger a "receive" event.Hi!
Just found Your script from eWebDesign Newsletter Issue #178 ๐
Looks very promissing but forgive my noob question
Where is the content of the grid in demo? :O
i see only
Currently there is no built-in drag placeholder. Would probably be a good usability enhancement.
So the code base is getting pretty heavy and I haven't had the chance to thoroughly test if memory is leaked. I have noticed that after dragging the memory is leaked a bit, but that might be also an issue with Hammer, don't know yet.
Ideally we would have automated memory leak tests and thankfully it appears there's already a library (drool) for making that happen.
If anyone is interested I'd be interested to hear reports of memory leaks. Also, pull requests are always appreciated if you're up for fixing the leaks you find ๐
Currently Muuri's items can be sorted by mutating the muuri._items
property, which is always an array of all the items in the Muuri instance. So just calling muuri._items.sort(function (a ,b) { ... })
does the trick. Define your own sorting logic and you're good to go.
However, this way of sorting has some concerns. The _items
property is meant for internal use only, so it's a bit of a hacky approach, although it works nicely. It would probably be nicer for the users if we created a public API method for the above snippet, which would go something like this:
muuri.sortItems(function (a, b) { ... });
Internally this method would just call muuri._items.sort(function (a ,b) { ... })
, but now it's more approachable for the users and can be documented.
Also later on we could add more functionality to the method by allowing it to, for example, accept an object which can be configured in the same style as Isotope allows.
Hi,
This is an awesome library. If there was a react.js version for this, I'm sure there'd be alot of users. There are existing libraries I've tried and the one of them is react-dnd which is what I use. Its setup is not straightforward but it is super flexible, it doesn't offer any transitions out of the box though. There's another called react-sortable-hoc which is easy to setup but does not support multiple lists/containers.
Let's wrap the library within Universal Module Definition so it can be user as an AMD module, in Node.js and in the browser.
I thought it would be nice to have a public method for retrieving Muuri instance's element (might come in handy when connected Muuri instances feature is ready). Also, layout system is now changed a bit and Muuri keeps the container element's dimensions and offsets cached, which should be retrievable via muuri.getRect()
method.
See the demo here: http://codepen.io/niklasramo/pen/jyJLGM. While dragging an item try to scroll the page. The item's position should be adjusted based during scroll events, but it's not happening. Find out why.
I had some luck creating a filter button using the demo js as a guide, but I have run up against another challenge. My first version worked on the first filter but then just appended the grid on every other filter. I added a rather inelegant reset of the "itemsToShow" array at the beginning of my click function to solve that problem, but it doesn't animate as nicely as the initial filter add gets progressively worse. Do you have any advice for a better solution? I apologize in advance for my basic code...still learning. Here is the js:
$(document).ready(function() {
var filterButton = $(".filter-button");
var items = grid.get();
var itemsToShow = [];
var itemsToHide = [];
$( filterButton ).click(function(e) {
e.preventDefault();
var filterButtonType = $(this).attr('id');
itemsToShow = [];
items.forEach(function (item) {
var $elem = $(item._element);
var isFilterMatch = filterButtonType ? $elem.attr('data-filter-type') === filterButtonType: true;
(isFilterMatch ? itemsToShow : itemsToHide).push(item);
grid.hide(itemsToHide);
grid.show(itemsToShow);
});
});
});
Hello!
How can i make a gellery with random order of items on every loading?
Thank you!
I love muuri,but not clear on how to applay to a real project. And I got a question when try it. Why the images concluded in the item-content can not be dragged? Is that OK to insert different size pictures? How can I fix my code. Thanks.
http://codepen.io/diyifang/pen/NdVgNX
Problem
A dragged item needs to overlap another item in order for moving to happen. A dragged item can not be dropped on empty gaps within the grid, which feels like bug.
Solution
When calculating the layout, don't lose the empty slots data, but instead use it for the drag overlap calculations. For example, connect each empty slot to an actual item and when the dragged item overlaps an empty slot the item is moved in place of the slot's connected item. The current layout data should be stored in the Muuri
instance.
Is it possible to use percentages instead of hard coded dimensions for items? I developed my project using them which worked fine on my local, but when I pushed them to my dev site the images weren't loading in time for muuri so leaving my heights at auto borked my grid. Can I use the refresh items function to resize after load? I tried preloading the images, but that hasn't solved the problem. Maybe delay the layout function? If I do delay or refresh...would I put that in the init code or run a separate script? Any help would be greatly appreciated. See my page here... http://www.tomwattsart.com/work.html
Thanks,
Tom
It would be nice if the user would not be limited to using Velocity as the only way to animate the items. Figure out a way to make it possible for the user to use another animation engine to do all the animations.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.