@@ -150,3 +150,29 @@ export function personPatchApi(id) { | |||
method: 'get' | |||
}) | |||
} | |||
/** | |||
* @description:保存或修改用户作品 | |||
* @param {object} data | |||
* @return data | |||
*/ | |||
export function saveOrUpdateApi(data) { | |||
return request({ | |||
url: `/api/userVideo/saveOrUpdate`, | |||
method: 'post', | |||
data: data | |||
}) | |||
} | |||
/** | |||
* @description 生成视频 | |||
* @param {object} id | |||
* @returns id | |||
*/ | |||
export function createVideoApi(id) { | |||
return request({ | |||
url: `/api/userVideo/createVideo`, | |||
method: 'post', | |||
data: { id } | |||
}) | |||
} |
@@ -6,9 +6,16 @@ import request from "@/utils/request.js"; | |||
* @params pageNum, pageSize | |||
* @returns data | |||
*/ | |||
// export function userPhotoVideoList(pageNum, pageSize, title) { | |||
// return request({ | |||
// url: `/api/userPhotoVideo/list?pageNum=${pageNum}&pageSize=${pageSize}&title=${title}`, | |||
// method: 'get' | |||
// }) | |||
// } | |||
export function userPhotoVideoList(pageNum, pageSize, title) { | |||
let videoStatuss = [1, 2, 3, 4, 5, 6] | |||
return request({ | |||
url: `/api/userPhotoVideo/list?pageNum=${pageNum}&pageSize=${pageSize}&title=${title}`, | |||
url: `/api/userVideo/list?pageNum=${pageNum}&pageSize=${pageSize}&title=${title}&videoType=-1&videoStatuss=${videoStatuss}`, | |||
method: 'get' | |||
}) | |||
} | |||
@@ -23,4 +30,17 @@ export function delUserPhotoVideoByID(videoID) { | |||
url: `/api/userPhotoVideo/del?id=${videoID}`, | |||
method: 'get' | |||
}) | |||
} | |||
/** | |||
* @description 获取作品列表 | |||
* @param {number} pageParams (pageNum, pageSize) | |||
* @returns data | |||
*/ | |||
export function getVideoList(pageNum, pageSize, videoType, videoStatuss) { | |||
return request({ | |||
url: `/api/userVideo/list?pageNum=${pageNum}&pageSize=${pageSize}&videoType=${videoType}&videoStatuss=${videoStatuss}`, | |||
method: 'get' | |||
}) | |||
} |
@@ -15,15 +15,74 @@ | |||
:style="virtualStyle" | |||
> | |||
<img id="virtualImg" :src="materialList.virtual.material" alt="" /> | |||
<div class="stretchBox box1"></div> | |||
<div ref="box1" class="stretchBox box1"></div> | |||
<div class="stretchBox box2"></div> | |||
<div class="stretchBox box3"></div> | |||
<div class="stretchBox box4"></div> | |||
<div | |||
v-show="showBorderActive == materialList.virtual.id" | |||
ref="box4" | |||
@mousedown.stop="changeScale" | |||
class="stretchBox box4" | |||
> | |||
<img | |||
style="width: 100px; height: 100px" | |||
src="../assets/img/Scale.png" | |||
alt="" | |||
/> | |||
</div> | |||
</div> | |||
<!-- 素材图的图片 --> | |||
<div | |||
v-for="(item, index) in materialList.scList" | |||
:key="item.id" | |||
class="scListItem" | |||
:class="showBorderActive == item.id ? 'border' : ''" | |||
@mousedown="handleDragSc(item.id, index, $event)" | |||
:style="{ | |||
top: item.y + 'px', | |||
left: item.x + 'px', | |||
transform: 'scale(' + item.w / props.proportion + ') ', | |||
transformOrigin: 'top left', | |||
zIndex: item.z, | |||
}" | |||
> | |||
<img id="scListItemImg" :src="item.material" alt="" /> | |||
<div ref="box1" class="stretchBox box1"></div> | |||
<div | |||
v-show="showBorderActive == item.id" | |||
class="stretchBox box2" | |||
@click="delScListItem(index)" | |||
> | |||
<img | |||
style="width: 100px; height: 100px" | |||
src="../assets/img/delete2.png" | |||
alt="" | |||
/> | |||
</div> | |||
<div class="stretchBox box3"></div> | |||
<div | |||
v-show="showBorderActive == item.id" | |||
ref="box4" | |||
@mousedown.stop="changeScaleSc(index, $event)" | |||
class="stretchBox box4" | |||
> | |||
<img | |||
style="width: 100px; height: 100px" | |||
src="../assets/img/Scale.png" | |||
alt="" | |||
/> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</template> | |||
<script setup> | |||
//#region 导入 | |||
import { useI18n } from "vue-i18n"; | |||
//#endregion -------------------------- | |||
const { locale } = useI18n(); | |||
const lanChange = ref(locale); | |||
//#region 父组件传参 | |||
const props = defineProps({ | |||
// isLoading: { | |||
@@ -38,6 +97,9 @@ const props = defineProps({ | |||
proportion: { | |||
required: true, | |||
}, | |||
showImgInfo: { | |||
required: true, | |||
}, | |||
}); | |||
//#endregion -------------------------- | |||
@@ -63,6 +125,7 @@ const virtualStyle = computed(() => { | |||
transform: | |||
"scale(" + props.materialList.virtual.w / props.proportion + ") ", | |||
transformOrigin: "top left", | |||
zIndex: props.materialList.virtual.z, | |||
}; | |||
}); | |||
// 拖动盒子 | |||
@@ -94,6 +157,248 @@ const handleDrag = (id, event) => { | |||
document.addEventListener("mousemove", onMouseMove); | |||
document.addEventListener("mouseup", onMouseUp); | |||
}; | |||
// 缩放盒子 | |||
const touchStart = ref({ | |||
pageX: null, | |||
pageY: null, | |||
}); | |||
const touchOver = ref({ | |||
pageX: null, | |||
pageY: null, | |||
}); | |||
const scaleNum = ref(0.004); | |||
// 初始版 | |||
// function changeScale(event) { | |||
// event.preventDefault(); | |||
// const x = event.clientX; | |||
// const y = event.clientY; | |||
// // touchStart = { | |||
// // pageX: x, | |||
// // pageY: y, | |||
// // }; | |||
// touchStart.pageX = x; | |||
// touchStart.pageY = y; | |||
// // 鼠标移动 | |||
// const onMouseMove = (event) => { | |||
// const MoveX = event.clientX; | |||
// const MoveY = event.clientY; | |||
// const flag = Math.sign(x - touchStart.pageX) == -1 ? true : false; | |||
// if (flag) { | |||
// // 限制最大缩放系数 | |||
// if (props.materialList.virtual.w > 1.3) return; | |||
// props.materialList.virtual.w += scaleNum.value; | |||
// // 更新坐标位置 | |||
// // touchStart = { | |||
// // pageX: MoveX, | |||
// // pageY: MoveY, | |||
// // }; | |||
// touchStart.pageX = MoveX; | |||
// touchStart.pageY = MoveY; | |||
// } else { | |||
// // 限制最小缩放系数 | |||
// if (props.materialList.virtual.w < 0.4) return; | |||
// props.materialList.virtual.w -= scaleNum.value; | |||
// // 更新坐标位置 | |||
// // touchStart = { | |||
// // pageX: MoveX, | |||
// // pageY: MoveY, | |||
// // }; | |||
// touchStart.pageX = MoveX; | |||
// touchStart.pageY = MoveY; | |||
// } | |||
// }; | |||
// // 鼠标抬起 | |||
// const onMouseUp = () => { | |||
// document.removeEventListener("mousemove", onMouseMove); | |||
// document.removeEventListener("mouseup", onMouseUp); | |||
// }; | |||
// document.addEventListener("mousemove", onMouseMove); | |||
// document.addEventListener("mouseup", onMouseUp); | |||
// } | |||
// 改动版 | |||
function changeScale(event) { | |||
event.preventDefault(); | |||
const x = event.clientX; | |||
const y = event.clientY; | |||
touchStart.pageX = x; | |||
touchStart.pageY = y; | |||
// 鼠标移动 | |||
const onMouseMove = (event) => { | |||
const MoveX = event.clientX; | |||
const MoveY = event.clientY; | |||
let flag = Math.sign(x - touchStart.pageX) == -1 ? true : false; | |||
if (flag) { | |||
// 限制最大缩放系数 | |||
if (props.materialList.virtual.w > 1.3) return; | |||
props.materialList.virtual.w += scaleNum.value; | |||
// 更新坐标位置 | |||
touchStart.pageX = MoveX; | |||
touchStart.pageY = MoveY; | |||
} else { | |||
// 限制最小缩放系数 | |||
if (props.materialList.virtual.w < 0.4) return; | |||
props.materialList.virtual.w -= scaleNum.value; | |||
// 更新坐标位置 | |||
touchStart.pageX = MoveX; | |||
touchStart.pageY = MoveY; | |||
} | |||
}; | |||
// 鼠标抬起 | |||
const onMouseUp = () => { | |||
document.removeEventListener("mousemove", onMouseMove); | |||
document.removeEventListener("mouseup", onMouseUp); | |||
}; | |||
document.addEventListener("mousemove", onMouseMove); | |||
document.addEventListener("mouseup", onMouseUp); | |||
} | |||
const box1 = ref(null); | |||
const box2 = ref(null); | |||
const box3 = ref(null); | |||
const box4 = ref(null); | |||
// 距离版 | |||
// function changeScale(event) { | |||
// const box1Element = box1.value; | |||
// const box4Element = box4.value; | |||
// if (!box1Element || !box4Element) { | |||
// return; | |||
// } | |||
// touchStart.pageX = event.clientX; | |||
// touchStart.pageY = event.clientY; | |||
// const box1Rect = box1Element.getBoundingClientRect(); | |||
// const box4Rect = box4Element.getBoundingClientRect(); | |||
// const boxDistance = Math.sqrt( | |||
// (box1Rect.left - box4Rect.left) ** 2 + (box1Rect.top - box4Rect.top) ** 2 | |||
// ).toFixed(0); | |||
// const onMouseMove = (event) => { | |||
// const x = event.clientX; | |||
// const y = event.clientY; | |||
// const deltaX = touchStart.pageX - x; | |||
// const deltaY = touchStart.pageY - y; | |||
// const boxDistance2 = Math.sqrt(deltaX ** 2 + deltaY ** 2).toFixed(0); | |||
// if (deltaX < 0) { | |||
// props.materialList.virtual.w = Math.min( | |||
// Math.max((boxDistance + boxDistance2) / boxDistance, 0.4), | |||
// 1.3 | |||
// ); | |||
// } else { | |||
// props.materialList.virtual.w = Math.min( | |||
// Math.max((boxDistance - boxDistance2) / boxDistance, 0.4), | |||
// 1.3 | |||
// ); | |||
// } | |||
// // touchOver.pageX = event.clientX; | |||
// // touchOver.pageY = event.clientY; | |||
// }; | |||
// // 鼠标抬起 | |||
// const onMouseUp = () => { | |||
// // touchStart.pageX = touchOver.pageX; | |||
// // touchStart.pageY = touchOver.pageY; | |||
// document.removeEventListener("mousemove", onMouseMove); | |||
// document.removeEventListener("mouseup", onMouseUp); | |||
// }; | |||
// document.addEventListener("mousemove", onMouseMove); | |||
// document.addEventListener("mouseup", onMouseUp); | |||
// } | |||
//#endregion -------------------------- | |||
//#region 素材图逻辑 | |||
const handleDragSc = (id, index, event) => { | |||
showBorderActive.value = id; | |||
event.preventDefault(); | |||
const imageElement = fatherBox.value; | |||
const startX = event.clientX; //保存初始值,避免闪烁 | |||
const startY = event.clientY; | |||
const startLeft = props.materialList.scList[index].x; | |||
const startTop = props.materialList.scList[index].y; | |||
// 鼠标移动 | |||
const onMouseMove = (event) => { | |||
const deltaX = event.clientX - startX; | |||
const deltaY = event.clientY - startY; | |||
props.materialList.scList[index].x = startLeft + deltaX; | |||
props.materialList.scList[index].y = startTop + deltaY; | |||
}; | |||
// 鼠标抬起 | |||
const onMouseUp = () => { | |||
document.removeEventListener("mousemove", onMouseMove); | |||
document.removeEventListener("mouseup", onMouseUp); | |||
}; | |||
document.addEventListener("mousemove", onMouseMove); | |||
document.addEventListener("mouseup", onMouseUp); | |||
}; | |||
function changeScaleSc(index, event) { | |||
event.preventDefault(); | |||
const x = event.clientX; | |||
const y = event.clientY; | |||
touchStart.pageX = x; | |||
touchStart.pageY = y; | |||
// 鼠标移动 | |||
const onMouseMove = (event) => { | |||
let MoveX = event.clientX; | |||
let MoveY = event.clientY; | |||
let flag = Math.sign(x - touchStart.pageX) == -1 ? true : false; | |||
if (flag) { | |||
// 限制最大缩放系数 | |||
if (props.materialList.scList[index].w > 1.3) return; | |||
props.materialList.scList[index].w += scaleNum.value; | |||
// 更新坐标位置 | |||
touchStart.pageX = MoveX; | |||
touchStart.pageY = MoveY; | |||
} else { | |||
// 限制最小缩放系数 | |||
if (props.materialList.scList[index].w < 0.4) return; | |||
props.materialList.scList[index].w -= scaleNum.value; | |||
// 更新坐标位置 | |||
touchStart.pageX = MoveX; | |||
touchStart.pageY = MoveY; | |||
} | |||
}; | |||
// 鼠标抬起 | |||
const onMouseUp = () => { | |||
document.removeEventListener("mousemove", onMouseMove); | |||
document.removeEventListener("mouseup", onMouseUp); | |||
}; | |||
document.addEventListener("mousemove", onMouseMove); | |||
document.addEventListener("mouseup", onMouseUp); | |||
} | |||
function delScListItem(index) { | |||
const msg = | |||
lanChange.value == "zh-cn" | |||
? "确定删除素材吗吗?" | |||
: "Are you sure you want to delete the footage?"; | |||
const notice = lanChange.value == "zh-cn" ? "提示" : "Notice"; | |||
const confirm = lanChange.value == "zh-cn" ? "确定" : "Confirm"; | |||
const cancel = lanChange.value == "zh-cn" ? "取消" : "Cancel"; | |||
ElMessageBox.confirm(msg, notice, { | |||
confirmButtonText: confirm, | |||
cancelButtonText: cancel, | |||
}).then(() => { | |||
props.materialList.scList.splice(index, 1); | |||
}); | |||
} | |||
//#endregion -------------------------- | |||
//#region 控制台回显 | |||
watch( | |||
() => showBorderActive.value, | |||
(newValue, oldValue) => { | |||
props.showImgInfo.id = showBorderActive.value; | |||
}, | |||
{ deep: true } | |||
); | |||
//#endregion -------------------------- | |||
//#region | |||
@@ -122,6 +427,11 @@ const handleDrag = (id, event) => { | |||
user-select: none; | |||
border: rgba(0, 0, 0, 0) 5px solid; | |||
} | |||
.scListItem { | |||
position: absolute; | |||
user-select: none; | |||
border: rgba(0, 0, 0, 0) 5px solid; | |||
} | |||
.border { | |||
border: 5px solid #00ff00; | |||
.stretchBox { | |||
@@ -135,25 +445,48 @@ const handleDrag = (id, event) => { | |||
top: -10px; | |||
left: -10px; | |||
cursor: nwse-resize; | |||
display: none; | |||
} | |||
.box2 { | |||
top: -10px; | |||
top: -50px; | |||
left: 100%; | |||
transform: translateX(-10px); | |||
cursor: nesw-resize; | |||
transform: translateX(-50px); | |||
// cursor: nesw-resize; | |||
cursor: pointer; | |||
background-color: rgba(0, 0, 0, 0); | |||
width: 100px; | |||
height: 100px; | |||
img { | |||
width: 100px; | |||
height: 100px; | |||
} | |||
} | |||
.box3 { | |||
top: 100%; | |||
left: -10px; | |||
transform: translateY(-10px); | |||
cursor: nesw-resize; | |||
display: none; | |||
} | |||
.box4 { | |||
top: 100%; | |||
left: 100%; | |||
transform: translate(-10px, -10px); | |||
transform: translate(-50px, -50px); | |||
cursor: nwse-resize; | |||
// background-color: rgba(0, 0, 0, 0); | |||
background-color: #fff; | |||
width: 100px; | |||
height: 100px; | |||
border-radius: 50px; | |||
img { | |||
position: absolute; | |||
top: 50%; | |||
left: 50%; | |||
transform: translate(-50%, -50%) rotate(90deg); | |||
max-width: 65px; | |||
max-height: 65px; | |||
} | |||
} | |||
} | |||
</style> |
@@ -15,15 +15,74 @@ | |||
:style="virtualStyle" | |||
> | |||
<img id="virtualImg" :src="materialList.virtual.material" alt="" /> | |||
<div class="stretchBox box1"></div> | |||
<div ref="box1" class="stretchBox box1"></div> | |||
<div class="stretchBox box2"></div> | |||
<div class="stretchBox box3"></div> | |||
<div @mousedown.stop="changeScale" class="stretchBox box4"></div> | |||
<div | |||
v-show="showBorderActive == materialList.virtual.id" | |||
ref="box4" | |||
@mousedown.stop="changeScale" | |||
class="stretchBox box4" | |||
> | |||
<img | |||
style="width: 100px; height: 100px" | |||
src="../assets/img/Scale.png" | |||
alt="" | |||
/> | |||
</div> | |||
</div> | |||
<!-- 素材图的图片 --> | |||
<div | |||
v-for="(item, index) in materialList.scList" | |||
:key="item.id" | |||
class="scListItem" | |||
:class="showBorderActive == item.id ? 'border' : ''" | |||
@mousedown="handleDragSc(item.id, index, $event)" | |||
:style="{ | |||
top: item.y / props.proportion + 'px', | |||
left: item.x / props.proportion + 'px', | |||
transform: 'scale(' + item.w / props.proportion + ') ', | |||
transformOrigin: 'top left', | |||
zIndex: item.z, | |||
}" | |||
> | |||
<img id="scListItemImg" :src="item.material" alt="" /> | |||
<div ref="box1" class="stretchBox box1"></div> | |||
<div | |||
v-show="showBorderActive == item.id" | |||
class="stretchBox box2" | |||
@click="delScListItem(index)" | |||
> | |||
<img | |||
style="width: 100px; height: 100px" | |||
src="../assets/img/delete2.png" | |||
alt="" | |||
/> | |||
</div> | |||
<div class="stretchBox box3"></div> | |||
<div | |||
v-show="showBorderActive == item.id" | |||
ref="box4" | |||
@mousedown.stop="changeScaleSc(index, $event)" | |||
class="stretchBox box4" | |||
> | |||
<img | |||
style="width: 100px; height: 100px" | |||
src="../assets/img/Scale.png" | |||
alt="" | |||
/> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</template> | |||
<script setup> | |||
//#region 导入 | |||
import { useI18n } from "vue-i18n"; | |||
//#endregion -------------------------- | |||
const { locale } = useI18n(); | |||
const lanChange = ref(locale); | |||
//#region 父组件传参 | |||
const props = defineProps({ | |||
// isLoading: { | |||
@@ -38,6 +97,9 @@ const props = defineProps({ | |||
proportion: { | |||
required: true, | |||
}, | |||
showImgInfo: { | |||
required: true, | |||
}, | |||
}); | |||
//#endregion -------------------------- | |||
@@ -58,11 +120,12 @@ const bgStyle = computed(() => { | |||
// 计算属性来计算盒子位置和大小 | |||
const virtualStyle = computed(() => { | |||
return { | |||
top: props.materialList.virtual.y + "px", | |||
left: props.materialList.virtual.x + "px", | |||
top: props.materialList.virtual.y / props.proportion + "px", | |||
left: props.materialList.virtual.x / props.proportion + "px", | |||
transform: | |||
"scale(" + props.materialList.virtual.w / props.proportion + ") ", | |||
transformOrigin: "top left", | |||
zIndex: props.materialList.virtual.z, | |||
}; | |||
}); | |||
// 拖动盒子 | |||
@@ -81,8 +144,8 @@ const handleDrag = (id, event) => { | |||
const onMouseMove = (event) => { | |||
const deltaX = event.clientX - startX; | |||
const deltaY = event.clientY - startY; | |||
props.materialList.virtual.x = startLeft + deltaX; | |||
props.materialList.virtual.y = startTop + deltaY; | |||
props.materialList.virtual.x = startLeft + deltaX * props.proportion; | |||
props.materialList.virtual.y = startTop + deltaY * props.proportion; | |||
}; | |||
// 鼠标抬起 | |||
@@ -95,42 +158,92 @@ const handleDrag = (id, event) => { | |||
document.addEventListener("mouseup", onMouseUp); | |||
}; | |||
// 缩放盒子 | |||
let touchStart = reactive({ | |||
const touchStart = ref({ | |||
pageX: null, | |||
pageY: null, | |||
}); | |||
const touchOver = ref({ | |||
pageX: null, | |||
pageY: null, | |||
}); | |||
const scaleNum = ref(0.003); | |||
const scaleNum = ref(0.004); | |||
// 初始版 | |||
// function changeScale(event) { | |||
// event.preventDefault(); | |||
// const x = event.clientX; | |||
// const y = event.clientY; | |||
// // touchStart = { | |||
// // pageX: x, | |||
// // pageY: y, | |||
// // }; | |||
// touchStart.pageX = x; | |||
// touchStart.pageY = y; | |||
// // 鼠标移动 | |||
// const onMouseMove = (event) => { | |||
// const MoveX = event.clientX; | |||
// const MoveY = event.clientY; | |||
// const flag = Math.sign(x - touchStart.pageX) == -1 ? true : false; | |||
// if (flag) { | |||
// // 限制最大缩放系数 | |||
// if (props.materialList.virtual.w > 1.3) return; | |||
// props.materialList.virtual.w += scaleNum.value; | |||
// // 更新坐标位置 | |||
// // touchStart = { | |||
// // pageX: MoveX, | |||
// // pageY: MoveY, | |||
// // }; | |||
// touchStart.pageX = MoveX; | |||
// touchStart.pageY = MoveY; | |||
// } else { | |||
// // 限制最小缩放系数 | |||
// if (props.materialList.virtual.w < 0.4) return; | |||
// props.materialList.virtual.w -= scaleNum.value; | |||
// // 更新坐标位置 | |||
// // touchStart = { | |||
// // pageX: MoveX, | |||
// // pageY: MoveY, | |||
// // }; | |||
// touchStart.pageX = MoveX; | |||
// touchStart.pageY = MoveY; | |||
// } | |||
// }; | |||
// // 鼠标抬起 | |||
// const onMouseUp = () => { | |||
// document.removeEventListener("mousemove", onMouseMove); | |||
// document.removeEventListener("mouseup", onMouseUp); | |||
// }; | |||
// document.addEventListener("mousemove", onMouseMove); | |||
// document.addEventListener("mouseup", onMouseUp); | |||
// } | |||
// 改动版 | |||
function changeScale(event) { | |||
event.preventDefault(); | |||
const x = event.clientX; | |||
const y = event.clientY; | |||
touchStart = { | |||
pageX: x, | |||
pageY: y, | |||
}; | |||
touchStart.pageX = x; | |||
touchStart.pageY = y; | |||
// 鼠标移动 | |||
const onMouseMove = (event) => { | |||
const MoveX = event.clientX; | |||
const MoveY = event.clientY; | |||
const flag = Math.sign(x - touchStart.pageX) == -1 ? true : false; | |||
let flag = Math.sign(x - touchStart.pageX) == -1 ? true : false; | |||
if (flag) { | |||
// 限制最大缩放系数 | |||
if (props.materialList.virtual.w > 1.3) return; | |||
props.materialList.virtual.w += scaleNum.value; | |||
// 更新坐标位置 | |||
touchStart = { | |||
pageX: MoveX, | |||
pageY: MoveY, | |||
}; | |||
touchStart.pageX = MoveX; | |||
touchStart.pageY = MoveY; | |||
} else { | |||
// 限制最小缩放系数 | |||
if (props.materialList.virtual.w < 0.4) return; | |||
props.materialList.virtual.w -= scaleNum.value; | |||
// 更新坐标位置 | |||
touchStart = { | |||
pageX: MoveX, | |||
pageY: MoveY, | |||
}; | |||
touchStart.pageX = MoveX; | |||
touchStart.pageY = MoveY; | |||
} | |||
}; | |||
@@ -143,6 +256,149 @@ function changeScale(event) { | |||
document.addEventListener("mousemove", onMouseMove); | |||
document.addEventListener("mouseup", onMouseUp); | |||
} | |||
const box1 = ref(null); | |||
const box2 = ref(null); | |||
const box3 = ref(null); | |||
const box4 = ref(null); | |||
// 距离版 | |||
// function changeScale(event) { | |||
// const box1Element = box1.value; | |||
// const box4Element = box4.value; | |||
// if (!box1Element || !box4Element) { | |||
// return; | |||
// } | |||
// touchStart.pageX = event.clientX; | |||
// touchStart.pageY = event.clientY; | |||
// const box1Rect = box1Element.getBoundingClientRect(); | |||
// const box4Rect = box4Element.getBoundingClientRect(); | |||
// const boxDistance = Math.sqrt( | |||
// (box1Rect.left - box4Rect.left) ** 2 + (box1Rect.top - box4Rect.top) ** 2 | |||
// ).toFixed(0); | |||
// const onMouseMove = (event) => { | |||
// const x = event.clientX; | |||
// const y = event.clientY; | |||
// const deltaX = touchStart.pageX - x; | |||
// const deltaY = touchStart.pageY - y; | |||
// const boxDistance2 = Math.sqrt(deltaX ** 2 + deltaY ** 2).toFixed(0); | |||
// if (deltaX < 0) { | |||
// props.materialList.virtual.w = Math.min( | |||
// Math.max((boxDistance + boxDistance2) / boxDistance, 0.4), | |||
// 1.3 | |||
// ); | |||
// } else { | |||
// props.materialList.virtual.w = Math.min( | |||
// Math.max((boxDistance - boxDistance2) / boxDistance, 0.4), | |||
// 1.3 | |||
// ); | |||
// } | |||
// // touchOver.pageX = event.clientX; | |||
// // touchOver.pageY = event.clientY; | |||
// }; | |||
// // 鼠标抬起 | |||
// const onMouseUp = () => { | |||
// // touchStart.pageX = touchOver.pageX; | |||
// // touchStart.pageY = touchOver.pageY; | |||
// document.removeEventListener("mousemove", onMouseMove); | |||
// document.removeEventListener("mouseup", onMouseUp); | |||
// }; | |||
// document.addEventListener("mousemove", onMouseMove); | |||
// document.addEventListener("mouseup", onMouseUp); | |||
// } | |||
//#endregion -------------------------- | |||
//#region 素材图逻辑 | |||
const handleDragSc = (id, index, event) => { | |||
showBorderActive.value = id; | |||
event.preventDefault(); | |||
const imageElement = fatherBox.value; | |||
const startX = event.clientX; //保存初始值,避免闪烁 | |||
const startY = event.clientY; | |||
const startLeft = props.materialList.scList[index].x; | |||
const startTop = props.materialList.scList[index].y; | |||
// 鼠标移动 | |||
const onMouseMove = (event) => { | |||
const deltaX = event.clientX - startX; | |||
const deltaY = event.clientY - startY; | |||
props.materialList.scList[index].x = startLeft + deltaX * props.proportion; | |||
props.materialList.scList[index].y = startTop + deltaY * props.proportion; | |||
}; | |||
// 鼠标抬起 | |||
const onMouseUp = () => { | |||
document.removeEventListener("mousemove", onMouseMove); | |||
document.removeEventListener("mouseup", onMouseUp); | |||
}; | |||
document.addEventListener("mousemove", onMouseMove); | |||
document.addEventListener("mouseup", onMouseUp); | |||
}; | |||
function changeScaleSc(index, event) { | |||
event.preventDefault(); | |||
const x = event.clientX; | |||
const y = event.clientY; | |||
touchStart.pageX = x; | |||
touchStart.pageY = y; | |||
// 鼠标移动 | |||
const onMouseMove = (event) => { | |||
let MoveX = event.clientX; | |||
let MoveY = event.clientY; | |||
let flag = Math.sign(x - touchStart.pageX) == -1 ? true : false; | |||
if (flag) { | |||
// 限制最大缩放系数 | |||
if (props.materialList.scList[index].w > 1.3) return; | |||
props.materialList.scList[index].w += scaleNum.value; | |||
// 更新坐标位置 | |||
touchStart.pageX = MoveX; | |||
touchStart.pageY = MoveY; | |||
} else { | |||
// 限制最小缩放系数 | |||
if (props.materialList.scList[index].w < 0.4) return; | |||
props.materialList.scList[index].w -= scaleNum.value; | |||
// 更新坐标位置 | |||
touchStart.pageX = MoveX; | |||
touchStart.pageY = MoveY; | |||
} | |||
}; | |||
// 鼠标抬起 | |||
const onMouseUp = () => { | |||
document.removeEventListener("mousemove", onMouseMove); | |||
document.removeEventListener("mouseup", onMouseUp); | |||
}; | |||
document.addEventListener("mousemove", onMouseMove); | |||
document.addEventListener("mouseup", onMouseUp); | |||
} | |||
function delScListItem(index) { | |||
const msg = | |||
lanChange.value == "zh-cn" | |||
? "确定删除素材吗吗?" | |||
: "Are you sure you want to delete the footage?"; | |||
const notice = lanChange.value == "zh-cn" ? "提示" : "Notice"; | |||
const confirm = lanChange.value == "zh-cn" ? "确定" : "Confirm"; | |||
const cancel = lanChange.value == "zh-cn" ? "取消" : "Cancel"; | |||
ElMessageBox.confirm(msg, notice, { | |||
confirmButtonText: confirm, | |||
cancelButtonText: cancel, | |||
}).then(() => { | |||
props.materialList.scList.splice(index, 1); | |||
}); | |||
} | |||
//#endregion -------------------------- | |||
//#region 控制台回显 | |||
watch( | |||
() => showBorderActive.value, | |||
(newValue, oldValue) => { | |||
props.showImgInfo.id = showBorderActive.value; | |||
}, | |||
{ deep: true } | |||
); | |||
//#endregion -------------------------- | |||
//#region | |||
@@ -153,17 +409,23 @@ function changeScale(event) { | |||
.myPage { | |||
// height: 768px; | |||
// width: 432px; | |||
width: 100%; | |||
height: 100%; | |||
} | |||
.content1 { | |||
position: relative; | |||
height: 768px; | |||
width: 432px; | |||
// height: 768px; | |||
// width: 432px; | |||
width: 100%; | |||
height: 100%; | |||
overflow: hidden; | |||
} | |||
.content2 { | |||
position: relative; | |||
height: 432px; | |||
width: 768px; | |||
// height: 432px; | |||
// width: 768px; | |||
width: 100%; | |||
height: 100%; | |||
overflow: hidden; | |||
} | |||
.virtual { | |||
@@ -171,6 +433,11 @@ function changeScale(event) { | |||
user-select: none; | |||
border: rgba(0, 0, 0, 0) 5px solid; | |||
} | |||
.scListItem { | |||
position: absolute; | |||
user-select: none; | |||
border: rgba(0, 0, 0, 0) 5px solid; | |||
} | |||
.border { | |||
border: 5px solid #00ff00; | |||
.stretchBox { | |||
@@ -184,25 +451,48 @@ function changeScale(event) { | |||
top: -10px; | |||
left: -10px; | |||
cursor: nwse-resize; | |||
display: none; | |||
} | |||
.box2 { | |||
top: -10px; | |||
top: -50px; | |||
left: 100%; | |||
transform: translateX(-10px); | |||
cursor: nesw-resize; | |||
transform: translateX(-50px); | |||
// cursor: nesw-resize; | |||
cursor: pointer; | |||
background-color: rgba(0, 0, 0, 0); | |||
width: 100px; | |||
height: 100px; | |||
img { | |||
width: 100px; | |||
height: 100px; | |||
} | |||
} | |||
.box3 { | |||
top: 100%; | |||
left: -10px; | |||
transform: translateY(-10px); | |||
cursor: nesw-resize; | |||
display: none; | |||
} | |||
.box4 { | |||
top: 100%; | |||
left: 100%; | |||
transform: translate(-10px, -10px); | |||
transform: translate(-50px, -50px); | |||
cursor: nwse-resize; | |||
// background-color: rgba(0, 0, 0, 0); | |||
background-color: #fff; | |||
width: 100px; | |||
height: 100px; | |||
border-radius: 50px; | |||
img { | |||
position: absolute; | |||
top: 50%; | |||
left: 50%; | |||
transform: translate(-50%, -50%) rotate(90deg); | |||
max-width: 65px; | |||
max-height: 65px; | |||
} | |||
} | |||
} | |||
</style> |
@@ -17,12 +17,12 @@ | |||
</div> | |||
{{ $t("createVideo.title") }} | |||
</div> | |||
<div class="myRouter-item" @click="router.push('/moveImg')"> | |||
<!-- <div class="myRouter-item" @click="router.push('/myCreating')"> | |||
<div class="icon"> | |||
<el-icon><i-ep-edit /></el-icon> | |||
</div> | |||
{{ $t("createVideo.title") }} | |||
</div> | |||
{{ $t("myCreating.title") }} | |||
</div> --> | |||
</div> | |||
</div> | |||
<!-- 右边显示部分 --> | |||
@@ -24,6 +24,7 @@ declare module '@vue/runtime-core' { | |||
ElFormItem: typeof import('element-plus/es')['ElFormItem'] | |||
ElIcon: typeof import('element-plus/es')['ElIcon'] | |||
ElInput: typeof import('element-plus/es')['ElInput'] | |||
ElInputNumber: typeof import('element-plus/es')['ElInputNumber'] | |||
ElOption: typeof import('element-plus/es')['ElOption'] | |||
ElPagination: typeof import('element-plus/es')['ElPagination'] | |||
ElRow: typeof import('element-plus/es')['ElRow'] | |||
@@ -92,20 +92,68 @@ | |||
<div class="showModelImgBox"> | |||
<!-- <img :src="modelImg" alt="" /> --> | |||
<!-- 编辑图片位置组件 --> | |||
<EditPosition | |||
id="editPosition" | |||
class="EditPosition" | |||
:materialList="materialList" | |||
:videoType="videoType" | |||
:proportion="proportion" | |||
<div | |||
:class="videoType == 1 ? 'EditPositionBox1' : 'EditPositionBox2'" | |||
class="" | |||
> | |||
</EditPosition> | |||
<EditPosition | |||
id="editPosition" | |||
:materialList="materialList" | |||
:videoType="videoType" | |||
:proportion="proportion" | |||
:showImgInfo="showImgInfo" | |||
> | |||
</EditPosition> | |||
</div> | |||
<!-- 控制台 --> | |||
<div v-if="showImgInfo.data" class="showImgInfoBox"> | |||
<!-- <div class="infoItem"> | |||
</div> --> | |||
<div class="ItemName">横坐标</div> | |||
<div class="ItemValue"> | |||
<el-input-number | |||
v-model="showImgInfo.data.x" | |||
size="large" | |||
controls-position="right" | |||
/> | |||
</div> | |||
<div class="ItemName">纵坐标</div> | |||
<div class="ItemValue"> | |||
<el-input-number | |||
v-model="showImgInfo.data.y" | |||
size="large" | |||
controls-position="right" | |||
/> | |||
</div> | |||
<div class="ItemName">层级</div> | |||
<div class="ItemValue"> | |||
<el-input-number | |||
v-model="showImgInfo.data.z" | |||
:min="1" | |||
:max="10" | |||
size="large" | |||
controls-position="right" | |||
/> | |||
</div> | |||
<div class="ItemName">缩放倍率</div> | |||
<div class="ItemValue"> | |||
<el-input-number | |||
v-model="showImgInfo.data.w" | |||
:min="0.4" | |||
:max="1.3" | |||
size="large" | |||
controls-position="right" | |||
/> | |||
</div> | |||
</div> | |||
<!-- 保存视频和生成视频 --> | |||
<div class="btnBox" v-show="!showChooseModel"> | |||
<div class="customBtn" style="margin-right: 15px"> | |||
{{ $t("createVideo.saveVideo") }} | |||
</div> | |||
<div class="customBtn">{{ $t("createVideo.generateVideo") }}</div> | |||
<div class="customBtn"> | |||
{{ $t("createVideo.generateVideo") }} | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
@@ -416,6 +464,8 @@ import { | |||
personSCIListApi, | |||
delImgApi, | |||
personPatchApi, | |||
saveOrUpdateApi, | |||
createVideoApi, | |||
} from "@/apis/createVideo"; | |||
import { useI18n } from "vue-i18n"; | |||
import Recorder from "js-audio-recorder"; //mp3插件 | |||
@@ -534,7 +584,8 @@ const tabItemActive = ref(1); | |||
//#endregion ---------------------------- | |||
//#region 文案及其音频 | |||
const title = ref(null); | |||
const title = | |||
lanChange.value == "zh-cn" ? ref("未定义标题") : ref("Undefined Title"); | |||
const textareaContent = ref(""); | |||
function insertIcon() { | |||
const textarea = document.getElementById("textInputId"); | |||
@@ -743,6 +794,8 @@ async function reGetpersonBGIList() { | |||
const bjImgActive = ref(null); | |||
function selectBjImg(img) { | |||
bjImgActive.value = img.id; | |||
materialList.BGI.material = img.material; | |||
materialList.BGI.id = img.id; | |||
} | |||
//#endregion ---------------------------- | |||
@@ -812,6 +865,7 @@ async function reGetpersonSCIList() { | |||
const scImgActive = ref(null); | |||
function selectScImg(img) { | |||
scImgActive.value = img.id; | |||
materialList.scList.push({ ...img, x: 0, y: 0, z: 0, w: 1, h: 1 }); | |||
} | |||
// 删除上传的背景或素材 | |||
function delImg(id) { | |||
@@ -849,8 +903,10 @@ watch( | |||
(newValue, oldValue) => { | |||
// 根据横竖屏来确定比例 1竖2横 | |||
if (videoType.value == 1) { | |||
proportion.value = 1080 / elementWidth.value; | |||
// proportion.value = 1080 / elementWidth.value; | |||
proportion.value = 1080 / 432; | |||
} else if (videoType.value == 2) { | |||
proportion.value = 1920 / 768; | |||
proportion.value = 1920 / elementWidth.value; | |||
} | |||
console.log(proportion.value); | |||
@@ -891,6 +947,101 @@ watch( | |||
); | |||
//#endregion ---------------------------- | |||
//#region 编辑区域控制台 | |||
const showImgInfo = ref({ | |||
id: null, | |||
data: null, | |||
}); //显示素材or数字人的id | |||
// const showImgInfoData = ref(null); | |||
watch( | |||
() => showImgInfo, | |||
(newValue, oldValue) => { | |||
if (materialList.virtual.id == showImgInfo.value.id) { | |||
showImgInfo.value.data = materialList.virtual; | |||
} else { | |||
showImgInfo.value.data = materialList.scList.find( | |||
(item) => item.id == showImgInfo.value.id | |||
); | |||
} | |||
}, | |||
{ deep: true } | |||
); | |||
//#endregion ---------------------------- | |||
//#region 保存视频和生成草稿 | |||
const workId = ref(null); //保存作品完成后会有一个id,用于生成视频 | |||
async function saveOrUpdate() { | |||
try { | |||
if (!textareaContent.value) { | |||
ElMessage.error( | |||
lanChange.value == "zh-cn" | |||
? `您还没有输入文案` | |||
: `You have not entered the copy yet` | |||
); | |||
return; | |||
} | |||
if (!sound.value && !soundStyle.value) { | |||
ElMessage.error( | |||
lanChange.value == "zh-cn" | |||
? `您还没有选择声音` | |||
: `You have not selected a sound` | |||
); | |||
return; | |||
} | |||
// 处理数字人 | |||
const shuziren = { | |||
id: materialList.virtual.id, | |||
type: 1, | |||
material: materialList.virtual.material, | |||
x: materialList.virtual.x, | |||
y: materialList.virtual.y, | |||
z: materialList.virtual.z, | |||
w: materialList.virtual.w, | |||
h: materialList.virtual.h, | |||
}; | |||
// 处理素材 | |||
const scList = materialList.scList.map((item) => { | |||
return { | |||
id: item.id, | |||
type: 5, | |||
material: item.material, | |||
x: item.x, | |||
y: item.y, | |||
z: item.z, | |||
w: item.w, | |||
h: item.h, | |||
}; | |||
}); | |||
// 处理上传数据 | |||
const data = { | |||
id: workId.value, | |||
videoType: videoType.value, //横屏竖屏 | |||
personMouldId: materialList.virtual.id, //数字人id | |||
voiceMouldIds: [soundStyle.value ? soundStyle.value : sound.value], //声音id | |||
title: title.value, //标题 | |||
paperwork: textareaContent.value, //语音内容 | |||
backgroundId: materialList.BGI.id + "", //背景id | |||
personJson: JSON.stringify(shuziren), // 数字人 | |||
materialAllJson: JSON.stringify([...scList]), // 素材 | |||
}; | |||
const res = await saveOrUpdateApi(data); | |||
workId.value = res.data.id; | |||
} catch (error) { | |||
ElMessage.error(error); | |||
} | |||
} | |||
async function createVideo() { | |||
try { | |||
await saveOrUpdate(); | |||
const res2 = await createVideoApi(workId.value); | |||
} catch (error) { | |||
ElMessage.error(error); | |||
} | |||
} | |||
//#endregion ---------------------------- | |||
//#region | |||
//#endregion ---------------------------- | |||
@@ -1080,13 +1231,54 @@ onMounted(async () => { | |||
max-width: 800px; | |||
max-height: 800px; | |||
} | |||
.EditPosition { | |||
.EditPositionBox1 { | |||
position: absolute; | |||
top: 50%; | |||
left: 50%; | |||
transform: translate(-50%, -50%); | |||
max-width: 800px; | |||
max-height: 800px; | |||
height: 768px; | |||
width: 432px; | |||
} | |||
.EditPositionBox2 { | |||
position: absolute; | |||
top: 50%; | |||
left: 50%; | |||
transform: translate(-50%, -50%); | |||
height: 432px; | |||
width: 768px; | |||
} | |||
.showImgInfoBox { | |||
position: absolute; | |||
top: 200px; | |||
right: 0px; | |||
width: 120px; | |||
background-color: #e9ecf1; | |||
border-radius: 20px; | |||
padding: 10px; | |||
box-shadow: rgba(0, 0, 0, 0.5) 0px 0px 15px; | |||
.ItemName { | |||
width: 100%; | |||
height: 40px; | |||
text-align: center; | |||
line-height: 40px; | |||
background-color: #fff; | |||
border-radius: 10px; | |||
} | |||
.ItemValue { | |||
margin-bottom: 10px; | |||
width: 100%; | |||
height: 40px; | |||
text-align: center; | |||
line-height: 40px; | |||
background-color: #f5f7fa; | |||
border-radius: 10px; | |||
.el-input-number--large { | |||
width: 100px; | |||
} | |||
.el-input-number__increase { | |||
width: none; | |||
} | |||
} | |||
} | |||
.btnBox { | |||
position: absolute; | |||