一个 react-easy-crop 的实现。

当前功能

  • 图像裁剪:基于交互式的宽高比裁剪区域,支持拖拽和调整大小功能
  • 旋转:以 90 度为增量旋转图像
  • 缩放控制:支持放大/缩小,并可配置最小/最大限制
  • 宽高比:设置并保持特定的宽高比
  • 翻转控制:水平和垂直翻转图像
  • 状态管理:使用 React Context 进行集中式状态管理
  • 自定义重置状态:为重置操作配置自定义初始状态

未来功能

  • [ ] 自由形式裁剪

安装

安装模块

npm install @wordpress/image-cropper --save

API

组件

  • ImageCropper – 提供裁剪画布的主裁剪组件。
interface ImageCropperProps {
    src: string; // 图像源 URL
    onLoad?: ( mediaSize: MediaSize ) => void; // 图像加载时带有媒体尺寸的回调函数
    minZoom?: number; // 最小缩放级别(默认值:1)
    maxZoom?: number; // 最大缩放级别(默认值:5)
}
  • ImageCropperProvider – 用于状态管理的上下文提供者。该组件实现了 React Context 模式,以便在整个应用程序中共享裁剪器状态。它管理所有内部裁剪器状态,包括裁剪位置、缩放级别、旋转、翻转变换、宽高比和媒体尺寸。任何需要读取或修改裁剪器状态的组件都必须是此提供者的后代。该提供者还处理状态持久化,并提供重置到自定义初始状态的方法。

Hooks

  • useImageCropper – 提供对所有裁剪器状态和方法的访问。
import { useImageCropper } from '@wordpress/image-cropper';

const {
    // 统一的状态对象
    cropperState, // 完整的裁剪器状态 { crop, croppedArea, croppedAreaPixels, zoom, rotation, aspectRatio, flip, mediaSize }
    setCropperState, // 一次性设置多个状态属性

    // 操作
    reset, // 重置所有更改(如果设置了 resetState,则使用它)
    setResetState, // 设置自定义重置状态
    getCroppedImage, // 将裁剪后的图像作为 data URL 获取

    // 重置状态
    resetState, // 当前的重置状态配置
    isDirty, // 基于 resetState 或默认设置,状态是否已更改
} = useImageCropper();

类型

  • ImageCropperState – 裁剪器数据的接口
  • ImageCropperProps – ImageCropper 组件的 Props 接口
  • ImageCropperContextValue – 上下文值接口
  • Flip – 翻转状态接口
  • Point, Area, MediaSize – 从 react-easy-crop 重新导出

工具函数

  • normalizeRotation – 将旋转值规范化为 0-360 度的工具函数
import { normalizeRotation } from '@wordpress/image-cropper';

const normalized = normalizeRotation( -90 ); // 返回 270
const normalized2 = normalizeRotation( 450 ); // 返回 90

用法

基本实现

图像裁剪器提供核心裁剪功能,但不包含任何内置 UI 控件。你必须使用 useImageCropper hook 实现自己的工具。

import {
    ImageCropper,
    ImageCropperProvider,
    useImageCropper,
} from '@wordpress/image-cropper';

function ImageEditor() {
    return (
        <ImageCropperProvider>
            <div className="image-editor">
                <ImageCropper
                    src="https://example.com/image.jpg"
                    className="image-cropper"
                    onLoad={ ( mediaSize ) =>
                        console.log( 'Image loaded', mediaSize )
                    }
                />
                <ImageEditorTools />
            </div>
        </ImageCropperProvider>
    );
}

function ImageEditorTools() {
    const { cropperState, setCropperState, reset, setResetState } =
        useImageCropper();

    const { zoom, rotation, aspectRatio, flip, mediaSize, croppedArea } =
        cropperState;

    const handleSave = () => {
        console.log( 'Cropper state:', {
            crop: croppedArea,
            zoom,
            rotation,
            aspectRatio,
            flip,
        } );
        // 将裁剪状态应用到你的图像上
    };

    return (
        <div className="image-editor-tools">
            <button
                onClick={ () => setCropperState( { rotation: rotation + 90 } ) }
            >
                向右旋转
            </button>
            <button
                onClick={ () => setCropperState( { rotation: rotation - 90 } ) }
            >
                向左旋转
            </button>
            <button
                onClick={ () =>
                    setCropperState( {
                        flip: { ...flip, horizontal: ! flip.horizontal },
                    } )
                }
            >
                水平翻转
            </button>
            <button
                onClick={ () =>
                    setCropperState( {
                        flip: { ...flip, vertical: ! flip.vertical },
                    } )
                }
            >
                垂直翻转
            </button>
            <button onClick={ handleSave }>应用更改</button>
            <button onClick={ reset }>重置</button>
        </div>
    );
}

带有自定义重置状态的高级用法

你可以配置一个自定义初始状态,该状态将在调用重置函数时使用:

function ImageEditorWithCustomReset() {
    const { setResetState, reset } = useImageCropper();

    // 设置自定义重置状态
    useEffect( () => {
        setResetState( {
            rotation: 90,
            zoom: 1.5,
            aspectRatio: 16 / 9,
            flip: { horizontal: false, vertical: false },
        } );
    }, [ setResetState ] );

    return (
        <div>
            { /* 你的裁剪器组件 */ }
            <button onClick={ reset }>重置为自定义状态</button>
        </div>
    );
}