Despite the fact that web-development technologies were seriosly improved in the last decade
with things like CSS3, media-queries, flexbox, we think that it still is requires to much time to create
complex grid layouts with raw technologies browsers provides. In 2012, we got a task to develop a horizontal
grid per web-app, that should satisfy following criterias:
- Grid items have different px-based sizes.(Can be changed dynamically)
- Grid items are placed with respect of sorting by create date from right to left side.
- Grid items can be aligned dynamically by centers in each new col.
- Grid items can be sorted, filtered and dragged around by a administrators.
- Grid should not have 'empty gaps', altrough, max distance from item valid sorting position
should be controlled.
There was a requirement, that we should show some subset of items on start, and than load new items
on reaching grid left/right corners:
- Grid items are loaded starting from current date on page load.
- New items are loaded on reach left grid corner(future, prepend) and
right grid corner(past, append).
- New items can also be loaded or deleted at any time later after/before any grid item.
- All current grid items should be moved only by X-coord on prepends.(Full relayout
looks too heavy per UX)
Additionally, there was a requirement to create a real-time application - collection of grid items should be synced to
web-sockets server through our internal modified version of backbone JS library. So, this requirement has spawned
following tasks:
- All users, that are interacting with grid, can modify/add/delete
grid items in random order.
- Grids of all users, that are currently connected to WS server,
are rendering all changes instantly.
- In any moment of time server can spawn a batch of concurrent grid operations.
- Any grid operation could happen while user is interacting with grid.
Final horizontal grid should look approximately like this(Without custom scrollers and loaders):
Async grids from a better future
Why Grid development process is so complex?
When we started development, we was upset with amount of work, which was required to produce
layout that covers all out requirements. It felt more strange, that browsers don't support natively such type of grids.
Looks like the perfect place for javascript per resque! So, we have started developing the grid library, that would
cover all our requirements. In the process of development some important additional requirements were formuled:
No dependencies
Library should be written in vanilla JS. Library should be able to attach to any HTML elements,
which sizes could be readed from browser calculated sizes. Thus, gridifier could be used with any
available frontend framework.(Angular, Ember, Backbone, Bootstrap, etc...).
No CSS dependencies in JS
Gridifier shouldn't require any parameters, that are related to item sizes. Otherwise any changes
in CSS should be replicated in Javascript.
Optimized per touch devices
Process of resolving element sizes should be optimized and cached untill the moment, when element sizes
can be changed. By 'optimized' we also mean the process of minimal performance loses after browsers
reflow/relayout operations, which can quite fast significantly decrease performance on touch devices.
Extensible
Gridifier API methods(togglers, coordsChangers, sorts, etc...) should be easily modified/added by developers.
To remove unused library classes Gridifier should be shipped with Grunt task, where unused classes could be
easily replaced with mock objects.
Browsers support
Gridifier should work in all wide-used browsers across all modern platforms. On desktops library should work
in IE8+ and good browsers.
Architecture
How Gridifier works?
We decided, that for providing architecture ready per real-time applications sync, Gridifier kernel should
work as a set of synced queues:
At illustration is shown simplified version of Gridifier internals. We must notice a few more important
points:
- All queues minimizes performance loses, caused by browsers reflow process.
- Gridifier will not lock main browsers thread per long time.(Almost in all operations)
- Reposition queue is processing data by small batches.(You can override batch size)
- All insert operations are inserting all items in single batch by default.
- You can pass custom batch/size and timeout per any insert operation.
- All built-in renderer togglers are synced.
Algorithm
How Gridifier repositions items?
Gridifier is positioning grid items with absolute positioning. We have tried different positioning algoritms
and have chosen the best:
- First algorithms required minimal item width/height to calculate
cells count, which are covered by item.(Incorrect)
- Next, we have tried to store how many X-Y space is allocated by every item.(Slow)
- After that, we thought that instead of storing 'How many space item allocates'
we should store 'All places where next item can be inserted'.
- Finally, after some experiments, we have written custom version of
skyline 2d bin-packing algorithm.
This algorithm is very fast, it requires only about 300-500 microseconds to reposition
any item inside grid.(In modern browser) Shortly, it stores all available connectors per current grid state.
Connectors can be shifted in different ways, depending on insert type. Additionally, we are using some additional
enhancements to minimize connectors count check at every reposition step.
next: Install