npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vue-lasso

v0.1.0

Published

Lasso selection for Vue 3 — select DOM elements with a freeform lasso or marquee rectangle, or cut freehand regions out of images. Headless core, zero dependencies, TypeScript, SSR-safe.

Downloads

39

Readme

vue-lasso

Freeform lasso & marquee selection for Vue 3 — plus a Photoshop-style image lasso that cuts pixels out of any image. Headless core, zero dependencies, fully typed, SSR-safe.

  • 🪢 <LassoArea> — drag a freeform lasso (or marquee rectangle) around DOM elements, get them in v-model
  • ✂️ <LassoImage> — draw on an image, get the region as canvas / Blob / data URL / CSS clip-path
  • 🧠 HeadlessuseLasso / usePointerLasso composables when you want your own rendering
  • 📐 vue-lasso/core — the pure geometry & selection math, importable without Vue
  • ⌨️ File-manager modifiers: Shift add · Ctrl/⌘ toggle · Alt subtract · Esc cancel
  • 📱 Touch: long-press to lasso, so normal page scrolling keeps working; pen supported
  • 🚀 ~10 kB gzipped, no runtime dependencies, tree-shakable

Install

npm install vue-lasso
// Global registration (optional — everything is also a named export)
import { createApp } from 'vue'
import VueLasso from 'vue-lasso'

createApp(App).use(VueLasso).mount('#app')
// registers <LassoArea>, <LassoImage> and the v-lasso-item directive

Quick start — select elements

<script setup lang="ts">
import { ref } from 'vue'
import { LassoArea, vLassoItem } from 'vue-lasso'

const photos = ref([{ id: 1, name: 'A' }, { id: 2, name: 'B' } /* … */])
const selected = ref([])
</script>

<template>
  <LassoArea v-model="selected">
    <div
      v-for="photo in photos"
      :key="photo.id"
      v-lasso-item="photo"
      :class="{ active: selected.includes(photo) }"
    >
      {{ photo.name }}
    </div>
  </LassoArea>
</template>

Drag anywhere in the area to draw a lasso — every element marked with v-lasso-item (or a data-lasso-item attribute) that the shape touches lands in selected. Clicking an item selects just that item; clicking empty space clears.

Three ways to mark selectable items:

| How | Selected values | | --- | --- | | v-lasso-item="anything" | whatever you bound (objects, ids, …) | | <div data-lasso-item="42"> | the attribute string ("42") | | data-lasso-item (empty) | the DOM element itself |

Quick start — cut a region out of an image

<script setup lang="ts">
import { LassoImage, type ImageLassoSelection } from 'vue-lasso'

async function onSelect(selection: ImageLassoSelection) {
  const url = selection.toDataURL()            // PNG of the cut-out region
  const blob = await selection.toBlob()        // …or a Blob
  document.body.style.clipPath = selection.clipPath // …or a CSS polygon()
}
</script>

<template>
  <LassoImage src="/photo.jpg" @select="onSelect" />
</template>

The drawn path stays visible (marching ants) until the next gesture, a tap, or clear(). Cutouts are computed lazily at the image's natural resolution and cropped to the path's bounding box, transparent outside the path.

CORS note: to export pixels from cross-origin images, serve them with CORS headers and set crossorigin="anonymous".

<LassoArea> API

Props

| Prop | Type / default | Description | | --- | --- | --- | | v-model | unknown[] | Selected item values | | mode | 'lasso' \| 'rect' = 'lasso' | Freeform path or marquee rectangle | | hitMode | 'intersect' \| 'contain' \| 'center' = 'intersect' | Overlap / fully-inside / center-inside | | commit | 'live' \| 'end' = 'live' | Update v-model while dragging, or only on release | | disabled | boolean = false | | | itemSelector | string = '[data-lasso-item]' | Which descendants are selectable | | items | () => LassoItem[] | Custom item source (rects/points in content space) — for canvas, charts, virtual lists | | modifierKeys | boolean = true | Shift/Ctrl/Alt behaviors | | clearOnClick | boolean = true | Plain click on empty space clears | | clickToSelect | boolean = true | Click selects a single item | | dragThreshold | number = 4 | px of movement before a drag becomes a lasso | | touch | 'hold' \| 'immediate' \| 'none' = 'hold' | Touch strategy (see Touch) | | touchHoldMs | number = 300 | Long-press duration for 'hold' | | autoScroll | boolean = true | Scroll container/window near edges while dragging | | autoScrollEdge / autoScrollSpeed | 28 / 22 | Edge zone (px) and max speed (px/frame) | | tag | string = 'div' | Root element tag |

Events

| Event | Payload | | --- | --- | | start | { event } | | change | { selected, added, removed, event } — throttled to animation frames | | end | { selected, added, removed, canceled, event } | | item-click | { value, selected, event } |

Slot props & exposed methods

Default slot receives { isSelecting, selected }. Template ref exposes selectAll(), clear(), cancel(), refresh() (re-measure mid-drag), isSelecting.

While dragging, items currently inside the shape get a data-lasso-hit attribute — style the live preview with plain CSS:

.card[data-lasso-hit] { outline: 2px dashed #528fff; }

<LassoImage> API

Props: src (required), alt, crossorigin, mode ('lasso' | 'rect' — rect gives you a crop box), feather (px, soft edge), keepSelection (true), disabled, touch, touchHoldMs, dragThreshold.

Events: select(ImageLassoSelection), change({ path, event }), start, cancel, load, error.

interface ImageLassoSelection {
  path: Point[]       // displayed-image coordinates
  imagePath: Point[]  // natural-pixel coordinates
  bounds: Rect        // natural-pixel bounding box of the cutout
  clipPath: string    // CSS polygon(evenodd, …) in percentages
  toCanvas(opts?: { feather?: number; invert?: boolean }): HTMLCanvasElement
  toMaskCanvas(opts?): HTMLCanvasElement     // white-on-transparent alpha mask
  toDataURL(opts?: { type?: string; quality?: number; feather?; invert? }): string
  toBlob(opts?): Promise<Blob>
}

invert: true keeps everything except the drawn region. The standalone helpers are exported too: createCutout(image, path, opts), createMaskCanvas, pathToClipPath.

Headless usage

useLasso gives you the full selection engine without the component:

import { ref } from 'vue'
import { useLasso } from 'vue-lasso'

const container = ref<HTMLElement>()
const { selected, isSelecting, pathD, selectAll, clear } = useLasso(container, {
  hitMode: 'center',
  onEnd: ({ selected }) => console.log(selected),
})

For chart/canvas scenarios, supply your own items (content-space rects or points):

useLasso(container, {
  items: () => dataPoints.map((d) => ({ value: d, point: { x: d.px, y: d.py } })),
})

usePointerLasso is one level lower — it only turns pointer gestures into a content-space path (points, selectionRect, pathD, callbacks), and is what both components are built on. Render the shape however you like.

vue-lasso/core exports the pure math with no Vue import: pointInPolygon, polygonIntersectsRect, polygonContainsRect, segmentsIntersect, combineSelection, behaviorFromModifiers, …

Styling

A tiny stylesheet is injected automatically on first mount (no CSS import needed). Customize via CSS variables on the container:

| Variable | Default | | | --- | --- | --- | | --lasso-stroke | #528fff | path stroke color | | --lasso-fill | rgba(82,143,255,.12) | path fill | | --lasso-stroke-width | 1.5 | | | --lasso-dash | 5 4 | dash pattern (marching ants) | | --lasso-ants-speed | .5s | animation speed (disabled with prefers-reduced-motion) | | --lasso-z-index | 999 | overlay z-index | | --lasso-cursor | crosshair | cursor while selecting |

Classes: lasso-area, lasso-active (during a gesture), lasso-overlay, lasso-path, lasso-image. Elements matching [data-lasso-ignore], inputs, buttons and links never start a lasso.

Touch behavior

Lassoing and scroll-panning fight over the same gesture. The default touch: 'hold' resolves it the way photo apps do: long-press (300 ms), then drag to lasso, while a normal swipe scrolls the page untouched. Use 'immediate' in UIs that don't scroll, or 'none' to keep touch scroll-only. Mouse and pen are always immediate.

SSR / Nuxt

Everything is SSR-safe: no window/document access at module scope, style injection happens on mount, and v-lasso-item ships getSSRProps so server markup matches the client. Just import and use.

Performance notes

  • Item rects are measured once per gesture and hit-tested against the shape on animation frames only.
  • Lasso points are thinned (minPointDistance) and capped (maxPoints), with polygon-bounds quick rejection before exact math.
  • A few thousand items are fine; for huge virtual lists supply items yourself.
  • Call the exposed refresh() if your layout reflows mid-drag.

FAQ

Esc closed my lasso and my modal. No — while a lasso is active, Esc is intercepted (capture phase) and only cancels the lasso.

A click fires after I finish dragging. One synthetic click after each drag is swallowed automatically so your @click handlers don't double-fire.

Range-select with Shift+click? Shift+click currently adds the clicked item. True range semantics depend on item order — track item-click if you need it.

Roadmap

Polygon (click-to-place-points) mode · magnetic edge snapping for images · spatial index for 10k+ items. PRs welcome.

License

MIT © 2026 Hareram Ray