GitHub - lucasb-eyer/d3-boundingbox: D3.js lib to give any element with x,y,width,height attributes a resizable, movable behaviour similar to interact.js. (original) (raw)

D3.js Bounding Box

This is a reusable componentfor D3.js which can give any element having x, y,width and height attributes an interactive behaviour, similarly toD3's brushand interact.js.

Available behaviours are:

Features of the library are:

The following are intentional non-features:

This library is part of a larger collection of D3.js utilities for supporting the quick creation of browser-based interactive labeling tools.

Installation

You can install d3-boundingbox using npm by installing from this git repo:

npm install https://github.com/lucasb-eyer/d3-boundingbox.git

Usage

Just like any D3.js reusable component, create the behaviour and call it on a collection:

var bb = d3lb.bbox() d3.selectAll("rect").call(bb) // Alternatively d3lb.bbox().infect(d3.selectAll("rect"))

That's it. Since a svg rect element supports the x, y, width and heightattributes, it has now become movable and resizable.

The return value of call is the D3.js selection it's been called on, so that you can keep chaining calls to selection methods. The return value of infect is the boundingbox itself, so that you can keep chaining calls to the boundingbox's methods.

Note that the test.htmlfile covers almost all possible usages, so you can use it as an example.

Also note that there's a disinfect function which can be used to remove all registered event handlers from the element.

In the following, all setter methods can also be used as getters by not passing in any value parameter.

Imposing a Constraint

You can impose constraints, i.e. min/max x- and y-coordinates within which the element should stay. You can either pass a pair of constant values:

var bb = d3lb.bbox() .xextent([minx, maxx]) .yextent([10, +Infinity]) d3.selectAll("rect.head").call(bb) // Alternatively d3lb.bbox().infect(d3.selectAll("rect.head")) .xextent([minx, maxx]) .yextent([10, +Infinity])

or a function which will be called with the element's datum and index as arguments and this bound to the element whenever a resize is attempted and should return two values: the minimum and the maximum. For example, you can restrict a head's bounding-box to stay within the corresonding full-body bounding-box:

d3lb.bbox().infect(d3.selectAll("rect.head")) .xextent(function(d, i) { return [d.body.left, d.body.right]; } .yextent(function(d, i) { return [d.body.top, d.body.bottom]; }

To get back to not having any constraints, call {x,y}extent(false).

Also notice how you can use Infinity to stay unbounded in some direction.

Selecting Interactions

All possible interactions have a name, and you can enable or disable each individually. Here are their names:

Using the directions function, you can restrict the enabled interactions:

var bb = d3lb.bbox().directions(['e', 'w', 'x']) d3.selectAll("rect").call(bb) // Alternatively d3lb.bbox().infect(d3.selectAll("rect")) .directions(['e', 'w', 'x'])

The above will only allow the user interact with the rectangles horizontally.

To get back to the default setting, call directions(true).

Custom Cursors

Since every design is different, you can customize which cursors should be shown before and during any kind of interaction through the cursors function.

It is easy to disable any cursor modification by just calling cursors(false). the customization is done by passing an object with properties having the interaction names mentioned above, and their values being the cursors to use:

var bb = d3lb.bbox().infect(d3.selectAll("rect")) .directions(['e', 'w', 'x']) .cursors({ x: 'url(dragx.cur), col-resize', e: 'url(szleft.cur), e-resize', w: 'url(szright.cur), w-resize' })

There is one additional name, M, which is used when dragging in both x andy are enabled. This allows you to specify all three of M, x and y, at initialization and enable/disable at will at runtime.

To get back to the default cursors, call cursors(true).

Callbacks

By registering callbacks through the on function, you can get notified of the following events:

All of these functions are passed D3.js' usual d and i, i.e. data and index parameters of the element being affected, and set this to the element itself.

During any of the callbacks, you can access D3.js' global d3.eventobject and, for example, get the mouse coordinates via d3.mouse.

Note: If the dragmove and/or resizemove callbacks return exactly false(not just a falsy value), the move will not happen. You can use this for even more fine-grained control than you could achieve with the constraints.

This is how you'd add the drag class to an element during drag:

var bb = d3lb.bbox().infect(d3.selectAll(".ninjas")) .on("dragstart", function(d, i) { this.classList.add("drag") }) .on("dragend", function(d, i) { this.classList.remove("drag") })

Handle Size

Finally, you can choose how large, in pixels, the resize-handle should be, either isometrically: handlesize(5), or for each side individually:

var bb = d3lb.bbox() .handlesize({ 'w': 3, 'e': 3, 'n': 6, 's': 6 })

Feedback

It's always nice to hear what cool things people do with my libraries, so don't hesitate to let me know what you used it for!

If you have any suggestions, use-cases I missed, or anything, make sure to file an issue or, better yet, make a pull-request!

License: MIT

Copyright (c) 2015 Lucas Beyer

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.