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

lsgraphics-cropper

v1.0.35

Published

Image and video cropper component with crop and tile modes

Readme

Cropper Component Library

Гибкая библиотека для кадрирования изображений и видео с поддержкой двух режимов: Crop и Tile.

📦 Установка

npm install lsgraphics-cropper
# или
yarn add lsgraphics-cropper

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

import {Cropper, CropperProvider} from "lsgraphics-cropper"

function App() {
    return (
        <CropperProvider>
            <Cropper
                materialIndex={0}
                showFileInput={true}
                showZoom={true}
                showSizeInputs={true}
                showFileSizeAlert={true}
                showModeSwitcher={true}
                showMuteControl={true}
                showLoopControl={true}
                showVideoControls={true}
                showProcessingControls={true}
                canvasWidth={550}
                canvasHeight={550}
            />
        </CropperProvider>
    )
}

📚 Основные компоненты и хуки

CropperProvider

Контекстный провайдер для управления состоянием. Обязательно оборачивайте все Cropper компоненты.

import {CropperProvider} from "lsgraphics-cropper"

function App() {
    return (
        <CropperProvider>
            {/* Все Cropper компоненты и хуки должны быть внутри */}
            <YourComponents />
        </CropperProvider>
    )
}

⚠️ Все хуки работают только внутри <CropperProvider>!

Cropper

Главный компонент с готовым UI.

Props

| Prop | Type | Default | Описание | | ------------------------ | --------------------- | ------------ | ------------------------------------------ | | materialIndex | number | required | Уникальный индекс материала | | canvasWidth | number | 250 | Ширина холста (canvas) | | canvasHeight | number | 250 | Высота холста (canvas) | | showFileInput | boolean | false | Показать input для загрузки файла | | showSizeInputs | boolean | false | Показать поля для изменения размеров | | showModeSwitcher | boolean | false | Показать переключатель режимов (Crop/Tile) | | showZoom | boolean | false | Показать слайдер зума | | showVideoControls | boolean | false | Показать кнопку Play/Pause для видео | | showProcessingControls | boolean | false | Показать кнопку обработки видео | | showMuteControl | boolean | false | Показать чекбокс Mute | | showLoopControl | boolean | false | Показать чекбокс Loop | | showFileSizeAlert | boolean | false | Показать предупреждение о размере файла | | className | string | | CSS класс для контейнера | | cropFrameClassName | string | | CSS класс для canvas | | zoomSliderClassName | string | | CSS класс для слайдера | | controlsClassName | string | | CSS класс для контролов | | style | React.CSSProperties | | Inline стили |

🎣 Headless Хуки

Для создания кастомного UI используйте хуки.

useCropperLogic

Главный хук для управления всей логикой кроппинга.

import {useCropperLogic} from "lsgraphics-cropper"

const {
    // Состояние
    imageState,
    cropState,
    mode,
    cropParams,
    isProcessing,
    downloadUrl,
    uploadProgress,

    // Действия
    handleImageUpload,
    handleVideoReset,
    handleZoomChange,
    handleDimensionChange,
    handleModeSwitch,
    handleDragStart,
    handleDrag,
    handleDragEnd,
    handleMouseLeave,
    processVideo,
    downloadProcessedVideo,
    setVideoLoop,
    setVideoMuted,
    setVideoPlaying,

    // Ссылки
    mainCanvasRef,

    // Конфигурация
    minZoom,
    maxZoom,
    step,
} = useCropperLogic(materialIndex, canvasWidth?, canvasHeight?)

⚠️ Важно: Если вы используете одновременно useCropperLogic и компонент <Cropper> для одного материала, передавайте одинаковые размеры canvasWidth и canvasHeight в оба места:

// ❌ Неправильно
const cropperLogic = useCropperLogic(0)
<Cropper materialIndex={0} canvasWidth={500} canvasHeight={350} />

// ✅ Правильно
const cropperLogic = useCropperLogic(0, 500, 350)
<Cropper materialIndex={0} canvasWidth={500} canvasHeight={350} />

Параметры

| Параметр | Type | Описание | | --------------- | -------- | --------------------------- | | materialIndex | number | Уникальный индекс материала | | canvasWidth | number | Ширина холста (опционально) | | canvasHeight | number | Высота холста (опционально) |

Возвращаемые значения

| Свойство | Type | Описание | | ------------------------ | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | | Состояние | | | | imageState | ImageState | Состояние изображения (status, position, zoom, dimensions, file, videoLoop, videoMuted, videoPlaying) | | cropState | CropState | Состояние кадрирования (dimensions, isDragging) | | mode | 'crop' \| 'tile' | Текущий режим | | cropParams | CropParams \| null | Параметры для обработки на бэкенде/Three.js | | isProcessing | boolean | Идет ли обработка видео | | downloadUrl | string \| null | URL обработанного видео | | uploadProgress | number | Прогресс загрузки (0-100) | | Действия | | | | handleImageUpload | (file: File) => void | Загрузить изображение/видео | | handleVideoReset | () => void | Сбросить видео на начало и поставить на паузу | | handleZoomChange | (zoom: number) => void | Изменить зум | | handleDimensionChange | (dim: 'width'\|'height', value: string) => void | Изменить размеры области кадрирования | | handleModeSwitch | (mode: 'crop'\|'tile') => void | Переключить режим | | handleDragStart | (e: React.MouseEvent) => void | Начать перетаскивание | | handleDrag | (e: React.MouseEvent) => void | Перетаскивание | | handleDragEnd | () => void | Завершить перетаскивание | | handleMouseLeave | () => void | Мышь покинула canvas | | processVideo | () => Promise<void> | Обработать видео на сервере | | downloadProcessedVideo | (url: string) => void | Скачать обработанное видео | | setVideoLoop | (loop: boolean) => void | Программно включить/выключить loop | | setVideoMuted | (muted: boolean) => void | Программно включить/выключить mute | | setVideoPlaying | (playing: boolean) => void | Программно запустить/остановить видео | | Ссылки | | | | mainCanvasRef | RefObject<HTMLCanvasElement> | Ref для canvas элемента | | Конфигурация | | | | minZoom | number | Минимальный зум | | maxZoom | number | Максимальный зум | | step | number | Шаг изменения зума |

Пример кастомного UI

import {
    useCropperLogic,
    useVideoControls,
    syncVideoWithTimeline,
    CropperProvider,
} from "lsgraphics-cropper"
import {useState, useEffect, useRef} from "react"

function CustomCropper() {
    const {
        imageState,
        cropState,
        cropParams,
        handleImageUpload,
        handleVideoReset,
        handleModeSwitch,
        handleZoomChange,
        handleDimensionChange,
        handleDragStart,
        handleDrag,
        handleDragEnd,
        handleMouseLeave,
        processVideo,
        isProcessing,
        uploadProgress,
        mainCanvasRef,
        minZoom,
        maxZoom,
        step,
        setVideoPlaying,
    } = useCropperLogic(0, 400, 300)

    const {
        isVideoPlaying,
        isMuted,
        isVideoLoop,
        toggleVideoPlaying,
        toggleMute,
        toggleLoop,
    } = useVideoControls(0)

    const [timeline, setTimeline] = useState(0)
    const animFrameRef = useRef<number | null>(null)

    // Обновляем таймлайн во время воспроизведения
    useEffect(() => {
        if (!isVideoPlaying) {
            if (animFrameRef.current) cancelAnimationFrame(animFrameRef.current)
            return
        }
        const tick = () => {
            const videos = document.getElementsByClassName(
                "cropVideo"
            ) as HTMLCollectionOf<HTMLVideoElement>
            if (videos.length > 0 && videos[0].duration) {
                setTimeline(videos[0].currentTime / videos[0].duration)
            }
            animFrameRef.current = requestAnimationFrame(tick)
        }
        animFrameRef.current = requestAnimationFrame(tick)
        return () => {
            if (animFrameRef.current) cancelAnimationFrame(animFrameRef.current)
        }
    }, [isVideoPlaying])

    const handleTimelineChange = (e: React.ChangeEvent<HTMLInputElement>) => {
        const progress = Number(e.target.value)
        setTimeline(progress)
        syncVideoWithTimeline(progress)
    }

    const handleReset = () => {
        setTimeline(0)
        handleVideoReset()
        if (isVideoPlaying) toggleVideoPlaying()
    }

    return (
        <div>
            <input
                type="file"
                accept="image/*,video/*"
                onChange={(e) => {
                    const file = e.target.files?.[0]
                    if (file) handleImageUpload(file)
                }}
            />

            {imageState.status === "loaded" && (
                <>
                    {!imageState.videoUrl && (
                        <div>
                            <button onClick={() => handleModeSwitch("crop")}>
                                Crop Mode
                            </button>
                            <button onClick={() => handleModeSwitch("tile")}>
                                Tile Mode
                            </button>
                        </div>
                    )}

                    <canvas
                        ref={mainCanvasRef}
                        width={400}
                        height={300}
                        onMouseDown={handleDragStart}
                        onMouseMove={handleDrag}
                        onMouseUp={handleDragEnd}
                        onMouseLeave={handleMouseLeave}
                    />

                    <input
                        type="range"
                        value={imageState.zoom}
                        min={minZoom}
                        max={maxZoom}
                        step={step}
                        onChange={(e) =>
                            handleZoomChange(Number(e.target.value))
                        }
                    />

                    {imageState.videoUrl && (
                        <>
                            {/* Таймлайн */}
                            <input
                                type="range"
                                min={0}
                                max={1}
                                step={0.001}
                                value={timeline}
                                onChange={handleTimelineChange}
                                style={{width: "100%"}}
                            />

                            <label>
                                <input
                                    type="checkbox"
                                    checked={isMuted}
                                    onChange={toggleMute}
                                />
                                Mute
                            </label>

                            <label>
                                <input
                                    type="checkbox"
                                    checked={isVideoLoop}
                                    onChange={toggleLoop}
                                />
                                Loop
                            </label>

                            <button onClick={toggleVideoPlaying}>
                                {isVideoPlaying ? "Pause" : "Play"}
                            </button>

                            <button onClick={handleReset}>Reset</button>

                            <button
                                onClick={processVideo}
                                disabled={isProcessing}
                            >
                                {isProcessing
                                    ? "Processing..."
                                    : "Process Video"}
                            </button>
                        </>
                    )}
                </>
            )}
        </div>
    )
}

function App() {
    return (
        <CropperProvider>
            <CustomCropper />
        </CropperProvider>
    )
}

useVideoControls

Хук для управления видео (play/pause/mute/loop).

import {useVideoControls} from "lsgraphics-cropper"

const {
    isVideoPlaying,
    isMuted,
    isVideoLoop,
    toggleVideoPlaying,
    toggleMute,
    toggleLoop,
    isVideoDisabled,
} = useVideoControls(materialIndex)

Возвращаемые значения

| Свойство | Type | Описание | | -------------------- | ------------------------------ | ---------------------------- | | isVideoPlaying | boolean | Играет ли видео | | isMuted | boolean | Выключен ли звук | | isVideoLoop | boolean | Включен ли loop | | toggleVideoPlaying | () => void | Переключить play/pause | | toggleMute | () => void | Переключить mute | | toggleLoop | () => void | Переключить loop | | isVideoDisabled | (imageState: any) => boolean | Проверить disabled состояние |

syncVideoWithTimeline

Утилита для синхронизации всех видео с позицией таймлайна. Используется при ручной перемотке.

import {syncVideoWithTimeline} from "lsgraphics-cropper"

syncVideoWithTimeline(0.5) // Устанавливает видео на 50% длительности

| Параметр | Type | Описание | | ---------- | -------- | ------------------------ | | progress | number | Прогресс таймлайна (0-1) |

⚠️ Использовать только для ручной перемотки. При play/pause видео управляется через toggleVideoPlaying или setVideoPlaying.

getVideoElements

Утилита для получения всех видео элементов кропперов.

import {getVideoElements} from "lsgraphics-cropper"

const videos = getVideoElements() // HTMLCollectionOf<HTMLVideoElement>

useCropper

Низкоуровневый хук для прямого доступа к контексту.

import {useCropper} from "lsgraphics-cropper"

const cropper = useCropper(materialIndex)

cropper?.setImageState({zoom: 150})
cropper?.setCropState({dimensions: {width: 3000, height: 2000}})
cropper?.setMode("tile")

⚠️ Используйте только если useCropperLogic недостаточно. Может вернуть null если материал не инициализирован.

useCropperReset

Хук для сброса состояния материалов.

import {useCropperReset} from "lsgraphics-cropper"

const {resetAllMaterials, resetMaterial} = useCropperReset()

resetAllMaterials() // Сбросить все материалы
resetMaterial(0) // Сбросить конкретный материал

Доступные данные из imageState

const {imageState} = useCropperLogic(materialIndex)

imageState.status // 'initial' | 'loaded'
imageState.file // File объект
imageState.imgUrl // Data URL (base64)
imageState.videoUrl // URL видео (только для видео)
imageState.dimensions // {width, height} в пикселях
imageState.position // {x, y} позиция на canvas
imageState.zoom // число (100 = 100%)
imageState.videoPlaying // boolean
imageState.videoMuted // boolean
imageState.videoLoop // boolean
imageState.videoDuration // длительность видео в секундах

// Параметры для бэкенда/Three.js
const {cropParams} = useCropperLogic(materialIndex)

cropParams.offsetX // 0-1
cropParams.offsetY // 0-1
cropParams.repeatX // 0-1
cropParams.repeatY // 0-1
cropParams.mode // 'crop' | 'tile'
cropParams.cropWidth // целевая ширина
cropParams.cropHeight // целевая высота
cropParams.sourceX // координата X источника
cropParams.sourceY // координата Y источника
cropParams.sourceCropWidth // ширина области кадрирования в источнике
cropParams.sourceCropHeight // высота области кадрирования в источнике