0.4.2 • Published 3 years ago

svg-drag-select v0.4.2

Weekly downloads
18
License
WTFPL
Repository
github
Last release
3 years ago

svg-drag-select

Add select-on-drag behavior to inline SVG elements.
No dependencies and lightweight (~ 1.8 kB minified gzipped).
Demo

Installation

via npm (with a module bundler)

$ npm i svg-drag-select
import svgDragSelect from "svg-drag-select"

via CDN (jsDelivr)

<script src="https://cdn.jsdelivr.net/npm/svg-drag-select@0.4.2"></script>
<script>/* `window.svgDragSelect` function is available */</script>

Usage and options

  • JavaScript
const {
  cancel,           // cleanup funciton.
                    // please call `cancel()` when the select-on-drag behavior is no longer needed.
  dragAreaOverlay,  // a div element overlaying dragging area.
                    // you can customize the style of this element.
                    // this element has "svg-drag-select-area-overlay" class by default.
} = svgDragSelect({
  // the svg element (required).
  svg: document.getElementById("my-svg"),

  // followings are optional parameters with default values.
  referenceElement: null,     // selects only descendants of this SVGElement if specified.
  selector: "enclosure",      // "enclosure": selects enclosed elements using getEnclosureList().
                              // "intersection": selects intersected elements using getIntersectionList().
                              // function: custom selector implementation

  // followings are optional selection handlers
  onSelectionStart({
    svg,                      // the svg element.
    pointerEvent,             // a `PointerEvent` instance with "pointerdown" type.
                              // (in case of Safari, a `MouseEvent` or a `TouchEvent` is used instead.)
    cancel,                   // cancel() cancels.
  }) {
    // for example: handles mouse left button only.
    if (pointerEvent.button !== 0) {
      cancel()
      return
    }
    // for example: clear "data-selected" attribute
    const selectedElements = svg.querySelectorAll('[data-selected]')
    for (let i = 0; i < selectedElements.length; i++) {
      selectedElements[i].removeAttribute('data-selected')
    }
  },

  onSelectionChange({
    svg,                      // the svg element.
    pointerEvent,             // a `PointerEvent` instance with either a "pointerdown" event or a "pointermove" event.
                              // (in case of Safari, a `MouseEvent` or a `TouchEvent` is used instead.)
    selectedElements,         // selected element array.
    previousSelectedElements, // previous selected element array.
    newlySelectedElements,    // `selectedElements - previousSelectedElements`
    newlyDeselectedElements,  // `previousSelectedElements - selectedElements`
  }) {
    // for example: toggle "data-selected" attribute
    newlyDeselectedElements.forEach(element => element.removeAttribute('data-selected'))
    newlySelectedElements.forEach(element => element.setAttribute('data-selected', ''))
  },

  onSelectionEnd({
    svg,                      // the svg element.
    pointerEvent,             // a `PointerEvent` instance with either a "pointerup" event or a "pointercancel" event.
                              // (in case of Safari, a `MouseEvent` or a `TouchEvent` is used instead.)
    selectedElements,         // selected element array.
  }) {
  },
})

// cleanup for when the select-on-drag behavior is no longer needed
// (including unbinding of the event listeners)
cancel()
  • CSS
/* please setup drag area overlay styles. for example: */
.svg-drag-select-area-overlay {
  border: 1px dotted gray;
  background-color: rgba(255,255,255,.4);
}

Custom Selector

You may need to implement your own selector function because:

  • If "intersection" is specified as selector option,
  • Implementing a good "intersection" selector is so hard for me because stricity and performance are in a trade-off relationship.
    • (BTW, IE 11 seems to have a good SVGSVGElement.prototype.getIntersectionList() implementation...)

The following is a custom selector example written for demo.

const strictIntersectionSelector = ({
  svg,                            // the svg element.
  referenceElement,               // please select only descendants of this SVGElement if specified.
  pointerEvent,                   // a `PointerEvent` instance with either a "pointerdown" event or a "pointermove" event.
                                  // (in case of Safari, a `MouseEvent` or a `TouchEvent` is used instead.)
  dragAreaInClientCoordinate,     // a `SVGRect` that represents the dragging area in client coordinate.
  dragAreaInSvgCoordinate,        // a `SVGRect` that represents the dragging area in svg coordinate.
  dragAreaInInitialSvgCoordinate, // a `SVGRect` that represents the dragging area in initial viewport coordinate of the svg.
  getEnclosures,                  // `getEnclosures()` returns elements enclosed in the dragging area.
  getIntersections,               // `getIntersections()` returns elements intersect the dragging area.
                                  // Chrome, Safari and Firefox checks only bounding box intersection.
}) => getIntersections().filter(element => {
  // the element that the pointer event raised is considered to intersect.
  if (pointerEvent.target === element) {
    return true
  }
  // strictly check only <path>s.
  if (!(element instanceof SVGPathElement)) {
    return true
  }
  // check if there is at least one enclosed point in the path.
  for (let i = 0, len = element.getTotalLength(); i <= len; i += 4 /* arbitrary */) {
    const { x, y } = element.getPointAtLength(i)
    if (
        dragAreaInSvgCoordinate.x <= x && x <= dragAreaInSvgCoordinate.x + dragAreaInSvgCoordinate.width &&
        dragAreaInSvgCoordinate.y <= y && y <= dragAreaInSvgCoordinate.y + dragAreaInSvgCoordinate.height
    ) {
      return true
    }
  }
  return false
})

svgDragSelect({
  selector: strictIntersectionSelector,
  /* ... */
})

License

WTFPL

0.4.2

3 years ago

0.4.1

5 years ago

0.4.0

5 years ago

0.3.1

5 years ago

0.3.0

5 years ago

0.2.0

5 years ago

0.1.2

5 years ago

0.1.1

5 years ago

0.1.0

5 years ago