forked from natuka/web.puabadge.com
修改:优化cartoon的图片裁剪比例为3:4
This commit is contained in:
1 parent
505c585772
commit
482f6a57ce
1 file changed
+273
-64
+273
-64
@@ -177,7 +177,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="photo-upload-body">
|
<div class="photo-upload-body" @click="triggerPick">
|
||||||
<div v-if="!picture" class="photo-upload-box">
|
<div v-if="!picture" class="photo-upload-box">
|
||||||
<div class="photo-upload-area">
|
<div class="photo-upload-area">
|
||||||
<div class="photo-upload-plus">
|
<div class="photo-upload-plus">
|
||||||
@@ -195,10 +195,60 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<img class="photo-upload-img" v-if="picture" :src="picture" alt="" srcset="">
|
<img class="photo-upload-img" v-if="picture" :src="picture" alt="" srcset="">
|
||||||
<div class="photo-upload-box-1">
|
<input
|
||||||
<h5-cropper :option="option" @getbase64Data="getbase64Data" @imgorigoinf="imgorigoinf"></h5-cropper>
|
ref="fileInput"
|
||||||
|
type="file"
|
||||||
|
accept="image/*"
|
||||||
|
class="photo-upload-input"
|
||||||
|
@change="onFileChange"
|
||||||
|
@click.stop
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Teleport to="body">
|
||||||
|
<div v-if="cropping" class="ct-cropper">
|
||||||
|
<div class="ct-cropper__stage">
|
||||||
|
<VueCropper
|
||||||
|
ref="cropperRef"
|
||||||
|
:img="cropImg"
|
||||||
|
:auto-crop="true"
|
||||||
|
:auto-crop-width="9999"
|
||||||
|
:auto-crop-height="9999"
|
||||||
|
:fixed="true"
|
||||||
|
:fixed-number="config.upload.crop.ratio"
|
||||||
|
:fixed-box="false"
|
||||||
|
:center-box="false"
|
||||||
|
:can-move="true"
|
||||||
|
:can-move-box="true"
|
||||||
|
:can-scale="true"
|
||||||
|
:info="true"
|
||||||
|
:info-true="true"
|
||||||
|
:full="false"
|
||||||
|
:high="true"
|
||||||
|
:enlarge="config.upload.crop.enlarge"
|
||||||
|
:output-type="config.upload.crop.outputType"
|
||||||
|
:output-size="1"
|
||||||
|
:fill-color="config.upload.crop.fillColor"
|
||||||
|
mode="contain"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="ct-cropper__bar">
|
||||||
|
<button class="ct-cropper__btn" type="button" @click="onCropCancel">{{ toValueWithout("取消") }}</button>
|
||||||
|
<div class="ct-cropper__zoom">
|
||||||
|
<button class="ct-cropper__zoom-btn" type="button" aria-label="缩小" @click="onCropZoom(-1)">-</button>
|
||||||
|
<button class="ct-cropper__zoom-btn" type="button" aria-label="放大" @click="onCropZoom(1)">+</button>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="ct-cropper__btn ct-cropper__btn--ok"
|
||||||
|
type="button"
|
||||||
|
:disabled="cropConfirming"
|
||||||
|
@click="onCropConfirm"
|
||||||
|
>
|
||||||
|
{{ toValueWithout("确定") }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Teleport>
|
||||||
<div :style="`height: 120px;`"></div>
|
<div :style="`height: 120px;`"></div>
|
||||||
<div class="design-action-bar">
|
<div class="design-action-bar">
|
||||||
<div class="design-left">
|
<div class="design-left">
|
||||||
@@ -214,9 +264,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { nextTick, onMounted, onUnmounted, ref } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import H5Cropper from 'vue-cropper-h5'
|
import { VueCropper } from 'vue-cropper'
|
||||||
import "vue-cropper-h5/dist/style.css"
|
import 'vue-cropper/dist/index.css'
|
||||||
import { showLoadingToast, showToast, closeToast, showFailToast, showSuccessToast } from 'vant';
|
import { showLoadingToast, showToast, closeToast, showFailToast, showSuccessToast } from 'vant';
|
||||||
import type { UploaderFileListItem } from 'vant'
|
import type { UploaderFileListItem } from 'vant'
|
||||||
import * as badgeApi from '@/api/badge'
|
import * as badgeApi from '@/api/badge'
|
||||||
@@ -237,25 +288,11 @@ const config = cartoonConfig
|
|||||||
const referPicture = ref<UploaderFileListItem[]>([])
|
const referPicture = ref<UploaderFileListItem[]>([])
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
// 初始化裁剪器配置
|
const fileInput = ref<HTMLInputElement | null>(null)
|
||||||
const option = ref({
|
const cropperRef = ref<any>(null)
|
||||||
...config.upload.cropOptions
|
const cropping = ref(false)
|
||||||
})
|
const cropImg = ref('')
|
||||||
|
const cropConfirming = ref(false)
|
||||||
// 判断是否是APP
|
|
||||||
const openApp = () => {
|
|
||||||
const ua = navigator.userAgent;
|
|
||||||
const isIOS = /iPhone|iPad|iPod/i.test(ua);
|
|
||||||
const isAndroid = /Android/i.test(ua);
|
|
||||||
|
|
||||||
if (isIOS) {
|
|
||||||
option.value.ceilbutton = true
|
|
||||||
} else if (isAndroid) {
|
|
||||||
option.value.ceilbutton = false
|
|
||||||
} else {
|
|
||||||
option.value.ceilbutton = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const imgurl = ref('')
|
const imgurl = ref('')
|
||||||
|
|
||||||
@@ -437,29 +474,55 @@ const picture = ref(null)
|
|||||||
const imageWidth = ref(0);
|
const imageWidth = ref(0);
|
||||||
const imageHeight = ref(0);
|
const imageHeight = ref(0);
|
||||||
|
|
||||||
function getbase64Data(data: string) {
|
let lockedScrollY = 0
|
||||||
picture.value = data;
|
|
||||||
|
|
||||||
const img = new Image();
|
function preventTouchScroll(e: TouchEvent) {
|
||||||
img.src = data;
|
const host = document.querySelector('.ct-cropper')
|
||||||
img.onload = () => {
|
if (host && e.target instanceof Node && host.contains(e.target)) return
|
||||||
imageWidth.value = img.naturalWidth;
|
e.preventDefault()
|
||||||
imageHeight.value = img.naturalHeight;
|
|
||||||
if (imageWidth.value < config.upload.minWidth || imageHeight.value < config.upload.minHeight) {
|
|
||||||
showToast(toValueWithout(`请上传尺寸大于${config.upload.minWidth}*${config.upload.minHeight}像素的照片`))
|
|
||||||
imgurl.value = null
|
|
||||||
picture.value = null
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
const blob = base64ToBlob(data);
|
|
||||||
if (blob.size > config.upload.maxSize) {
|
function setBodyScrollLocked(locked: boolean) {
|
||||||
showToast(toValueWithout(`照片大小不能超过${config.upload.maxSize / 1024 / 1024}M`))
|
const html = document.documentElement
|
||||||
imgurl.value = null
|
const body = document.body
|
||||||
picture.value = null
|
if (locked) {
|
||||||
return false;
|
lockedScrollY = window.scrollY || window.pageYOffset || 0
|
||||||
|
html.style.overflow = 'hidden'
|
||||||
|
body.style.overflow = 'hidden'
|
||||||
|
body.style.position = 'fixed'
|
||||||
|
body.style.top = `-${lockedScrollY}px`
|
||||||
|
body.style.left = '0'
|
||||||
|
body.style.right = '0'
|
||||||
|
body.style.width = '100%'
|
||||||
|
document.addEventListener('touchmove', preventTouchScroll, { passive: false })
|
||||||
|
} else {
|
||||||
|
html.style.overflow = ''
|
||||||
|
body.style.overflow = ''
|
||||||
|
body.style.position = ''
|
||||||
|
body.style.top = ''
|
||||||
|
body.style.left = ''
|
||||||
|
body.style.right = ''
|
||||||
|
body.style.width = ''
|
||||||
|
document.removeEventListener('touchmove', preventTouchScroll)
|
||||||
|
window.scrollTo(0, lockedScrollY)
|
||||||
}
|
}
|
||||||
imgurl.value = blob;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openCropping(dataUrl: string) {
|
||||||
|
cropImg.value = dataUrl
|
||||||
|
cropping.value = true
|
||||||
|
setBodyScrollLocked(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeCropping() {
|
||||||
|
cropping.value = false
|
||||||
|
cropImg.value = ''
|
||||||
|
cropConfirming.value = false
|
||||||
|
setBodyScrollLocked(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
function triggerPick() {
|
||||||
|
fileInput.value?.click()
|
||||||
}
|
}
|
||||||
|
|
||||||
function base64ToBlob(base64: string) {
|
function base64ToBlob(base64: string) {
|
||||||
@@ -476,29 +539,98 @@ function base64ToBlob(base64: string) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function imgorigoinf(data: File) {
|
function applyCroppedData(data: string) {
|
||||||
const img = new Image();
|
picture.value = data
|
||||||
const objectUrl = URL.createObjectURL(data);
|
const img = new Image()
|
||||||
img.src = objectUrl;
|
img.src = data
|
||||||
img.onload = () => {
|
img.onload = () => {
|
||||||
imageWidth.value = img.naturalWidth;
|
imageWidth.value = img.naturalWidth
|
||||||
imageHeight.value = img.naturalHeight;
|
imageHeight.value = img.naturalHeight
|
||||||
if (imageWidth.value < config.upload.minWidth || imageHeight.value < config.upload.minHeight) {
|
if (imageWidth.value < config.upload.minWidth || imageHeight.value < config.upload.minHeight) {
|
||||||
showToast(toValueWithout(`请上传尺寸大于${config.upload.minWidth}*${config.upload.minHeight}像素的照片`))
|
showToast(toValueWithout(`请上传尺寸大于${config.upload.minWidth}*${config.upload.minHeight}像素的照片`))
|
||||||
setTimeout(() => {
|
|
||||||
const btn = document.querySelector('.btn')
|
|
||||||
if (btn) {
|
|
||||||
(btn as HTMLElement).click()
|
|
||||||
}
|
|
||||||
}, 1500);
|
|
||||||
imgurl.value = null
|
imgurl.value = null
|
||||||
picture.value = null
|
picture.value = null
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
const blob = base64ToBlob(data)
|
||||||
|
if (blob.size > config.upload.maxSize) {
|
||||||
|
showToast(toValueWithout(`照片大小不能超过${config.upload.maxSize / 1024 / 1024}M`))
|
||||||
|
imgurl.value = null
|
||||||
|
picture.value = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
imgurl.value = blob
|
||||||
|
}
|
||||||
|
img.onerror = () => {
|
||||||
|
showToast(toValueWithout('裁剪图片加载失败'))
|
||||||
|
imgurl.value = null
|
||||||
|
picture.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCropCancel() {
|
||||||
|
closeCropping()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 裁剪页缩放:正数放大、负数缩小;也支持双指捏合 */
|
||||||
|
function onCropZoom(step: number) {
|
||||||
|
const cropper = cropperRef.value as any
|
||||||
|
cropper?.changeScale?.(step)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCropConfirm() {
|
||||||
|
if (cropConfirming.value) return
|
||||||
|
const cropper = cropperRef.value as any
|
||||||
|
if (!cropper?.getCropData) {
|
||||||
|
showToast(toValueWithout('裁剪组件未就绪'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cropConfirming.value = true
|
||||||
|
cropper.getCropData((data: string) => {
|
||||||
|
closeCropping()
|
||||||
|
if (!data) {
|
||||||
|
showToast(toValueWithout('裁剪失败'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
applyCroppedData(data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function onFileChange(e: Event) {
|
||||||
|
const input = e.target as HTMLInputElement
|
||||||
|
const file = input.files?.[0]
|
||||||
|
input.value = ''
|
||||||
|
if (!file) return
|
||||||
|
if (file.size > config.upload.maxSize) {
|
||||||
|
showToast(toValueWithout(`照片大小不能超过${config.upload.maxSize / 1024 / 1024}M`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const objectUrl = URL.createObjectURL(file)
|
||||||
|
const img = new Image()
|
||||||
|
img.src = objectUrl
|
||||||
|
img.onload = () => {
|
||||||
|
imageWidth.value = img.naturalWidth
|
||||||
|
imageHeight.value = img.naturalHeight
|
||||||
URL.revokeObjectURL(objectUrl)
|
URL.revokeObjectURL(objectUrl)
|
||||||
|
if (imageWidth.value < config.upload.minWidth || imageHeight.value < config.upload.minHeight) {
|
||||||
|
showToast(toValueWithout(`请上传尺寸大于${config.upload.minWidth}*${config.upload.minHeight}像素的照片`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const reader = new FileReader()
|
||||||
|
reader.onload = (ev) => {
|
||||||
|
const dataUrl = String(ev.target?.result || '')
|
||||||
|
if (!dataUrl) {
|
||||||
|
showToast(toValueWithout('图片读取失败'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nextTick(() => openCropping(dataUrl))
|
||||||
|
}
|
||||||
|
reader.onerror = () => showToast(toValueWithout('图片读取失败'))
|
||||||
|
reader.readAsDataURL(file)
|
||||||
}
|
}
|
||||||
img.onerror = () => {
|
img.onerror = () => {
|
||||||
URL.revokeObjectURL(objectUrl)
|
URL.revokeObjectURL(objectUrl)
|
||||||
|
showToast(toValueWithout('图片加载失败'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -697,10 +829,13 @@ const createLog = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
openApp()
|
|
||||||
getOrderStat()
|
getOrderStat()
|
||||||
getSizeList()
|
getSizeList()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (cropping.value) closeCropping()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@@ -843,6 +978,7 @@ onMounted(() => {
|
|||||||
height: 80vw;
|
height: 80vw;
|
||||||
margin: 16px auto 0 auto;
|
margin: 16px auto 0 auto;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.photo-upload-box {
|
.photo-upload-box {
|
||||||
margin: 16px auto 0 auto;
|
margin: 16px auto 0 auto;
|
||||||
@@ -856,21 +992,94 @@ onMounted(() => {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
.photo-upload-box-1 {
|
.photo-upload-input {
|
||||||
width: 80vw;
|
|
||||||
height: 70vw;
|
|
||||||
border-radius: 12px;
|
|
||||||
background: rgba(0, 0, 0, 0);
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
inset: 0;
|
||||||
left: 0;
|
width: 100%;
|
||||||
right: 0;
|
height: 100%;
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
.photo-upload-img {
|
.photo-upload-img {
|
||||||
max-width: 80vw;
|
max-width: 80vw;
|
||||||
max-height: 80vw;
|
max-height: 80vw;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 全屏裁剪层:3:4 + 可缩放 */
|
||||||
|
.ct-cropper {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 2147483000;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background: #000;
|
||||||
|
overscroll-behavior: none;
|
||||||
|
}
|
||||||
|
.ct-cropper__stage {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.ct-cropper__stage :deep(.vue-cropper) {
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
background: #111 !important;
|
||||||
|
touch-action: none;
|
||||||
|
}
|
||||||
|
.ct-cropper__stage :deep(.cropper-view-box) {
|
||||||
|
outline: 2px solid #fff !important;
|
||||||
|
}
|
||||||
|
.ct-cropper__stage :deep(.crop-point) {
|
||||||
|
background-color: #50cf54 !important;
|
||||||
|
opacity: 0.95;
|
||||||
|
}
|
||||||
|
.ct-cropper__bar {
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: #111;
|
||||||
|
}
|
||||||
|
.ct-cropper__zoom {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
.ct-cropper__zoom-btn {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.35);
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 22px;
|
||||||
|
line-height: 1;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.ct-cropper__btn {
|
||||||
|
height: 40px;
|
||||||
|
line-height: 40px;
|
||||||
|
padding: 0 20px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 20px;
|
||||||
|
background: transparent;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
.ct-cropper__btn--ok {
|
||||||
|
background: #50cf54;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.ct-cropper__btn:disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
.photo-upload-header {
|
.photo-upload-header {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
Reference in new issue
Block a user