跳转到内容

ImageCropper 图片裁切

用于选择图片中需要保留的区域。

用法

拖动裁切区域或调整把手

山谷与湖泊风景图

组件结构

加粗的是必需部件。

data-scope="image-cropper"root · viewport · image · crop-area · crop-handle · grid · zoom-slider · rotate-slider · hidden-input

示例

固定比例

以 16:9 裁切封面

山谷与湖泊风景图

圆形裁切

以 1:1 裁切头像

山谷与湖泊风景图

缩放与旋转

使用内置滑块调整视图

山谷与湖泊风景图

禁用

禁用后不可调整

山谷与湖泊风景图

设计指引

何时使用

  • 裁切头像、封面或缩略图。
  • 需要固定比例的图片输出。

何时不用

特性

  • 使用源图自然像素记录裁切矩形。
  • 支持拖动、八方向调整和键盘微调。
  • 边缘把手显示为贴住裁切框边框的圆端短条;角部使用与可调容器相同的单拐角圆弧。
  • 支持固定宽高比、圆形遮罩、缩放和旋转。
  • 支持受控裁切区域和原生表单提交。
  • onValueChangeEnd 在一次调整结束时触发。

组合

最佳实践

  • 为裁切区域设置合理的最小尺寸。
  • 头像使用 1:1 比例和圆形遮罩。
  • 在调整结束或确认时生成裁切结果。
  • 跨域图片应在加载前配置 crossorigin

反模式

  • 在每次位置变化时生成图片或请求服务端。
  • 使用过小且无法键盘聚焦的调整把手。

API 参考

产物

自定义元素<xh-image-cropper>
Vue 组件XhImageCropperCropArea XhImageCropperCropHandle XhImageCropperGrid XhImageCropperHiddenInput XhImageCropperImage XhImageCropperRoot XhImageCropperRotateSlider XhImageCropperViewport XhImageCropperZoomSlider
组合式函数useImageCropper
状态机imageCropperMachine
皮肤@xihan-ui/styles/image-cropper.css

Props

属性类型必填说明
srcstring图片地址,原样写到 image 部件的 src 上。
altstring被裁切图片的替代文本,原样写到 image 部件的 alt 上。 未提供时 image 部件写 alt="":读屏跳过该图片,不朗读地址。
aspectRationumber | null宽高比(宽 ÷ 高)。提供后改尺寸时另一条边随之计算;null 与未提供都表示不锁定比例。 非有限数与非正数按不锁定处理。
valueImageCropperRect裁切矩形。提供即受控:内部不再自行修改,只发 onValueChange。
defaultValueImageCropperRect
minWidthnumber裁切框的最小宽度,自然像素,默认 0。
minHeightnumber裁切框的最小高度,自然像素,默认 0。
zoomnumber显示缩放倍率,默认 1。提供即受控:setZoom 只发 onZoomChange。
defaultZoomnumber
minZoomnumber缩放滑杆的下限,默认 1。只约束滑杆,不夹取 setZoom。
maxZoomnumber缩放滑杆的上限,默认 3。只约束滑杆,不夹取 setZoom。
zoomStepnumber缩放滑杆的步长,默认 0.01。
rotationnumber显示旋转角度,单位度,默认 0。提供即受控:setRotation 只发 onRotationChange。 缩放与旋转只改变图片与裁切框的呈现,裁切矩形与源图像素的对应关系不变。
defaultRotationnumber
minRotationnumber旋转滑杆的下限,默认 -180。
maxRotationnumber旋转滑杆的上限,默认 180。
rotationStepnumber旋转滑杆的步长,默认 1。
shapeImageCropperShape裁切框外形,默认 rect。
disabledboolean禁用:裁切框与把手退出 Tab 序列,指针与键盘都不可修改,也不参与表单提交。
readOnlyboolean只读:仍可聚焦与被读屏朗读,不可修改。
namestring表单字段名;提供后才参与提交,值序列化为 x,y,width,height
translationsPartial<ImageCropperTranslations>
onValueChange(details: ImageCropperValueChangeDetails) => void每次裁切矩形变化都发出;拖动过程中连续发出。
onValueChangeEnd(details: ImageCropperValueChangeEndDetails) => void只在一次拖动结束时发出一次,适合用于裁切导出。
onZoomChange(details: ImageCropperZoomChangeDetails) => void缩放变化意图;受控时是唯一出口。
onRotationChange(details: ImageCropperRotationChangeDetails) => void旋转变化意图;受控时是唯一出口。

事件

自定义元素将载荷放在 detail;Vue 使用同名 emit。

事件载荷说明
value-changeImageCropperValueChangeDetails裁切矩形变化(拖动途中连续发出);detail 为 { value: { x, y, width, height } }
value-change-endImageCropperValueChangeEndDetails一次指针拖动松开时发出一次,一次方向键微调也发出一次;detail 为 { value: { x, y, width, height } }
zoom-changeImageCropperZoomChangeDetails缩放倍率变化;detail 为 { zoom: number }
rotation-changeImageCropperRotationChangeDetails旋转角度变化;detail 为 { rotation: number }

插槽

仅列出带载荷的插槽。

Vue 组件插槽载荷说明
XhImageCropperRootdefaultImageCropperRootSlotProps

状态

以下名称仅用于内部状态机。

状态dragging · idle · resizing

事件VALUE.SET · ZOOM.SET · ROTATE.SET · IMAGE.LOAD · CROP.NUDGE · HANDLE.NUDGE · DRAG.START · RESIZE.START · DRAG.MOVE · DRAG.END · FORM.RESET

判据canEdit

connect API

getXxxProps() 返回对应部件的宿主属性。

成员类型说明
valueImageCropperRect当前裁切矩形,自然像素。
zoomnumber
rotationnumber
naturalImageCropperSize图片自然尺寸;未加载完成时为 0×0,此时裁切框无法测量位置。
draggingboolean正在整体拖动裁切框。
resizingboolean正在拉动某个把手。
disabledboolean
readOnlyboolean
getCropRect() => ImageCropperRect获取一份当前裁切矩形的副本,交给 cropToCanvas 出图。
setValue(next: ImageCropperRect) => void
setZoom(next: number) => void
setRotation(next: number) => void
getRootProps() => T['element']
getViewportProps() => T['element']
getImageProps() => T['img']
getCropAreaProps() => T['element']
getCropHandleProps(props: ImageCropperHandleProps) => T['button']
getGridProps() => T['element']裁切框中的构图参考线,纯装饰。
getZoomSliderProps() => T['input']缩放滑杆,原生 range 输入。
getRotateSliderProps() => T['input']旋转滑杆,原生 range 输入。
getHiddenInputProps() => T['input']

无障碍

键盘

规格出处:W3C APG

按键生效条件行为
ArrowLeft / ArrowRight / ArrowUp / ArrowDownfocus on crop-area, 未禁用且非只读裁切框整体平移一个自然像素,尺寸不变;走到图片边界就停住
Shift+ArrowLeft / Shift+ArrowRight / Shift+ArrowUp / Shift+ArrowDownfocus on crop-area, 未禁用且非只读同上,一次走十个自然像素
ArrowLeft / ArrowRight / ArrowUp / ArrowDownfocus on crop-handle, 未禁用且非只读这个把手负责的那条边或那个角挪一个自然像素,对面那条边钉住不动;锁了比例时另一条边跟着算
Shift+ArrowLeft / Shift+ArrowRight / Shift+ArrowUp / Shift+ArrowDownfocus on crop-handle, 未禁用且非只读同上,一次走十个自然像素
Tab / Shift+Tab未禁用裁切框与八个把手各占一个 Tab 停靠点,按文档序依次走过

ARIA

以下属性由 connect 生成。

部件属性
crop-areaaria-disabled'true' | 'false'
crop-areaaria-labellabel.cropArea
crop-arearole'application'
crop-handlearia-disabled'true' | 'false'
crop-handlearia-labellabel.handle(position)
crop-handlearia-valuemaxString(HANDLE_AXIS[position] === 'width' ? natural.wi…
crop-handlearia-valueminString(HANDLE_AXIS[position] === 'width' ? minWidth :…
crop-handlearia-valuenowString(HANDLE_AXIS[position] === 'width' ? value.widt…
crop-handlearia-valuetextlabel.valueText({ ...value })
crop-handlerole'slider'
gridaria-hidden'true'
zoom-slideraria-labellabel.zoomSlider
rotate-slideraria-labellabel.rotateSlider

样式参考

皮肤

@xihan-ui/styles/image-cropper.css 使用 [data-scope="image-cropper"][data-part="root"] 部件选择器,位于 xihan.components 层。覆盖样式使用 xihan.overrides

数据属性

connect 生成;条件不成立时不输出无值属性。

部件属性
rootdata-disabled''(条件成立时才出现)
rootdata-dragging''(条件成立时才出现)
rootdata-readonly''(条件成立时才出现)
rootdata-resizing''(条件成立时才出现)
rootdata-shapeprops.shape
viewportdata-disabled''(条件成立时才出现)
viewportdata-dragging''(条件成立时才出现)
viewportdata-readonly''(条件成立时才出现)
viewportdata-resizing''(条件成立时才出现)
crop-areadata-disabled''(条件成立时才出现)
crop-areadata-dragging''(条件成立时才出现)
crop-areadata-readonly''(条件成立时才出现)
crop-areadata-resizing''(条件成立时才出现)
crop-areadata-shapeprops.shape
crop-handledata-disabled''(条件成立时才出现)
crop-handledata-positionposition
crop-handledata-readonly''(条件成立时才出现)
crop-handledata-resizing''(条件成立时才出现)
griddata-shapeprops.shape
zoom-sliderdata-disabled''(条件成立时才出现)
rotate-sliderdata-disabled''(条件成立时才出现)

CSS 变量

本组件公开覆盖槽由独立皮肤的实际消费位生成;默认来源、作用部件和状态均与 CSS 同源。

变量部件CSS 属性状态默认来源说明
--xh-image-cropper-bgviewportbackgrounddefault--xh-bg-mutedimage-cropper 的 viewport 部件 background 覆盖槽。
--xh-image-cropper-crop-area-radiuscrop-areaborder-radiusshape=round--xh-shape-circleimage-cropper 的 crop-area 部件 border-radius 覆盖槽。
--xh-image-cropper-crop-bordercrop-areaborder-colordefault--xh-bg-surfaceimage-cropper 的 crop-area 部件 border-color 覆盖槽。
--xh-image-cropper-grid-linegridbackground-imagedefault--xh-border-subtleimage-cropper 的 grid 部件 background-image 覆盖槽。
--xh-image-cropper-handle-bgcrop-handlebackground
border
default
is([data-position='nw'], [data-position='ne'], [data-position='sw'], [data-position='se'])
position=ne
position=nw
position=se
position=sw
--xh-bg-surfaceimage-cropper 的 crop-handle 部件 background、border 覆盖槽。
--xh-image-cropper-handle-bg-hovercrop-handlebackground
border
disabled
hover
is([data-position='nw'], [data-position='ne'], [data-position='sw'], [data-position='se'])
not([data-disabled], [data-readonly])
position=ne
position=nw
position=se
position=sw
readonly
--xh-bg-subtleimage-cropper 的 crop-handle 部件 background、border 覆盖槽。
--xh-image-cropper-handle-bg-resizingcrop-handlebackground
border
is([data-position='nw'], [data-position='ne'], [data-position='sw'], [data-position='se'])
position=ne
position=nw
position=se
position=sw
resizing
--xh-bg-brandimage-cropper 的 crop-handle 部件 background、border 覆盖槽。
--xh-image-cropper-handle-bordercrop-handleborderis([data-position='nw'], [data-position='ne'], [data-position='sw'], [data-position='se'])
position=ne
position=nw
position=se
position=sw
--xh-_image-cropper-handle-colorimage-cropper 的 crop-handle 部件 border 覆盖槽。
--xh-image-cropper-handle-lengthcrop-handleblock-size
inline-size
is([data-position='e'], [data-position='w'])
is([data-position='n'], [data-position='s'])
position=e
position=n
position=s
position=w
--xh-space-8image-cropper 的 crop-handle 部件 block-size、inline-size 覆盖槽。
--xh-image-cropper-handle-radiuscrop-handleborder-radiusdefault--xh-shape-pillimage-cropper 的 crop-handle 部件 border-radius 覆盖槽。
--xh-image-cropper-handle-sizecrop-handle
root
block-size
inline-size
inset
default
is([data-position='nw'], [data-position='ne'], [data-position='sw'], [data-position='se'])
position=ne
position=nw
position=se
position=sw
--xh-control-indicator-sizeimage-cropper 的 crop-handle、root 部件 block-size、inline-size、inset 覆盖槽。
--xh-image-cropper-handle-thicknesscrop-handleblock-size
border-block-end-width
border-block-start-width
border-inline-end-width
border-inline-start-width
inline-size
is([data-position='e'], [data-position='w'])
is([data-position='n'], [data-position='s'])
position=e
position=n
position=ne
position=nw
position=s
position=se
position=sw
position=w
--xh-stroke-strongimage-cropper 的 crop-handle 部件 block-size、border-block-end-width、border-block-start-width、border-inline-end-width、border-inline-start-width、inline-size 覆盖槽。
--xh-image-cropper-maskcrop-areabox-shadowdefault--xh-bg-overlayimage-cropper 的 crop-area 部件 box-shadow 覆盖槽。
--xh-image-cropper-slider-accentrotate-slider
zoom-slider
accent-colordefault--xh-bg-brandimage-cropper 的 rotate-slider、zoom-slider 部件 accent-color 覆盖槽。
--xh-image-cropper-slider-wrotate-slider
zoom-slider
inline-sizedefault100%image-cropper 的 rotate-slider、zoom-slider 部件 inline-size 覆盖槽。
--xh-image-cropper-viewport-radiusviewportborder-radiusdefault--xh-shape-surfaceimage-cropper 的 viewport 部件 border-radius 覆盖槽。
--xh-image-cropper-wrootinline-sizedefault100%image-cropper 的 root 部件 inline-size 覆盖槽。

动效

background · border-colortransition 过渡。时长与缓动读动效令牌,改令牌即改全局节奏。

系统开启减弱动效时由令牌层统一收敛,皮肤不另作判断。

RTL

皮肤用逻辑属性排布(inline-start 一族),dir="rtl" 下自动镜像。

Released under The MIT License