Code Monkey home page Code Monkey logo

plain-draggable's Introduction

PlainDraggable

npm GitHub issues dependencies license

The simple and high performance library to allow HTML/SVG element to be dragged.

Document and Examples https://anseki.github.io/plain-draggable/

ss-01

Features:

  • Accept HTML/SVG element as an element that comes to be draggable.
  • Support both mouse and touch interfaces.
  • Restrict the draggable area.
  • Snap the draggable element to other elements, points, lines, consecutive points (i.e. grid) or something.
  • Scroll an element or window automatically when the draggable element was moved to edge of the area.
  • Use requestAnimationFrame API, translate and will-change CSS if possible, for high performance.
    ss-02
  • Provide only basic methods, easy and simple usage.
  • No dependency.
  • Single file.
  • Support modern browsers.

Usage

Load PlainDraggable into your web page. (Or, install it for importing.)

<script src="plain-draggable.min.js"></script>

This is simplest case:

draggable = new PlainDraggable(element);

Now, the element can be moved by mouse-dragging (or touch interfaces) operation.
The element might be a <div>, <span>, <circle> or something.

By default, the moving is restricted to inside of the element's parent element.

<div style="background-color: gray;">
  <div id="draggable">Drag This</div>
</div>

The <div id="draggable"> can be moved within the gray box. (See containment option.)

You can specify a "Grid" that the draggable element snaps to.

draggable.snap = {step: 30};

For options, methods and more details, refer to the following.

Constructor

draggable = new PlainDraggable(element[, options])

The element argument is a HTML or SVG element that will be a draggable element. Any element that has a bounding-box is accepted. Other elements such as <g> can not be specified.

The options argument is an Object that can have properties as options (and leftTop option for constructor). You can also change the options by setOptions method or properties of the PlainDraggable instance.

When you will do something about HTML document regardless of the PlainDraggable, you typically do that after the HTML document is ready (i.e. the HTML document has been loaded and parsed by web browser).
For example:

// Wait for HTML document to get ready
window.addEventListener('load', function() { // NOT `DOMContentLoaded`
  // Do something about HTML document
  var draggable = new PlainDraggable(document.getElementById('target'));
});

If you don't wait for HTML document to be ready, you might not be able to get a target element yet, or problems with incomplete layout may occur. Also, you should do so asynchronous like the above for the performance because synchronous code blocks parsing HTML.

Before HTML document is ready, the translate CSS function might not be initialized yet. If so, PlainDraggable tries to initialize a draggable element with the left and top CSS properties regardless of leftTop option.
This may be performance degradation.

leftTop option

By default, PlainDraggable tries to move the draggable element by using the translate CSS function.
If true is specified for leftTop option of the constructor, it uses the left and top CSS properties instead, for HTML element. You may use this to make it synchronize with the layout of something.

Note that the left and top CSS properties may be performance degradation.
Also, this is an option for constructor, that is, you can not change this after.

Methods

setOptions

self = draggable.setOptions(options)

Set one or more options.
The options argument is an Object that can have properties as options.

position

self = draggable.position()

Re-calculate the position of the draggable element, containment option, snap option and autoScroll option, with current layout of a page.
Those are re-calculated as needed automatically when a window is resized, or a window or something is scrolled.
You should call position method if you changed the layout without resizing the window or scrolling something.

remove

draggable.remove()

Remove the current PlainDraggable instance.
Never use the removed instance.

Options

containment

Type: HTML/SVG element or Rect
Default: Parent element

A rectangle that restricts the moving of the draggable element. The draggable element can not move to the outside of this rectangle even if a mouse pointer is moved to there.
It can be a HTML or SVG element, or a Rect object like {left: '10%', top: 20, right: '90%', height: 300} (See Rect). The base of the Rect object is the current document.
Note that the rectangle is "padding-box" of the element if an element is specified. That is, it is the inside of borders of the element.

The width or height can be less than the size of the draggable element, such as zero, to disable the moving on that axis.
For example, this draggable element can be moved in a horizontal direction only.

draggable.containment = {left: 20, top: 20, width: 800, height: 0};
// See next code too.

When the moving on an axis is disabled, the draggable element is not moved in that axis direction at all. Therefore the position of the containment on that axis has no meaning.
For example, when 0 is specified for height to disable the moving on that Y-axis, even if 0, 999 or another value is specified for top, the draggable element is not moved to 0 or 999 in Y-axis.
Therefore the above example code can be replaced with:

draggable.containment = {left: 20, top: 0, width: 800, height: 0};
// The element is not moved to `top: 0` even if it is positioned at `top: 480` now.

The default is the draggable element's parent element.

You can specify a <body> element that means all of a page, or {left: 0, top: 0, width: '100%', height: '100%'} means all of a document (the document height might differ from the <body> height).

snap

Type: number, string, HTML/SVG element, Rect, Array, Object or undefined
Default: undefined

See Snap.

autoScroll

Type: boolean, window, HTML element, Object or undefined
Default: undefined

Scroll a scrollable area when the draggable element is moved to near the edges of the area. The scrollable area can be the current window or a HTML element.

For example:

draggable.autoScroll = true;

If true is specified, the autoScroll is enabled with all default options.

If a HTML element is specified, it is enabled with {target: element} option.

Or, you can specify an Object that can have properties as following options.

target

Type: window or HTML element
Default: window

A scrollable area. Maybe, that contains the containment or that is the containment itself.

speed

Type: number or Array
Default: [40, 200, 1000]

A number determining the speed of scrolling, pixels per second.

You can specify an Array that contains multiple values. Then, scroll it with the first speed when the draggable element was moved near the edges of the area, and scroll it with the next speed when the draggable element was moved near more..., in this way. At most, you can specify three numbers.
Those must be sorted in ascending order.

See sensitivity option for lines of demarcation that switch the speed.

sensitivity

Type: number or Array
Default: [100, 40, 0]

A number determining the starting or switching the speed, the distance from the edge of the area, in pixels.

When you specify multiple values for speed option, you can specify an Array that contains multiple values. Then, scroll it with the first speed when the draggable element was moved at less than the first distance from the edges of the area, and scroll it with the next speed when the draggable element was moved at less than the next distance..., in this way. At most, you can specify three numbers.
Those must be sorted in descending order.

See speed option for the speed.

minX / maxX / minY / maxY

Type: number or undefined
Default: undefined

A number setting a limit to scrolling. These values are returned value by pageXOffset, pageYOffset, scrollLeft or scrollTop.
You can specify 0 for maxX or maxY to disable scrolling for that axis only. For example, maxX: 0 disables horizontal scrolling only.

Note that this is a limit of the autoScroll, not scrolling of UI. That is, user can scroll manually, regardless of this.

handle

Type: HTML/SVG element
Default: Draggable element

A part element of the draggable element that receives mouse (or touch interfaces) operations. A user seizes and drags this element to move the draggable element.
The default is the draggable element itself, and all of the draggable element can be seized and dragged.

zIndex

Type: number or boolean
Default: 9000

A z-index CSS property that is applied to the draggable element when it is being dragged. It is restored when the dragging finished.
If false is specified, it is not changed.

left / top

Type: number or undefined
Default: undefined

The distance between a left or top edge of the draggable element and a left or top edge of the document, in pixels.

It can be used for initial position when constructing new PlainDraggable instance. Also, it can be used to move the draggable element after constructing.
For example, move it rightward:

draggable.left += 10;

onDrag

Type: function or undefined
Default: undefined

A function that is called when the draggable element was dragged. That is not moved yet.
It is called even if the draggable element is not moved because containment or snap avoided the moving. Use onMove if you want to do something when the draggable element was moved.

In the function, this refers to the current PlainDraggable instance.

An Object that has left and top properties as new position is passed to the function. These are numbers that were calculated the same as left / top options.
It is new position that the draggable element is about to go to. Therefore it might differ from a position of a mouse pointer. And also, you can change these properties.

For example:

draggable.onDrag = function(newPosition) {
  if (newPosition.left > this.rect.left) {
    newPosition.left = this.rect.left + 48; // Move it 48px to the right forcibly.
  }
};

Also, snapped property is true if the position was determined by the draggable element being snapped (See Snap).

If the function returns false, the moving is canceled.

For example:

draggable.onDrag = function(newPosition) {
  return !!newPosition.snapped; // It is moved only when it is snapped.
};
// The `gravity` option also can be used for the same purpose.

onMove

Type: function or undefined
Default: undefined

A function that is called when the draggable element was moved.
It is not called when the draggable element is not moved because containment or snap avoided the moving even if the draggable element is dragged.

In the function, this refers to the current PlainDraggable instance.

An Object that has left and top properties is passed to the function. It might have snapped property too. See onDrag for those properties.
And also, autoScroll property is true if it tried scroll (See autoScroll option). This means that the draggable element was moved to near the edges of the area, it might have been not scrolled.

For example:

draggable.onMove = function(newPosition) {
  console.log('left: %d top: %d width: %d height: %d it is scrolling: %s'
    // determined position that was changed by snap and onDrag
    newPosition.left, newPosition.top,
    this.rect.width, this.rect.height,
    newPosition.autoScroll);
};

onDragStart

Type: function or undefined
Default: undefined

A function that is called when a mouse button or touch interface was pressed on the draggable element. That is not dragged yet.
It is called even if the draggable element is not dragged. Use onDrag if you want to do something when the draggable element was dragged.

In the function, this refers to the current PlainDraggable instance.

An Object that has clientX and clientY properties pointing a position of a mouse pointer or touch interface is passed to the function. That might be MouseEvent or first Touch object of TouchEvent.

If the function returns false, the dragging is canceled.

For example:

draggable.onDragStart = function(pointerXY) {
  if (isEar(pointerXY)) {
    alert('Do not pull my ear!!');
    return false;
  }
  return true;
};

onMoveStart

Type: function or undefined
Default: undefined

A function that is called when the moving the draggable element started.
It is not called until the draggable element is moved even if it was dragged, and it is called at most once, while a mouse button is being pressed.

In the function, this refers to the current PlainDraggable instance.

An Object that has left and top properties is passed to the function. It might have snapped and autoScroll properties too. See onMove for those properties.

onDragEnd

Type: function or undefined
Default: undefined

A function that is called when a mouse button was released.
It is called even if the mouse pointer was not moved.

In the function, this refers to the current PlainDraggable instance.

An Object that has left and top properties is passed to the function. See onDrag for those properties.

Properties

disabled

Type: boolean
Default: false

If true is specified, the draggable element stops the moving immediately, and it does not receive mouse operations.

element

Read-only

Type: HTML/SVG element

The element that was passed to constructor.

rect

Read-only

Type: Rect

A Rect object to indicate current position and size of the draggable element.
The base of the Rect object is the current document.

Accessor Properties

Properties with the same name as each option to get or set following options:

For example:

draggable.containment = wideArea;
draggable.handle = newHandle;
draggable.left += 10;

You can use setOptions method instead to set multiple options.

PlainDraggable.draggableCursor / PlainDraggable.draggingCursor

Static Property

Type: string or Array
Default: (depend on web browser)

  • PlainDraggable.draggableCursor: grab ['grab', 'all-scroll', 'move'] or ['all-scroll', 'move'] (For Webkit)
  • PlainDraggable.draggingCursor: grabbing ['grabbing', 'move'] or 'move' (For Webkit)

PlainDraggable.draggableCursor is a cursor CSS property that is applied to the handle element.
PlainDraggable.draggingCursor is a cursor CSS property that is applied to the handle element while a mouse button is being pressed.
If an Array that contains multiple values is specified, PlainDraggable tries it with each value until any of them succeeded.

Note that the allowed values of the cursor CSS property depend on each web browser. And also, Webkit has a bug about the cursor CSS property, and the bug is being ignored a long time.

If false is specified, it is not changed.
If false is specified for both PlainDraggable.draggableCursor and PlainDraggable.draggingCursor, PlainDraggable does nothing to a cursor CSS property.

PlainDraggable.draggableClass / PlainDraggable.draggingClass / PlainDraggable.movingClass

Static Property

Type: string
Default:

  • PlainDraggable.draggableClass: 'plain-draggable'
  • PlainDraggable.draggingClass: 'plain-draggable-dragging'
  • PlainDraggable.movingClass: 'plain-draggable-moving'

PlainDraggable.draggableClass is a class name that is added to the draggable element.
PlainDraggable.draggingClass is a class name that is added to the draggable element within the period from when a mouse button is pressed until the button is released. Then, the draggable element has both classes.
PlainDraggable.movingClass is a class name that is added to the draggable element within the period from when the draggable element starts moving until a mouse button is released. Then, the draggable element has three classes.

For example:

PlainDraggable.draggableClass = 'cat';
PlainDraggable.draggingClass = 'lift';
PlainDraggable.movingClass = 'swing';

Rect

An Object to indicate a position and size of a rectangle, like DOMRect.
It has following properties:

  • left or x
  • top or y
  • width or right
  • height or bottom

Also, it is relative to a "base".

The left / top / right / bottom indicate distance between the left or top edge of the base and the left or top side of the rectangle. Note that right / bottom also indicate distance from the left or top edge of the base, not from the right or bottom edge.
The x is an alias for left, and the y is an alias for top.
The width / height indicate a size of the rectangle. The width is required if right is not specified, and the height is required if bottom is not specified.

All values are a number as pixels or string as percentage (e.g. '30%').
The "base" determines the origin of coordinates, and the number of pixels of 100%.

  • When the Rect object is specified for the containment option, the base is the current document.
  • When the Rect object is specified for the snap option, the base is determined by base option.
  • When the Rect object is got by the rect property, the base is the current document.

Note that the right must be greater than or equal to left, and the bottom must be greater than or equal to top. In other words, the width and height can not be negative value. PlainDraggable does not invert these to avoid behavior that you don't want. For example, {left: 400, right: '50%'} is ignored when the base is resized to smaller than 800px width.

For example, when the base width is 600px and height is 400px:

  • {left: 20, top: '10%', width: '50%', bottom: 390} indicates a rectangle that is left: 20px; top: 40px; width: 300px; height: 350px;, positioned relative to the top-left corner of the base.
  • {left: '50%', top: 0, width: '50%', height: '100%'} indicates a rectangle that is the right half of the base.
  • {left: '10%', top: '10%', right: '90%', bottom: '90%'} indicates a rectangle that has 10% margin at all sides.

Snap

You can specify snap option.
The snap option makes one or more "snap-targets". The draggable element gravitates toward a snap-target when it is moved to near the snap-target, and it sticks to the snap-target.

Each snap-target can be one of the following:

  • Point
  • Line
  • Element or Rect object

The point acts on both a horizontal direction and a vertical direction, and the line acts on either direction.
The point and line are the basic of snap-target. The element and Rect object create multiple lines.

Single value creates a point that the value indicates both X and Y coordinates of the point. Then a top-left corner (default) of the draggable element snaps to this point.
This is simplest case.

draggable.snap = 60; // Point (X: 60px, Y: 60px)

A value as coordinate is a number as pixels or string as percentage (e.g. '30%'). The percentage is calculated the same as the x and y of Rect.
For example, this indicates the center of the base.

draggable.snap = '50%'; // Point (X: 50%, Y: 50%) i.e. the center

An Object that has x and y properties creates a point that the properties indicate coordinates of the point.

draggable.snap = {x: 160, y: '40%'}; // Point (X: 160px, Y: 40%)

That is, a value that does not have both x and y is considered as a point that the value indicates the same value for the x and y. For example, 30 is considered as {x: 30, y: 30}.

An Object that has either one of x and y properties creates a line that the property indicates a coordinate on the axis. Then two sides (default) of the draggable element along a direction of this line snap to this line.

draggable.snap = {x: 130}; // Vertical Line (from top edge to bottom edge)

By default, the line has full length of size of the base.
The other property can be an Object that has one or both of start and end properties that indicate a coordinate on the axis. This Object indicates the range on the axis for the line.

draggable.snap = {x: 130, y: {start: 5, end: '50%'}}; // Vertical Line

Note that the end must be greater than or equal to start. PlainDraggable does not invert these to avoid behavior that you don't want. For example, x: {start: 400, end: '50%'} is ignored when the base is resized to smaller than 800px width.

A step property of the Object copies the point or line repeatedly at specified intervals. This may be called "Grid". It can be also grid on only either one of X and Y axis.
Its value is a number as pixels or string as percentage (e.g. '30%'). The percentage is calculated the same as the width or height of Rect.

// Consecutive Points at 40px intervals, on X and Y
draggable.snap = {step: 40};
// As previously mentioned, this is considered as {x: {step: 40}, y: {step: 40}}.
// Consecutive Points at X: 10px and Y: 20px intervals
draggable.snap = {x: {step: 10}, y: {step: 20}};
// Consecutive Horizontal Lines at 25% intervals (from left edge to right edge)
draggable.snap = {y: {step: '25%'}};
// 4 Consecutive Horizontal Lines (from left edge to right edge)
draggable.snap = {y: {step: '20%', end: '60%'}};
// 4 Consecutive Horizontal Lines (300px length)
draggable.snap = {x: {end: 300}, y: {step: '20%', end: '60%'}};

An element or a Rect object creates four lines that overlap with edges of its rectangle.

draggable.snap = document.getElementById('snap-box'); // Element
draggable.snap = {left: '10%', top: 0, width: '80%', height: 280}; // Rect object

The base of the Rect object is determined by base option.

You can specify options for the snap-target via properties of the Object that is passed to the snap option, or an Object instead of something that is not Object. For example, you can specify {x: 80, y: 80, gravity: 30} instead of 80 to specify the gravity option.
Also, you can specify an Array that contains multiple snap-targets.

For options and more details, refer to the following.

Structure and Abbreviation

Actually, you saw shortened structure of a snap option in the above.
Its basic structure is here:

SnapOptions:

{
  targets: [snapTarget1, snapTarget2...], // Array containing `SnapTarget`s
  // Common options for all `SnapTarget`s in the `targets`
  option1: optionValue1,
  option2: optionValue2,
    :
}

SnapTarget:

{
  x: xValue, // number, string or Object
  y: yValue,
  boundingBox: elementOrRect, // Element or Rect object
  // Options for this `SnapTarget`, that override each common option
  option1: optionValue1,
  option2: optionValue2,
    :
}

The x and y can be one of the following:

  • Coordinate:
    A value that indicates a coordinate on the axis.
  • Range:
    An Object that indicates a range on the axis, that can have start and end properties that are each coordinate on the axis.
  • Repeated Coordinates:
    An Object that indicates repeated coordinates on the axis, that has step property that is interval between repeated coordinates. It can have start and end properties as a range.

All these values are a number as pixels or a string as percentage (e.g. '30%'). The step as percentage is calculated the same as the width or height of Rect, and the other values as percentage are calculated the same as the x or y of that.

The combination of the x and y determines what the SnapTarget indicates.

  • If both x and y indicate each coordinate, this SnapTarget indicates a point.
  • If either one of x and y indicates a coordinate and the other indicates a range, this SnapTarget indicates a line.
  • If either one of x and y indicates a coordinate and the other indicates repeated coordinates, this SnapTarget indicates consecutive points arranged linearly.
  • If both x and y indicate repeated coordinates, this SnapTarget indicates consecutive points arranged in the horizontal and vertical directions.
  • If either one of x and y indicates a range and the other indicates repeated coordinates, this SnapTarget indicates consecutive parallel lines.
  • If both x and y indicate each range, this SnapTarget indicates four lines as a rectangle. Unlike boundingBox, edge option is not applied.

An important point, the default for the x and y is a range, and the default for the start is 0, and the default for the end is '100%'.
Therefore, you can specify either one of the x and y to indicate a line. For example, {y: 30} indicates a horizontal line. The x having either one or both of the start and end is required only when the line should be shortened.
Also, you can specify only the step to indicate consecutive points or consecutive lines. For example, {y: {step: 50}} indicates consecutive horizontal lines.

The boundingBox is used instead of the x and y, to indicate a rectangle.
You can specify an element or Rect object, then this SnapTarget indicates four lines that overlap with edges of its rectangle.

You can specify one or more SnapTargets for the targets Array of SnapOptions.
And you can specify one or more options in both SnapTarget and SnapOptions. The options in SnapTarget override each option in SnapOptions.
For example:

draggable.snap = {
  targets: [
    {x: 100},
    {x: 200, gravity: 60},
    {x: 300}
  ],
  gravity: 50,
  center: true
};

A 50 is applied to the gravity option of first and third snap-targets.
A 60 is applied to the gravity option of second snap-target only.
A true is applied to the center option of all three snap-targets.

Abbreviation

As above, you can specify the snap option in detail. Or, you can also specify it simply as follows.

You can specify something for the SnapTarget directly. It is considered as both x and y, or boundingBox.
For example, the following two codes work same:

draggable.snap = {
  targets: [
    100,
    document.getElementById('snap-box'),
    {x: 20, y: 0, width: 400, height: 200}
  ]
};
draggable.snap = {
  targets: [
    {x: 100, y: 100},
    {boundingBox: document.getElementById('snap-box')},
    {boundingBox: {x: 20, y: 0, width: 400, height: 200}}
  ]
};

You can specify single SnapTarget for the targets directly instead of an Array.
For example:

draggable.snap = {
  targets: 100 // The same as [100] and [{x: 100, y: 100}]
};

You can specify the targets for the SnapOptions directly.
For example:

draggable.snap = 100;
// The same as {targets: 100} and {targets: [100]} and {targets: [{x: 100, y: 100}]}

draggable.snap = [100, {x: 200, gravity: 60}]; // 2 targets

Options for Snap

These are specified in the SnapOptions and SnapTarget.

gravity

Type: number
Default: 20

Distance from the draggable element, in pixels.
The draggable element gravitates toward the snap-target when the distance becomes less than or equal to this.

To avoid staying between "repeated coordinates", you can specify a number greater than or equal to the half of the step. Or, onDrag option also can be used for the same purpose.

corner

For snap-target as point

Type: string
Default: 'tl'

One or more corners of the draggable element that gravitate toward the snap-target.
It is a value that indicates a corner or a list of the values separated by space or comma.

Allowed values are:

  • 'top-left' (Alias: 'tl', 'left-top', 'lt')
  • 'top-right' (Alias: 'tr', 'right-top', 'rt')
  • 'bottom-left' (Alias: 'bl', 'left-bottom', 'lb')
  • 'bottom-right' (Alias: 'br', 'right-bottom', 'rb')
  • 'all' (The same as 'tl tr bl br')

side

For snap-target as line

Type: string
Default: 'both'

One or more sides of the draggable element that gravitate toward the snap-target.
It is a value that indicates a side or a list of the values separated by space or comma.

Allowed values are:

  • 'start': Indicate a left side of the draggable element if the snap-target is vertical line, otherwise a top side.
  • 'end': Indicate a right side of the draggable element if the snap-target is vertical line, otherwise a bottom side.
  • 'both' (The same as 'start end')

center

For snap-target as point or line

Type: boolean
Default: false

If true is specified, the center of the draggable element gravitates toward the snap-target. The corner and side options are ignored.

edge

For snap-target as element or Rect object

Type: string
Default: 'both'

One or more sides of each edge of the rectangle that the draggable element gravitates toward. The rectangle is that of the element or Rect object that is specified for the boundingBox.
It is a value that indicates a side or a list of the values separated by space or comma.

Allowed values are:

  • 'inside'
  • 'outside'
  • 'both' (The same as 'inside outside')

base

Type: string
Default: 'containment'

A base for the Rect object. The x and y also are relative to this.

Allowed values are:

  • 'containment': Indicate the element or Rect object that is specified for the containment option.
  • 'document': Indicate the current document.

Development

If you want to use a NPM package in development mode such as importing it to your app with e.g. Webpack, you have to install the package and devDependencies packages in accordance with customary practice.

For example:

npm i plain-draggable
cd node_modules/plain-draggable
npm i

plain-draggable's People

Contributors

anseki avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

plain-draggable's Issues

issue with css zoom property (element and cursor position not match)

Hello, thanks for the great script!

It works without problem when use with normal css zoom (i.e. 100%), but when used with css zoom property different than 1.0, position of target draggable element is not coordinated with mouse(cursor) position.

Could you help on this issue?

Restrict SVG movement?

Hi,
I'm using your SVG example as a basis for interacting with an SVG path.
I am trying to restrict movement to the x axis but am failing.
Might you have a suggestion?

x-only

The Fiddle is here: https://jsfiddle.net/Squinter/frc1pL08/11/

Also, my next desire is to control (drag) the corners of a 4-sided SVG shape. I guess a beginning would be the same Fiddle above, but if you have any suggestions I would appreciate them too.

4-corner-drag

Thanks.

Typescript Typedef

Hello,

I am trying to use plain-draggable in combination with typescript, however I can't rely on the typesafety as there are no type definitions.

Would it be possible to add a .d.ts file with the types? or create a official @types/plain-draggable package?

How to reset translate?

Hello anseki, i like this library very much, and thank you first.

this is my question, when i modified an element top and left value, after i dragged this element with plain-draggable, i fund the transfrom translate value won't be clear, It could cause drift issues when setting the top value.
Β 

@click doesn't work in draggable element

Hello there, thanks for your work on this library!

I have an issue, @click event doesn't work on elements/components inside draggable element.
Any ideas how to make it works?

Let me know if needed more details.

Need onSnap event.

Hi,
I am trying to attach a sound effect 'on snap'. Can you point me in a direction on how to do this with the current code? I'm aware of the onDragEnd event. I like to catch a snap while dragging. Maybe can you add an onSnap event in a future release? Or maybe it can be added as a property to the onMove event?
I love plain-draggable very much so far!
Thx,
Marco

How to use WordPress (Issue being unrelated to PlainDraggable)

Hi!
So I’ve made a little game that works as planned as a normal web page (see the CodePen here: https://codepen.io/squinter/pen/VNMpgW).

But, when I put it into a local server WordPress page I get this error and nothing works:
TypeError: undefined is not an object (evaluating 'p0.cx.baseVal')

The error references line 55 in the JS part of the Pen. I’m stumped.

FYI is that I’m using the WP Scripts n Styles plug-in on the WP site. I’ve done other pages there using plain draggable and those seem to work well.

While I can pull the game in via an iFrame (which does work), I’d prefer not to.

npm does not install required dependencies

Hi anseki,

When someone installs the plain-draggable package using npm (or yarn), the required dependencies are not installed automatically:
anim-event
cssprefix
m-class-list
etc.

People who are using their own module bundler can't use your minified bundle. They need to import the non-minified version. And in that case, the packages above are needed during run time, so they should be included as "dependencies" and not as "devDependencies" in your package.json.

"devDependencies" are reserved for dependencies that are only needed during the development life-cycle of plain-draggable (e.g. "webpack").

After you sort out "dependencies" and "devDependencies" in this way, users will be able to import your package much more smoothly.

This problem exists in leader-line as well.

Thanks!

-Caghan

Containment issues

So here is the full use case;

  1. I want all draggable items to stay inside of a div that is wrapped in an outer scrollable div.
  2. I would like the autoScroll to continue to work (like it does now!)
  3. I want to be able to put additional containment on some of the draggable items, so that "line 1" does not go past "line 2" and "line 2" cannot come above "line 1". In my local project I have code to recalculate the containment on every drag; so that if you move line1 then line2's containment is updated to now know where the top is now at. However the containment system seems to have issues when inside a scrollable item. It seems like coordinates must change from the scroll, and so then the object is moved.
  4. I would love to be able to add additional items that aren't contained but can snap to those lines. (I believe this is already doable; just haven't done the math -- but it is possible that the snap system might have issues with the scrollable div)

Primary issue: How do I contain Line 1 so it can't go below Line 2. Contain Line 2 so it can't go above line 1, while allowing the div to scroll.

https://jsfiddle.net/yoc3r1mv/8/

Issues so far found:
[ ] - Auto-movement of contained items: #30
[x] - leftTop causes draggable to be outside of the constraints of the parent container #31

Snapping Problem when dragging elements

Currently I am writing a class that will handle the initiation of new Draggable objects. However i seem to be having the problem that when 2 or more objects/elements are made and run the process they have a weird behaviour of snapping.

const drag1 = () => {
return new PlainDraggable(document.getElementById('block1'))
}

drag1().snap = {x: {step: 10}, y: {step: 20}};
const drag2 = () => {return new PlainDraggable(document.getElementById('block2'))}
drag2().snap = {x: {step: 10}, y: {step: 20}};

This works fine and there are no odd behaviours. However Ive tried to clean up my code by using classes instead of have to create separate functions for each draggable item;

class Furniture{

         addToRoom(){
        //console.log(this.el);
        const dropArea = document.querySelector('.dropArea');
        dropArea.appendChild(this.el)
        Room.addDragToElement();
        }
}

Each Furniture object is passed into the addToRoom function which then adds the furniture element into the dropArea element. Thats then passed to the Room class which tries to add drag;

static addDragToElement(){
        const dropArea = document.querySelector('.dropArea');
        //console.log(dropArea.lastElementChild)
        return new PlainDraggable(dropArea.lastElementChild)
    }

It creates a new Draggable class then returns that allowing the new furniture objects to be draggable within the Room. However this has the snapping issue.
Am i missing something, am i dumb? Could i get some help please

Apologies in advance for poor format.

reposition of draggable object when parent is scaled

Hi, first - thank you @anseki for sharing your work!

I'm having some issues when a draggable object is inside a scaled div for example. I created a fiddle (https://jsfiddle.net/goepfert/bms9L3xz/) to show what I mean.

Basicly I can see two somehow related features:

  1. Scale parent div by 0.5 (Button 0.5), move/drag the green draggable div and then force draggable.position() by Button click 'Reposition'. Now evertime you force a reposition the draggable object jumps a little to the top left.

  2. Reload. Move/Drag the draggable object a litte. Scal by factor of 2 (Button 2) and the scroll. Now evertime you force a reposition the draggable object jumps a litte to the bottom right. Remark: If I overwrite the draggable.position() method the behaviour is the same.

Note: To match the mouse position to the dragging object while dragging, the position is calculated manually via ondrag. This has no influence to the obove mentioned issues.

I'm unable to find out what causes these style transformations nor how to fix them. They seem to be related to the scaling factor.

I'm very grateful for any help!

docs: Provide code for sortable list example

On the documentation page, there is an example for sortable list : This is an example for sortable list. Drag these items to sort these..
The area I am most interested in is how the other items make room when dragged item is moved?
Can you please add to documentation how this is acheived?

connect many dragging elements with lines

Hi,
I'm testing possibility of connecting many dragging elements into a flow.
I'm able to connect elements into pairs, but stucked with connection between pairs. I'm trying to connect element 0 and 4 with a line.
Could you please support me?

connect lines

Code:

Plain-draggable06.txt

leftTop option doesn't seem to work

I love the library and the documentation is great! I'm having an issue however with the leftTop option that doesn't seem to have an effect on the way the items are being moved. I want to use simple left and top coordinates from a dragged object in order to store and recall position of DOM elements from a json file, but regardless of the leftTop option it's using transform: translate css every time. Is this a known issue or am I missing something? Otherwise I would love to hear any other ways I could get coordinates from draggable elements for storage use!

containment moves when scrolling

I created a draggable element that has a parent larger than the viewable area on the screen so it is scrollable. I set the elements containment to something inside the viewable area. When I go to the browser and scroll the containment moves along with the scrolling and as a result the element moves as well. I tried setting the target to the scrolling element but this doesn't fix it. do I have to change the containment left and top properties on each scroll event? I would rather not have to do this.

var draggable = new PlainDraggable(domElem[0], {
snap: {step: '1%'},
left: coords.left,
top: coords.top,
containment: {
left: layerCoords.left,
top: layerCoords.top,
width: layerBounds[2] - layerBounds[0],
height: layerBounds[3] - layerBounds[1]
},
target: $('#plot-container')[0]
});

Can't resolve 'anim-event', and 'cssprefix ', and 'pointer-event' ...

Failed to compile.

./node_modules/plain-draggable/plain-draggable.esm.js
Module not found: Error: Can't resolve 'anim-event' in 'D:\workspace\zj-sytem\node_modules\plain-draggable'

Hi, i used the command 'npm i plain-draggable' to Install dependencies yesterday. I found other bags missing when i wanted to import 'plain-draggable', so i kept on installing others dependencies. So,, can you update the package 'plain-draggable'.

sortable list example

Hi, very good documentation and impressive demo page. good work.

Only problem is the "sortable list" demo, which feels very slow and also has a bug, where any element cannot be dragged to be the last one.

I would like dragging & sorting to feel much much faster, and the way it behaves now is not smooth enough for production-level projects

steps option on boundingBox

Hi Anseki,

nice project! Is it possible to have the steps option just on a specific target? Unfortunately, that is not working.
Example:
{ boundingBox: document.getElementById('target') }, { boundingBox: { x: 35, y: 280, width: 200, height: 80, step: 35 } }
And, if I have multiple draggable elements, is it possible to prevent that they have the same coordinates? Thank you!

Dragging between containers

Good day,

Congratulations! This is some amazing work done on this plugin.

Would you be so kind to provide an example of dragging items between containers?

Thanks in advance.

Resize an element by mouse/touch dragging

Hi,
I'm trying to use Plain Draggable in a WordPress page (can't show it because it's still only on my local drive). I'm using Scrips N Styles to allow me to add CSS and JS to the page.

I'm following your JSFiddle from here: https://jsfiddle.net/byaje56n/
And the closed thread: resize element? #7

Anyway, all is working fine outside of WordPress.

In WordPress all is fine as well, except when I switch to responsive.
When I'm in responsive mode and begin to drag the handle, the box immediately expands down below the bottom of the window. See the attached image (two screenshots combined).
1

I cannot figure it out.

Here's what's in the main html:

<div id="mainBx">
<div id="box"></div>
<div id="handle"></div>
</div>

Here's what's in the CSS:

#mainBx {
    position: relative;
	height: 200px;
  }

#box {
	background-color: transparent;
	border-style: solid;
	border-width: 1px;
	border-color: #000000 !important;
	display: flex;
  }

#handle {
    /* Disables panning and selecting on touch devices */
	touch-action: none;
	user-select: none;
	cursor: pointer !important;
	position: absolute;
    background-color: #43ba8e;
    width: 30px;
    height: 30px;
    left: 400px;
    top: 200px;
  }

Here's what's in the JS before body closing:

window.addEventListener('load', function() {
  'use strict';

  let handle = new PlainDraggable(document.getElementById('handle'));
  let box = document.getElementById('box');

  function resize() {
    let rect = box.getBoundingClientRect();
    box.style.width = handle.rect.right - rect.left + 'px';
    box.style.height = handle.rect.bottom - rect.top + 'px';
  };

  handle.onMove = resize;
  resize();

});

Plain Draggable incompatble with shadow dom

If you attach the render tree of elements to a shadow dom that you are trying to use PD with; the following line fails:
https://github.com/anseki/plain-draggable/blob/master/src/plain-draggable.js#L338
as apparently anything inside the shadow dom is considered disconnected:

Test code:

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>

<test-shadow></test-shadow>

</body>
<script>
    customElements.define('test-shadow',
        class extends HTMLElement {
            constructor() {
                super();
                this._container = document.createElement('div');
                this._shadowContainer = document.createElement('div');
                this.appendChild(this._container);
                let shadow = this.attachShadow({mode: 'open'});
                shadow.appendChild(this._shadowContainer);
            }

            connectedCallback() {
                console.log("Connected");
                console.log(this._container.compareDocumentPosition(document));
                console.log(this._shadowContainer.compareDocumentPosition(document));
           }
         }
    );
</script>

You will see the _container which is NOT attached to the shadow dom returns 10.
You will see the _shadowContainer which is attached to the shadow dom return 35 or 37 depending on the browser...

Deleting this single check seems to allow it to fully work inside the shadow dom...

Support installation as a node module

I would appreciate if this could work as a regular node module with ES6 imports, allowing greater flexibility and interoperability with other libraries as well as own code.

I tried to make this work in a webpack environment to no avail – mostly due to missing dependencies I'm not willing to install explicitly as they have nothing to do with my project, just this library.

I'm looking forward to giving this a shot in the future; especially the snapping features look great.

Use drag item in combinaison with Flickity handle

Hi, I am trying to use a drag item with Flickity (https://flickity.metafizzy.co). Do you have an idea as how I could bind the drag of the item with the drag of the slider?

Basically I want the custom handle in Flickity to be the draggable element setup with PlainDraggable. If you see in the JSFiddle bellow, the gray square is the custom handle that is used to switch slides in Flickty on drag.

Here is a basic slider with a custom handle and the draggable item.
https://jsfiddle.net/1unbwmja/10/

Thanks

"module" script was not transpiled to ES5

Hi anseki,

Thank you for adding the "module" property to package.json. There is one last problem remaining, for "Create React App" users. After importing your package, npm run-script build throws the following error:

Failed to minify the code from this file:

        ./node_modules/plain-draggable/src/plain-draggable.js:26

Read more here: http://bit.ly/2tRViJ9

If you follow the link above, under the heading "npm run build fails to minify", you will see that they require the "module" script to be transpiled to ES5 except for the ES module directives (import/export).

Well, I think you know this already, because the specs in https://github.com/rollup/rollup/wiki/pkg.module mention the same thing. :)

Could you please take care of this? It should be pretty easy. For example, in plain-draggable:

  1. npm install --save-dev babel-cli
  2. Add a new build script to package.json, like this:
    "build-module": "babel src/plain-draggable.js --out-file plain-draggable.es.js"
  3. Add a new .babelrc file at your repository root, with the following contents:
{
	"presets": [
		["es2015", {"modules": false}]
	]
}
  1. In package.json, point the "module" property to "plain-draggable.es.js".
  2. Make sure to call npm run-script build-module before publishing.

If my understanding is correct, this should make your packages 100% ready for consumption.

Thanks!

-Caghan

Draggable does not work well with transform/matrix/scale elements

Positioning is off for those elements that have been scaled up/down using css transform (this includes scale, matrix and matrix3d). Do you plan to support such functionality? Your examples show that it supports scaled SVG so I assumed scaling would be within the scope of plain-draggable.

Turn off CSS will-change

There doesn't look to be an option to turn off CSS will-change from the start. While will-change helps performance, it seems to limit plain-draggable such that scaling produces pixelated draggables.

Getting the element a snappable element snapped to

Awesome library but I seem to have encountered a bit of a flaw for my use case.

I am currently trying to do a multiple choice drag and drop - so I need to be able to see what options the user has dropped where.

Essentially I need to get the element where the user has dragged the answer to and snapped it to. is there a way I can get the element that i is snapped to?

Add tips for the "losted" dependencies .

Hi, @anseki .
Thank you for creating such an awesome library, I've read several dependencies-related issues. I think before we find a better solution, it is better to add a section in the README to indicate that 'modern' users should install the losted dependencies by themselves. Otherwise, it is quiet confusing and may cause a time-consuming debugging.

Failure with SVG on FireFox

Hello,
got a wierd problem when trying to apply a draggable to a svg element.
My Stack is: VueJS, vuetify, typescript, plain-draggable

This error only occurs in FireFox, but when trying to drag on chrome, the element just disappears.

[Vue warn]: Error in mounted hook: "[Exception... "Failure"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: webpack-internal:///./node_modules/plain-draggable/plain-draggable.esm.js :: initSvg :: line 841"  data: no]"

found in

---> <StepElemContainer> at src/components/StepElemContainer.vue
       <VStepper>
         <HelloWorld> at src/components/HelloWorld.vue
           <VContent>
             <VApp>
               <App> at src/App.vue
                 <Root> vue.runtime.esm.js:619
    VueJS 15
    <anonym> main.ts:7
    ts app.js:1336
    __webpack_require__ app.js:786
    fn app.js:151
    1 app.js:1362
    __webpack_require__ app.js:786
    checkDeferredModules app.js:46
    <anonym> app.js:862
    <anonym> app.js:865

When trying a simple example with the svg element not being inside a vuetify element it works without problems.

Thanks for the help.

Import error when using module bundler

Hi anseki,

I am a big fan of your libraries, and I would like to report an important issue.

For example, take a look at these two lines in your package.json for plain-draggable:

  "main": "plain-draggable.min.js",
  "jsnext:main": "./src/plain-draggable.js",

This means that when someone imports your package using import PlainDraggable from 'plain-draggable', then he/she will get the minified file plain-draggable.min.js. But, if that person is already using a module bundler (like webpack) that minifies all imported packages, then that bundler will throw an error.

I don't know the exact reason why it does not work, but probably it's because the source is already minified, and it can't be minified again.

In fact, the "main" property in package.json is not meant for the minified version of the library. It is meant for the "jscurrent" version of the library. In the "jsnext" version (./src/plain-draggable.js), you are using ES2015, so the "main" version should simply be the same thing in ES5 (and not minified).

You are probably taking advantage of your existing minification process to convert from ES2015 to ES5. Instead, you can use this page to do the same conversion, but without any minification: http://babeljs.io/repl/

If you want, you can still include the minified file in your package. Whoever needs it can reference that file directly. But what's important is to make sure that the "main" property in package.json points to a file that is not minified.

As a result of this change, webpack users (including most React developers and all users of Create React App) will finally be able to import your package. This should add to the popularity of this excellent package.

This problem is in all the dependencies of plain-draggable as well: anim-event, cssprefix, m-class-list, leader-line, etc. Only after all of them are fixed can the parent package be imported.

Thanks!

-Caghan

how to set a wall that draggable can't move into

Hi,

i find that there is an example in the document, "You can control the moving with coordinates of the draggable element and a mouse pointer that drags." it is really useful. but i can't find the method.

how can i set an rect hole in the containment, that the draggable element can't move into it ?

double click

its look like the drag option take the double click event and i can't use it
any solution?

Issue with Leaderline plugin, multiple elements linked to one node becomes not movable except the last added one

Hi,

First of all I would like to appreciate your invaluable creations of these two wonderful plugins plainDraggable and LeaderLine.

I am working on implementing a tree based structure starting from a single parent node in top. Top most parent can have multiple child nodes and users can dynamically add child nodes. Child nodes can also become parent nodes and tree can grow downward. All nodes are needed to be draggable9i used plainDraggable) and connected to their parents with LeaderLine.

The issue I came accross is, for a a parent node i can add first child node, it comes with LeaderLine and also movable.Then if I add the second child for same parent,second child comes with LeaderLine and also movable. But first one becomes not draggable. After 3rd one added its movable, 2nd one becomes immovable. Sorry if I have messed up it and if its not an issue with couple of plugins.

I don't see any error in console as well.
`
if($('#row_'+child_row).length)
{

    }
    else
    {
        rows.push('row_'+child_row);
        rows['row_'+child_row] = new Array();
        elems['row_'+child_row] = new Array();
        $('#rowContainer').html($('#rowContainer').html()+'<div id="row_'+child_row+'"class="row padding0 mx-auto" style="margin:0 auto;"></div>');
    }

    if(type == 'greeting')
    {
        //alert(get_num_elements_of_row('row_'+child_row)); 
        var cindex = get_num_elements_of_row('row_'+child_row)+1;
        $('#row_'+child_row).html($('#row_'+child_row).html()+'<div class="col-1 padding0 text-center cnodewrapper"><i class="fa fa-handshake fa-3x greeting cnode nodes" id="e_'+child_row+'_'+cindex+'"></i></div>');
        elems['row_'+child_row].push('e_'+child_row+'_'+cindex);


        drags['e_'+child_row+'_'+cindex] = new PlainDraggable(document.getElementById('e_'+child_row+'_'+cindex));

        lines['e_'+child_row+'_'+cindex] = new LeaderLine(
            document.getElementById(parentNode),
            document.getElementById('e_'+child_row+'_'+cindex)
          );

        drags['e_'+child_row+'_'+cindex].containment = {left: 0, top: 0, width: '100%', height: '100%'};
        drags['e_'+child_row+'_'+cindex].onDrag = function(newPosition) {
        lines['e_'+child_row+'_'+cindex].position(); 

       /* $('.cnode').each(function(index,item){
            var thisId = $(this).attr('id');
            drags[thisId].position();
            lines[thisId].position();
        }); */
      };
    }

`

``

Recalculate the position of the dragged element after container/window resize

If I drag an element and it snaps to an element that is itself positioned using relative units and then a resize event occurs, how can I modify the position of the draggable so that it matches the position of the snap element it snapped to (while retaining the ability to drag)? As you confirmed here there's no property storing the snap target nor the index of the this.snap.targets object - the index of the target would be great to get in places where the snapped property is set during events.

https://jsfiddle.net/frumbert/docr5mj7/

In your example, you just iterate the snap targets and match bounding boxes / coordinates and drag positions to find the particular target, which is fine for static objects. However during certain resize conditions it can become un-apparent which droppable object was snapped to which target so its position could be adjusted after resize.

`stopPropagation` and right-button

  1. As I saw in devtool, you delegate the mousemove event on document. If some guys use the stopPropagation in the container element mousemove listener. The drag would not work.
    So, I proposed to expose the mousemove element and other event delegation element.
<div class="app" id="app">
  <div id="draggable">Drag This</div> 
</div>
html,body {
  width:100%;
  height:100%;
  display:flex;
  justify-content:center;
  align-items:center;
}
.app {
  width:500px;
  height:500px;
  background:pink;
}
#draggable {
  width:100px;
  height:100px;
  background:lightblue;
  cursor:move; 
}
var draggableIns = new PlainDraggable(draggable);
// doesn't work with code below
app.onmousemove = function() {
  event.stopPropagation();
};

Solution:

1. event capture may solve this
2. allow user to bind mousemove on other element. for example, `#app` in this case.
  1. Right click would also trigger mousedown event and cause the drag behavior. I think it is not an expected behavior.

Solution:

 1. add event.button detection

I thought of these because I was making a simple draggableJs. ...

publish an npm package

hi , the plain-draggable is a very useful project for our work.
but there is a problem that we expect to install your code with npm.
so is there any plan to publish your work to npm?


i'm sorry that i haven't searh the right key word before . already found it . please close this issuse if you see it.

How to set the position

Can it retain the position of the dragged item(s) after page refresh or device reboot (mobile)?

Regards

translate and rotate at a time.

Hi anseki, thank you for the awesome library . I added drag function to the div element , the same element i need to rotate , am unable to rotate the div element because of inline transform: translate style . how can i chain the css style like this with both rotate and drag functionality.
.rotate {
transform-origin: left;
transform: translate(x, y) rotate(90deg) ;
}

issue with Keep the Original Position and Mouse Coordinates after CSS scale

Thank you for your excellent script.

I run into some problems after i use css transform scale.

When dragging, the position of target draggable element is not coordinated with mouse cursor position.
Draggable element is just like a car drifting.

In a scrollable area, when i scroll this area, the draggable element will deviate from the original position.
The draggable element can't keep its original position after scrolling the scrollable area.

https://jsfiddle.net/qwqaq/0ufxjm3e/

like this ↓

Could you please help me on this issue? Thank you very much! πŸ˜€πŸ˜€πŸ˜€

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.