feat: 裁剪
This commit is contained in:
@@ -28,7 +28,7 @@
|
||||
ref="imageClipRef"
|
||||
v-bind="$attrs"
|
||||
:ratio="data.ratio"
|
||||
:fixedBox="type !== 'apparel'"
|
||||
:fixedBox="data.isProduct ? type !== 'apparel' : false"
|
||||
:url="data.url"
|
||||
:type="type"
|
||||
@change="(v) => (data.preview_url = v)"
|
||||
@@ -47,7 +47,9 @@
|
||||
<span class="icon"><svg-icon name="seller-preview" size="24" /></span>
|
||||
<span class="label">Crop Preview</span>
|
||||
</div>
|
||||
<img :src="data.preview_url" />
|
||||
<div class="preview-image">
|
||||
<img :src="data.preview_url" />
|
||||
</div>
|
||||
<div class="submit" @click="onSubmit">
|
||||
<svg-icon name="seller-dui" size="24" />
|
||||
</div>
|
||||
@@ -175,14 +177,21 @@ defineExpose({
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.crop-wrapper {
|
||||
width: 100%;
|
||||
.tips {
|
||||
text-align: center;
|
||||
color: #585858;
|
||||
font-size: 1.4rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
&.is-product {
|
||||
column-gap: 18.6rem;
|
||||
}
|
||||
.tips {
|
||||
text-align: center;
|
||||
color: #585858;
|
||||
font-size: 1.4rem;
|
||||
font-weight: 400;
|
||||
.crop-wrapper {
|
||||
width: initial;
|
||||
}
|
||||
}
|
||||
> .image-clip {
|
||||
flex: 1;
|
||||
@@ -194,11 +203,13 @@ defineExpose({
|
||||
> .preview {
|
||||
margin-left: 6rem;
|
||||
width: 28rem;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
gap: 2.4rem;
|
||||
min-height: 0;
|
||||
|
||||
> .title {
|
||||
display: flex;
|
||||
@@ -210,32 +221,42 @@ defineExpose({
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
}
|
||||
> img {
|
||||
> .preview-image {
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
> .preview-image > img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
margin-bottom: 3rem;
|
||||
max-height: 100%;
|
||||
}
|
||||
> .submit {
|
||||
margin-top: auto;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
&.is-product {
|
||||
margin-left: 0;
|
||||
height: 100%;
|
||||
|
||||
justify-content: flex-start;
|
||||
img {
|
||||
> .preview-image > img {
|
||||
width: 20.8rem;
|
||||
height: 36.7rem;
|
||||
margin-bottom: 0;
|
||||
box-shadow: 4px 4px 16px 0px #0000000f;
|
||||
border: 1px solid #ededed;
|
||||
}
|
||||
&.is-cover {
|
||||
img {
|
||||
> .preview-image > img {
|
||||
// width: 29.7rem;
|
||||
height: 37.5rem;
|
||||
}
|
||||
}
|
||||
&.is-apparel {
|
||||
img {
|
||||
> .preview-image > img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
max-height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user