feat: 裁剪

This commit is contained in:
2026-04-22 10:20:49 +08:00
parent 4394158d30
commit 19ef5b031e
2 changed files with 124 additions and 82 deletions

View File

@@ -7,15 +7,13 @@
crossOrigin="Anonymous"
:autoCrop="true"
:fixedNumber="ratio"
:fixed="type !== 'apparel'"
:fixed="type !== 'apparel' && isProduct"
movable
centerBox
:fixedBox="fixedBox"
@realTime="onChange"
v-bind="$attrs"
outputType="png"
:autoCropWidth="type === 'cover' ? 297 : 242"
:autoCropHeight="autoCropHeight"
v-bind="bindProps"
></VueCropper>
</div>
<div class="clip_opterate">
@@ -72,6 +70,17 @@ const autoCropHeight = computed(() => {
return height
})
const bindProps = computed(() => {
// :autoCropWidth="isProduct ? undefined : type === 'cover' ? 297 : 242"
// :autoCropHeight="isProduct ? undefined : autoCropHeight"
if (props.isProduct) {
return {
autoCropHeight: autoCropHeight.value,
autoCropWidth: props.type === "cover" ? 297 : 242
}
}
})
const onChange = (data) => {
if (attrs.onChange) {
getCropUrl().then((url) => attrs.onChange(url))
@@ -95,8 +104,7 @@ const createCropLabel = ({ text, top, className }) => {
label.textContent = text
label.style.top = top
label.style.left = className === "label-v" ? "50%" : "0"
label.style.transform =
className === "label-v" ? "translate(-50%, -50%)" : "translateY(-50%)"
label.style.transform = className === "label-v" ? "translate(-50%, -50%)" : "translateY(-50%)"
return label
}
@@ -126,7 +134,6 @@ const injectCropLabel = () => {
if (!cropperBox) return false
clearCropLabels(cropperBox)
;(cropLabelMap[props.type] || []).forEach((config) => {
cropperBox.appendChild(createCropLabel(config))
})
@@ -273,75 +280,89 @@ defineExpose({
}
}
&.is-product {
&.is-product {
.image-clip-body {
width: 45.7rem;
height: 45.7rem;
:deep(.cropper-modal) {
background: transparent;
}
:deep(.vue-cropper .cropper-view-box) {
position: relative;
overflow: visible !important;
/* 原有的蓝色边框outline由组件控制这里不干涉 */
}
:deep(.vue-cropper .cropper-view-box::after) {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 9; /* 位于图片之上,但在控制点之下 */
background-image: none;
background-repeat: no-repeat;
}
}
&[data-crop-type="cover"] {
.image-clip-body {
width: 45.7rem;
height: 45.7rem;
:deep(.cropper-modal) {
background: transparent;
}
:deep(.vue-cropper .cropper-view-box) {
position: relative;
overflow: visible !important;
/* 原有的蓝色边框outline由组件控制这里不干涉 */
}
:deep(.vue-cropper .cropper-view-box::after) {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 9; /* 位于图片之上,但在控制点之下 */
background-image: none;
background-repeat: no-repeat;
background-image:
linear-gradient(to right, #4ba5ff 50%, transparent 50%),
linear-gradient(to right, #4ba5ff 50%, transparent 50%),
linear-gradient(to right, #4ba5ff 50%, transparent 50%),
linear-gradient(to bottom, #4ba5ff 50%, transparent 50%);
background-repeat: repeat-x, repeat-x, repeat-x, repeat-y;
background-size:
8px 1px,
8px 1px,
8px 1px,
1px 8px;
background-position:
0 2.67%,
0 63.47%,
0 92.8%,
50% 0;
}
}
}
&[data-crop-type="cover"] {
.image-clip-body {
:deep(.vue-cropper .cropper-view-box::after) {
background-image:
linear-gradient(to right, #4ba5ff 50%, transparent 50%),
linear-gradient(to right, #4ba5ff 50%, transparent 50%),
linear-gradient(to right, #4ba5ff 50%, transparent 50%),
linear-gradient(to bottom, #4ba5ff 50%, transparent 50%);
background-repeat: repeat-x, repeat-x, repeat-x, repeat-y;
background-size: 8px 1px, 8px 1px, 8px 1px, 1px 8px;
background-position: 0 2.67%, 0 63.47%, 0 92.8%, 50% 0;
}
&[data-crop-type="mainProductImage"],
&[data-crop-type="sketch"] {
.image-clip-body {
:deep(.vue-cropper .cropper-view-box::after) {
background-image:
linear-gradient(to right, #4ba5ff 50%, transparent 50%),
linear-gradient(to right, #4ba5ff 50%, transparent 50%),
linear-gradient(to bottom, #4ba5ff 50%, transparent 50%);
background-repeat: repeat-x, repeat-x, repeat-y;
background-size:
8px 1px,
8px 1px,
1px 8px;
background-position:
0 2.67%,
0 97.6%,
50% 0;
}
}
}
&[data-crop-type="mainProductImage"],
&[data-crop-type="sketch"] {
.image-clip-body {
:deep(.vue-cropper .cropper-view-box::after) {
background-image:
linear-gradient(to right, #4ba5ff 50%, transparent 50%),
linear-gradient(to right, #4ba5ff 50%, transparent 50%),
linear-gradient(to bottom, #4ba5ff 50%, transparent 50%);
background-repeat: repeat-x, repeat-x, repeat-y;
background-size: 8px 1px, 8px 1px, 1px 8px;
background-position: 0 2.67%, 0 97.6%, 50% 0;
}
}
}
&[data-crop-type="apparel"] {
.image-clip-body {
:deep(.vue-cropper .cropper-view-box::after) {
background-image: linear-gradient(to bottom, #4ba5ff 50%, transparent 50%);
background-repeat: repeat-y;
background-size: 1px 8px;
background-position: 50% 0;
}
&[data-crop-type="apparel"] {
.image-clip-body {
:deep(.vue-cropper .cropper-view-box::after) {
background-image: linear-gradient(to bottom, #4ba5ff 50%, transparent 50%);
background-repeat: repeat-y;
background-size: 1px 8px;
background-position: 50% 0;
}
}
}
}
}
</style>
<style lang="less">