lsgraphics-cropper
v1.0.35
Published
Image and video cropper component with crop and tile modes
Maintainers
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 // высота области кадрирования в источнике