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

@feugene/granularity-media

v0.7.1

Published

Media components for @feugene/granularity: image cropping, camera capture, code scanning and video playback.

Readme

@feugene/granularity-media

Работа с изображением, камерой и видео для @feugene/granularity. Четыре компонента, и они стоят рядом по сценарию: GrCameraCapture снимает кадр камерой, GrImageCrop выбирает кадр из уже существующей картинки, GrCodeScanner читает камерой QR и штрихкоды, GrVideoPlayer проигрывает готовое видео своими элементами управления.

yarn add @feugene/granularity-media

Своих зависимостей у пакета нет: кадрирование держится на Canvas и браузерных API, а не на библиотеке. Наружу объявлены только peer — ядро, пресет и Vue.

Быстрый старт

<script setup lang="ts">
import { ref, useTemplateRef } from 'vue'
import { GrImageCrop } from '@feugene/granularity-media'

const file = ref<File | null>(null)
const cropper = useTemplateRef('cropper')

async function save() {
  const blob = await cropper.value?.crop()
  if (blob)
    await fetch('/api/avatar', { method: 'POST', body: blob })
}
</script>

<template>
  <GrImageCrop ref="cropper" :src="file" shape="circle" :output="{ width: 256, height: 256 }" />
  <button @click="save">
Сохранить
</button>
</template>

Рамка неподвижна, двигается картинка

Обратная модель — рамка ездит по изображению — требует двух жестов вместо одного, а её угловые ручки на телефоне меньше пальца. Здесь жест один: тянуть картинку, а увеличение живёт отдельным контролом (по умолчанию GrSlider ядра, заменяется слотом #controls).

Соотношение сторон кадра задаёт приложение (aspectRatio), а не пользователь: место, куда картинка потом встанет, известно заранее.

Кадр считается в пикселях исходника

Без output результат получает размер захваченной области исходного файла, а не окна на экране: окно почти всегда меньше картинки, и вывод по нему молча ополовинил бы разрешение. Нужен точный размер — он задаётся явно.

Что внутри

| | | | --- | --- | | Жест | перетаскивание на useDragGesture ядра: оборванный жест откатывается, а не коммитит | | Клавиатура | стрелки двигают кадр, +/- меняют увеличение, Home сбрасывает | | Экспорт | crop() отдаёт Blob; тип и качество — output.type, output.quality | | Формы кадра | прямоугольник и круг (shape="circle" — маска показа, результат остаётся прямоугольным) | | Серверный рендер | браузерного API в setup нет, оборачивать в <ClientOnly> не нужно |

Подключение к сборке

Провайдер — рядом с ядром, резолвер — перед жадным GranularityResolver():

presetGranularNode({
  providers: [granularityProvider, granularityMediaProvider],
  components: ['@feugene/granularity-media:GrImageCrop'],
})

Локали подключаются тем же createFintI18n, что и словари ядра — блок GR_MEDIA_I18N_BLOCK.

Снимок камерой

<GrCameraCapture :aspect-ratio="1" @capture="blob => upload(blob)" />

Камера включается только по кнопке: запрос разрешения, всплывший сам по себе, отклоняют не глядя — а второй раз браузер уже не спросит, потому что решение запоминается для сайта целиком.

Отказ разведён на четыре состояния (denied, missing, busy, insecure), потому что действия пользователя в них разные: «разрешите доступ» при отсутствующей камере отправляет искать настройку, которой нет, а на http:// объекта navigator.mediaDevices не существует вовсе.

Превью фронтальной камеры зеркальное, снимок — нет: иначе текст на визитке или в документе уехал бы в зазеркалье.

Чтение кодов

<GrCodeScanner :formats="['qr_code']" @detect="codes => open(codes[0].value)" />

Декодера пакет не несёт. Нативный BarcodeDetector есть в Chrome и Edge, а Safari и Firefox закрываются детектором, который передаёт приложение (:detector): встроить библиотеку значило бы навязать самую тяжёлую зависимость и тем, кто взял один кроп. Готовый рецепт на @zxing/browser — на странице компонента.

Один код в кадре даёт одно событие: камера отдаёт десятки кадров в секунду, и без фильтра приложение оформило бы двадцать заказов вместо одного. Режим приёмки, где сканируют одинаковые упаковки подряд, включается continuous.

Видео со своими элементами управления

<GrVideoPlayer :src="clip" poster="/preview.jpg" muted />

Нативные controls в каждом браузере выглядят по-своему и не знают ни про темы, ни про размеры дизайн-системы. Полный экран запрашивается у рамки, а не у <video>: иначе браузер подменил бы эти элементы управления своими — вместе с клавиатурой и подписями.

Документация