Commit bc180f71 authored by 朱国瑞's avatar 朱国瑞

裁剪组件支持裁剪图片

parent 3aa0bb5e
...@@ -21,22 +21,31 @@ const props = defineProps({ ...@@ -21,22 +21,31 @@ const props = defineProps({
}) })
watch( watch(
() => [props.cropArea, props.visible], () => [props.cropArea, props.visible, props.url],
(newVal, oldVal) => { (newVal, oldVal) => {
console.log('cropArea', newVal[0]) console.log('cropArea', newVal[0])
if (!props.visible) { if (!props.visible) {
showCropBox.value = false showCropBox.value = false
// handleInitCropArea() // handleInitCropArea()
} }
if (props.url) {
// 判断URL是视频还是图片
const type = utils.getFileType(props.url)
sourceMaterialType.value = type
title.value = type === 'video' ? '裁剪视频' : '裁剪图片'
}
} }
) )
const emit = defineEmits(['update:visible', 'crop']) const emit = defineEmits(['update:visible', 'crop'])
const isVisible = ref(props.visible) const isVisible = ref(props.visible)
const loginFormRef = ref<FormInstance>() const loginFormRef = ref<FormInstance>()
const title = ref('裁剪视频') const title = ref('裁剪视频')
const sourceMaterialType = ref<Wm.SourceMaterialType>('video')
const isMobile = ref(utils.checkIsMobile()) const isMobile = ref(utils.checkIsMobile())
const cropBoxRef = ref<any>() const cropBoxRef = ref<any>()
const backgroundVideoRef = ref<HTMLVideoElement>() const backgroundVideoRef = ref<HTMLVideoElement>()
const backgroundImageRef = ref<HTMLImageElement>()
const croppedCanvasRef = ref<HTMLCanvasElement>() const croppedCanvasRef = ref<HTMLCanvasElement>()
const rectData = ref({ const rectData = ref({
left: 0, left: 0,
...@@ -63,13 +72,31 @@ const onCancel = () => { ...@@ -63,13 +72,31 @@ const onCancel = () => {
console.log('cancel') console.log('cancel')
handleClose() handleClose()
} }
const getSourceMaterialInfo = () => {
if (sourceMaterialType.value === 'video') {
const video = backgroundVideoRef.value
return {
width: video?.videoWidth || 0,
height: video?.videoHeight || 0,
clientWidth: video?.clientWidth || 0,
clientHeight: video?.clientHeight || 0
}
} else {
const image = backgroundImageRef.value
return {
width: image?.naturalWidth || 0,
height: image?.naturalHeight || 0,
clientWidth: image?.clientWidth || 0,
clientHeight: image?.clientHeight || 0
}
}
}
const onConfirm = async () => { const onConfirm = async () => {
const video = backgroundVideoRef.value const sourceMaterialInfo = getSourceMaterialInfo()
// 计算裁剪框在视频中的位置 // 计算裁剪框在视频中的位置
const scaleX = video!.videoWidth / video!.clientWidth const scaleX = sourceMaterialInfo!.width / sourceMaterialInfo!.clientWidth
const scaleY = video!.videoHeight / video!.clientHeight const scaleY = sourceMaterialInfo!.height / sourceMaterialInfo!.clientHeight
const cropData = { const cropData = {
x: Math.round(rectData.value.left * scaleX), x: Math.round(rectData.value.left * scaleX),
y: Math.round(rectData.value.top * scaleY), y: Math.round(rectData.value.top * scaleY),
...@@ -86,17 +113,17 @@ const onConfirm = async () => { ...@@ -86,17 +113,17 @@ const onConfirm = async () => {
if (cropData.y < 0) { if (cropData.y < 0) {
cropData.y = 0 cropData.y = 0
} }
if (cropData.width > video!.videoWidth) { if (cropData.width > sourceMaterialInfo!.width) {
cropData.width = video!.videoWidth cropData.width = sourceMaterialInfo!.width
} }
if (cropData.x + cropData.width > video!.videoWidth) { if (cropData.x + cropData.width > sourceMaterialInfo!.width) {
cropData.width = video!.videoWidth - cropData.x cropData.width = sourceMaterialInfo!.width - cropData.x
} }
if (cropData.height > video!.videoHeight) { if (cropData.height > sourceMaterialInfo!.height) {
cropData.height = video!.videoHeight cropData.height = sourceMaterialInfo!.height
} }
if (cropData.y + cropData.height > video!.videoHeight) { if (cropData.y + cropData.height > sourceMaterialInfo!.height) {
cropData.height = video!.videoHeight - cropData.y cropData.height = sourceMaterialInfo!.height - cropData.y
} }
console.log(rectData.value, scaleX, scaleY) console.log(rectData.value, scaleX, scaleY)
...@@ -223,6 +250,46 @@ const handleInitCropArea = () => { ...@@ -223,6 +250,46 @@ const handleInitCropArea = () => {
} }
} }
} }
const onImageLoaded = (val: any) => {
console.log('onImageLoaded', val)
const img = val.target
const imageWidth = img.naturalWidth
const imageHeight = img.naturalHeight
cropPreviewBox.value = {
width: img.width,
height: img.height
}
showCropBox.value = true
const cropArea: any = toRaw(props.cropArea)
if (cropArea.length > 0) {
const scaleX = cropPreviewBox.value.width / imageWidth
const scaleY = cropPreviewBox.value.height / imageHeight
console.log('cropArea', cropArea, scaleX, scaleY)
const left = Math.round(cropArea[0][0] * scaleX)
const top = Math.round(cropArea[0][1] * scaleY)
const width = Math.round((cropArea[1][0] - cropArea[0][0]) * scaleX)
const height = Math.round((cropArea[1][1] - cropArea[0][1]) * scaleY)
console.log('init cropArea', left, top, width, height)
rectData.value = {
left,
top,
width,
height
}
} else {
// 全部显示
rectData.value = {
left: 0,
top: 0,
width: imageWidth,
height: imageHeight
}
}
}
</script> </script>
<template> <template>
<el-dialog <el-dialog
...@@ -249,8 +316,15 @@ const handleInitCropArea = () => { ...@@ -249,8 +316,15 @@ const handleInitCropArea = () => {
playsinline playsinline
class="background-video" class="background-video"
@loadeddata="onVideoLoaded" @loadeddata="onVideoLoaded"
v-if="isVisible" v-if="isVisible && sourceMaterialType === 'video'"
></video> ></video>
<img
ref="backgroundImageRef"
:src="url"
class="background-image"
@load="onImageLoaded"
v-if="isVisible && sourceMaterialType === 'image'"
/>
<VueDragResize <VueDragResize
ref="cropBoxRef" ref="cropBoxRef"
:w="rectData.width" :w="rectData.width"
...@@ -300,6 +374,13 @@ const handleInitCropArea = () => { ...@@ -300,6 +374,13 @@ const handleInitCropArea = () => {
object-fit: cover; object-fit: cover;
} }
.background-image {
width: 100%;
height: 100%;
object-fit: cover;
-webkit-user-drag: none; /* 禁止图片拖动 */
}
.crop-box { .crop-box {
border-color: var(--el-color-primary-dark-2); border-color: var(--el-color-primary-dark-2);
position: absolute; position: absolute;
......
...@@ -143,4 +143,19 @@ export default class utils { ...@@ -143,4 +143,19 @@ export default class utils {
return false return false
} }
} }
static getFileType = (url: string) => {
const videoExtensions = ['mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv'];
const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'];
const extension = (url.split('.').pop() || '').toLowerCase();
if (videoExtensions.includes(extension)) {
return 'video';
} else if (imageExtensions.includes(extension)) {
return 'image';
} else {
return 'unknown';
}
}
} }
...@@ -424,7 +424,7 @@ const onDownloadVideo = async () => { ...@@ -424,7 +424,7 @@ const onDownloadVideo = async () => {
size="small" size="small"
@click.stop="showCropVideoDialog('show_image_or_video')" @click.stop="showCropVideoDialog('show_image_or_video')"
> >
裁剪视频画面 裁剪图片或视频画面
</el-button> </el-button>
<template #tip> <template #tip>
<div class="el-upload__tip" style="color: #0000ff; background-color: #e6f7ff"> <div class="el-upload__tip" style="color: #0000ff; background-color: #e6f7ff">
......
declare namespace Wm { declare namespace Wm {
interface ImgSize { interface ImgSize {
"width": string, width: string
"height": string, height: string
} }
interface PromptHistory { interface PromptHistory {
"role": string, role: string
"content": string, content: string
} }
interface PicText { interface PicText {
"text": string, text: string
"color": string, color: string
"bg_color": string, bg_color: string
"font_size": number, font_size: number
"position": number, position: number
} }
interface ScriptsItem { interface ScriptsItem {
"编号": string, 编号: string
"场景描述": string, 场景描述: string
"场景关键词": string, 场景关键词: string
"场景关键词英文": string, 场景关键词英文: string
"角色": string, 角色: string
"角色关键词": string, 角色关键词: string
"角色关键词英文": string, 角色关键词英文: string
"画面描述词": string, 画面描述词: string
"本镜配图": string, 本镜配图: string
"local_image_path": string, local_image_path: string
"info": string, info: string
"roles": String[], roles: String[]
"info2": string, info2: string
} }
interface GenVideo { interface GenVideo {
"task_id": string, task_id: string
"task_info": GenVideoItem[], task_info: GenVideoItem[]
} }
interface GenVideoItem { interface GenVideoItem {
"idx": string, idx: string
"text": string, text: string
"img_path": string, img_path: string
} }
interface RolesItem { interface RolesItem {
"角色": string, 角色: string
"角色关键词": string, 角色关键词: string
"角色关键词英文": string, 角色关键词英文: string
"属性": string, 属性: string
} }
interface UploadResult { interface UploadResult {
"code": int, code: int
"data": [ data: [
{ {
"id": int, id: int
"key": string, key: string
"path": string, path: string
"url": string url: string
} }
], ]
"message": string message: string
} }
interface Coordinate { interface Coordinate {
"x": int, x: int
"y": int, y: int
} }
interface LoginParams { interface LoginParams {
...@@ -83,4 +83,6 @@ declare namespace Wm { ...@@ -83,4 +83,6 @@ declare namespace Wm {
password: string password: string
username: string username: string
} }
type SourceMaterialType = 'video' | 'image' | 'unknown'
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment