@@ -25,7 +25,7 @@ | |||
</div> | |||
{{ $t("myCreating.title") }} | |||
</div> | |||
<!-- 测试功能页 --> | |||
<!-- 测试功能页 v-if="false"--> | |||
<div | |||
v-if="false" | |||
class="myRouter-item" | |||
@@ -8,10 +8,13 @@ export {} | |||
declare module '@vue/runtime-core' { | |||
export interface GlobalComponents { | |||
Breadcrumb: typeof import('./../components/Breadcrumb/index.vue')['default'] | |||
ElAlert: typeof import('element-plus/es')['ElAlert'] | |||
ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb'] | |||
ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem'] | |||
ElButton: typeof import('element-plus/es')['ElButton'] | |||
ElCard: typeof import('element-plus/es')['ElCard'] | |||
ElCol: typeof import('element-plus/es')['ElCol'] | |||
ElColorPicker: typeof import('element-plus/es')['ElColorPicker'] | |||
ElDialog: typeof import('element-plus/es')['ElDialog'] | |||
ElDivider: typeof import('element-plus/es')['ElDivider'] | |||
ElDropdown: typeof import('element-plus/es')['ElDropdown'] | |||
@@ -21,35 +24,54 @@ 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'] | |||
ElLink: typeof import('element-plus/es')['ElLink'] | |||
ElOption: typeof import('element-plus/es')['ElOption'] | |||
ElPagination: typeof import('element-plus/es')['ElPagination'] | |||
ElPopover: typeof import('element-plus/es')['ElPopover'] | |||
ElRadio: typeof import('element-plus/es')['ElRadio'] | |||
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] | |||
ElRow: typeof import('element-plus/es')['ElRow'] | |||
ElScrollbar: typeof import('element-plus/es')['ElScrollbar'] | |||
ElSelect: typeof import('element-plus/es')['ElSelect'] | |||
ElSwitch: typeof import('element-plus/es')['ElSwitch'] | |||
ElTable: typeof import('element-plus/es')['ElTable'] | |||
ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] | |||
ElTabPane: typeof import('element-plus/es')['ElTabPane'] | |||
ElTabs: typeof import('element-plus/es')['ElTabs'] | |||
ElTag: typeof import('element-plus/es')['ElTag'] | |||
ElTooltip: typeof import('element-plus/es')['ElTooltip'] | |||
ElTree: typeof import('element-plus/es')['ElTree'] | |||
ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect'] | |||
ElUpload: typeof import('element-plus/es')['ElUpload'] | |||
GithubCorner: typeof import('./../components/GithubCorner/index.vue')['default'] | |||
Hamburger: typeof import('./../components/Hamburger/index.vue')['default'] | |||
IconSelect: typeof import('./../components/IconSelect/index.vue')['default'] | |||
IEpCaretBottom: typeof import('~icons/ep/caret-bottom')['default'] | |||
IEpCaretTop: typeof import('~icons/ep/caret-top')['default'] | |||
IEpClose: typeof import('~icons/ep/close')['default'] | |||
IEpCloseBold: typeof import('~icons/ep/close-bold')['default'] | |||
IEpCollection: typeof import('~icons/ep/collection')['default'] | |||
IEpDelete: typeof import('~icons/ep/delete')['default'] | |||
IEpDocumentAdd: typeof import('~icons/ep/document-add')['default'] | |||
IEpDownload: typeof import('~icons/ep/download')['default'] | |||
IEpEdit: typeof import('~icons/ep/edit')['default'] | |||
IEpFolder: typeof import('~icons/ep/folder')['default'] | |||
IEpFolderAdd: typeof import('~icons/ep/folder-add')['default'] | |||
IEpGoods: typeof import('~icons/ep/goods')['default'] | |||
IEpHeadset: typeof import('~icons/ep/headset')['default'] | |||
IEpLoading: typeof import('~icons/ep/loading')['default'] | |||
IEpLock: typeof import('~icons/ep/lock')['default'] | |||
IEpMenu: typeof import('~icons/ep/menu')['default'] | |||
IEpMic: typeof import('~icons/ep/mic')['default'] | |||
IEpPlus: typeof import('~icons/ep/plus')['default'] | |||
IEpPosition: typeof import('~icons/ep/position')['default'] | |||
IEpRefresh: typeof import('~icons/ep/refresh')['default'] | |||
IEpRefreshLeft: typeof import('~icons/ep/refresh-left')['default'] | |||
IEpSearch: typeof import('~icons/ep/search')['default'] | |||
IEpSetting: typeof import('~icons/ep/setting')['default'] | |||
IEpSwitchButton: typeof import('~icons/ep/switch-button')['default'] | |||
IEpTop: typeof import('~icons/ep/top')['default'] | |||
IEpUploadFilled: typeof import('~icons/ep/upload-filled')['default'] | |||
IEpVideoCamera: typeof import('~icons/ep/video-camera')['default'] | |||
IEpVideoPause: typeof import('~icons/ep/video-pause')['default'] | |||
IEpVideoPlay: typeof import('~icons/ep/video-play')['default'] | |||
LangSelect: typeof import('./../components/LangSelect/index.vue')['default'] | |||
@@ -64,10 +86,9 @@ declare module '@vue/runtime-core' { | |||
SizeSelect: typeof import('./../components/SizeSelect/index.vue')['default'] | |||
SvgIcon: typeof import('./../components/SvgIcon/index.vue')['default'] | |||
WangEditor: typeof import('./../components/WangEditor/index.vue')['default'] | |||
Websocket: typeof import('./../components/websocket.vue')['default'] | |||
Websoket: typeof import('./../components/websoket.vue')['default'] | |||
} | |||
export interface ComponentCustomProperties { | |||
vInfiniteScroll: typeof import('element-plus/es')['ElInfiniteScroll'] | |||
vLoading: typeof import('element-plus/es')['ElLoadingDirective'] | |||
} | |||
} |
@@ -18,18 +18,42 @@ | |||
:placeholder="$t('createVideo.typeTitle')" | |||
/> | |||
<el-icon class="icon-edit"><i-ep-edit /></el-icon> | |||
<!-- 编辑字幕框 --> | |||
</div> | |||
<div v-if="imgUrl&&SubtitlesSwitch" class="editSubtitlesBox "> | |||
<div class="item">X: <el-input | |||
v-model="SubtitlesTop" | |||
width="30" | |||
/></div> | |||
<div class="item">Y: <el-input | |||
v-model="SubtitlesLeft" | |||
width="30" | |||
/></div> | |||
<div class="item">Size: <el-input | |||
v-model="SubtitlesSize" | |||
width="30" | |||
/></div> | |||
<div class="item">Color: <el-color-picker v-model="SubtitlesColor" show-alpha /></div> | |||
<div class="item">BGC: <el-color-picker v-model="SubtitlesBgc" show-alpha /></div> | |||
</div> | |||
</el-row> | |||
<!-- 中间展示大图 --> | |||
<div class="bigImg inmiddle"> | |||
<div v-if="false" class="canvasBox"> | |||
<canvas id="myCanvas" ref="myCanvasRef" > | |||
</canvas> | |||
</div> | |||
<img id="canvasImg" ref="canvasImgRef" :src="imgUrl" alt="" class="inmiddle" /> | |||
<div ref="canvasImgBox" class="bigImg inmiddle"> | |||
<img id="canvasImg" :src="imgUrl" alt="" class="" /> | |||
<div ref="SubtitlesBox" @mousedown="handleDrag" :style="SubtitlesStyle" v-if="SubtitlesSwitch" class="SubtitlesBox">请在文案区编辑您的正文 | |||
<div class="stretchBox box1"></div> | |||
<div class="stretchBox box2"></div> | |||
<div class="stretchBox box3"></div> | |||
<div class="stretchBox box4"></div> | |||
<div class="stretchBox box5"></div> | |||
<div class="stretchBox box6"></div> | |||
<div class="stretchBox box7"></div> | |||
<div class="stretchBox box8"></div> | |||
</div> | |||
</div> | |||
<!-- 字幕开关 --> | |||
<div v-if="false" class="SubtitlesSwitch"> | |||
<div v-if="imgUrl" class="SubtitlesSwitch"> | |||
<p>{{ $t('createVideo.subtitles') }}</p> | |||
<el-switch v-model="SubtitlesSwitch" /> | |||
</div> | |||
@@ -1915,42 +1939,49 @@ function copyAnswerContent() { | |||
//#endregion ---------------------------- | |||
//#region 字幕部分 | |||
const canvasImgBox=ref(null) //图片外盒子dom | |||
const SubtitlesBox=ref(null) //字幕盒子dom | |||
const SubtitlesSwitch=ref(false) //字幕开关 | |||
const canvas = ref(null); //canvas对象 | |||
const myCanvasRef = ref(null); //画布dom | |||
const canvasImgRef = ref(null); //图片dom | |||
const imgWidth=ref(null) | |||
const imgHeight=ref(null) | |||
const myText = ref("hello world"); | |||
const iText = new fabric.IText(myText.value, { | |||
left: 100, | |||
top: 100, | |||
}); | |||
function setCanvasText() { | |||
canvas.value = new fabric.Canvas("myCanvas",{ | |||
width:imgWidth.value, | |||
height:imgHeight.value, | |||
}); | |||
iText.on("mouseup", function () { | |||
console.log(iText, "iText"); | |||
console.log(iText.angle.toFixed(0), "angle"); | |||
console.log(iText.left.toFixed(0), "left"); | |||
console.log(iText.top.toFixed(0), "top"); | |||
console.log(iText.width.toFixed(0), "width"); | |||
console.log(iText.height.toFixed(0), "height"); | |||
}); | |||
canvas.value.add(iText); | |||
} | |||
const SubtitlesTop=ref(0) //字幕Y轴 | |||
const SubtitlesLeft=ref(0) //字幕X轴 | |||
const SubtitlesSize=ref(18) //字幕字号 | |||
const SubtitlesColor=ref('rgba(0, 0, 0, 1)') //字幕字色 | |||
const SubtitlesBgc=ref('rgba(255, 255, 255,1 )') //字幕背景色 | |||
// 计算属性来计算盒子位置 | |||
const SubtitlesStyle = computed(() => { | |||
return { | |||
top: SubtitlesTop.value + 'px', | |||
left: SubtitlesLeft.value + 'px', | |||
fontSize: SubtitlesSize.value+ 'px', | |||
color:SubtitlesColor.value, | |||
backgroundColor: SubtitlesBgc.value, | |||
}; | |||
}); | |||
// 拖动盒子 | |||
const handleDrag = (event) => { | |||
event.preventDefault(); | |||
const boxElement = canvasImgBox.value; | |||
const imageElement = SubtitlesBox.value; | |||
const offsetX = event.clientX - imageElement.offsetLeft; | |||
const offsetY = event.clientY - imageElement.offsetTop; | |||
const onMouseMove = (event) => { | |||
const newX = event.clientX - offsetX; | |||
const newY = event.clientY - offsetY; | |||
SubtitlesLeft.value= newX; | |||
SubtitlesTop.value = newY; | |||
}; | |||
const onMouseUp = () => { | |||
document.removeEventListener('mousemove', onMouseMove); | |||
document.removeEventListener('mouseup', onMouseUp); | |||
}; | |||
document.addEventListener('mousemove', onMouseMove); | |||
document.addEventListener('mouseup', onMouseUp); | |||
}; | |||
// 监听字幕显示 | |||
watch( | |||
() => SubtitlesSwitch, | |||
async (newValue, oldValue) => { | |||
if(SubtitlesSwitch.value) { | |||
setCanvasText() | |||
} else { | |||
canvas.value.remove(iText); | |||
canvas.value=null; | |||
} | |||
}, | |||
{ deep: true } | |||
); | |||
@@ -1958,11 +1989,8 @@ watch( | |||
watch( | |||
() => imgUrl.value, | |||
async (newValue, oldValue) => { | |||
await nextTick() | |||
if(canvas.value) { | |||
canvas.value.width=imgWidth.value, | |||
canvas.value.height=imgHeight.value | |||
} | |||
await nextTick() | |||
// console.log(getComputedStyle(document.getElementById("canvasImg"),null)); | |||
}, | |||
{ deep: true } | |||
); | |||
@@ -2013,7 +2041,7 @@ onMounted(async () => { | |||
} | |||
}); | |||
createWS(); //创建websocket | |||
}); | |||
//#endregion ---------------------------- | |||
@@ -2067,6 +2095,34 @@ onUnmounted(() => { | |||
height: 67%; | |||
// height: calc(70vh - 80px ); | |||
margin-bottom: 20px; | |||
.editSubtitlesBox{ | |||
display: flex; | |||
justify-content: flex-start; | |||
padding-right: 80px; | |||
margin-left: 10px; | |||
margin-right: 80px; | |||
border-radius: 20px; | |||
flex: 1; | |||
line-height: 35px; | |||
background-color: #f1f2f6; | |||
font-size: 16px; | |||
.item { | |||
display: flex; | |||
align-items: center; | |||
margin-left: 10px; | |||
.el-input { | |||
width: 60px; | |||
height: 25px; | |||
} | |||
} | |||
:deep(.el-input__wrapper) { | |||
border: none; | |||
border: 2px solid #000; | |||
border-radius: 5px; | |||
box-shadow: none; | |||
} | |||
} | |||
.uploadBtn { | |||
position: relative; | |||
padding: 10px 15px; | |||
@@ -2086,34 +2142,74 @@ onUnmounted(() => { | |||
width: 50%; | |||
height: 88%; | |||
margin-top: 20px; | |||
.canvasBox { | |||
.SubtitlesBox { | |||
position: absolute; | |||
left: 0; | |||
top: 0; | |||
width: 100%; | |||
height: 100%; | |||
background-color: skyblue; | |||
opacity: 0.5; | |||
// z-index: 9; | |||
.canvas-container { | |||
// background-color: red; | |||
// z-index: 9; | |||
// width: 100%!important; | |||
// height: 100%!important; | |||
// #myCanvas { | |||
// width: 100%!important; | |||
// height: 100%!important; | |||
// } | |||
// .upper-canvas { | |||
// width: 100%!important; | |||
// height: 100%!important; | |||
// } | |||
user-select: none; | |||
width: 300px; | |||
padding: 5px 5px; | |||
border: 2px solid #00ff00; | |||
text-align: center; | |||
background-color: #fff; | |||
cursor: move; | |||
.stretchBox { | |||
position: absolute; | |||
width: 10px; | |||
height: 10px; | |||
background-color: #fff; | |||
} | |||
.box1 { | |||
top: -5px; | |||
left: -5px; | |||
cursor: nwse-resize; | |||
} | |||
.box2 { | |||
top: -5px; | |||
left: 50%; | |||
transform: translateX(-50%); | |||
cursor: ns-resize; | |||
} | |||
.box3 { | |||
top: -5px; | |||
left:100%; | |||
transform: translateX(-5px); | |||
cursor: nesw-resize; | |||
} | |||
.box4 { | |||
top: 50%; | |||
left: -5px; | |||
transform: translateY(-5px); | |||
cursor: ew-resize; | |||
} | |||
.box5 { | |||
top: 50%; | |||
left:100%; | |||
transform: translate(-5px,-5px); | |||
cursor: ew-resize; | |||
} | |||
.box6 { | |||
top: 100%; | |||
left: -5px; | |||
transform: translateY(-5px); | |||
cursor: nesw-resize; | |||
} | |||
.box7 { | |||
top: 100%; | |||
left: 50%; | |||
transform: translate(-5px,-5px); | |||
cursor: ns-resize; | |||
} | |||
.box8 { | |||
top: 100%; | |||
left: 100%; | |||
transform: translate(-5px,-5px); | |||
cursor: nwse-resize; | |||
} | |||
} | |||
img { | |||
// position: absolute; | |||
// left: 0; | |||
// top: 0; | |||
position: absolute; | |||
left: 0; | |||
top: 0; | |||
max-width: 100%; | |||
max-height: 100%; | |||
border-radius: 15px; | |||
@@ -1,136 +1,92 @@ | |||
<template> | |||
<div class="page"> | |||
<div class="canvasBox"> | |||
<canvas id="myCanvas" width="960" height="540"> </canvas> | |||
<img | |||
style="display: none; width: 200px; height: 200px" | |||
id="bjImg" | |||
src="../../assets/img/star.png" | |||
alt="" | |||
/> | |||
<div> | |||
<img src="../../assets/img/trueImg.png" ref="image" /> | |||
<div | |||
class="box" | |||
ref="box" | |||
:style="{ top: boxPosition.top + 'px', left: boxPosition.left + 'px' }" | |||
@mousedown="handleDrag" | |||
> | |||
<!-- 这里可以放置盒子的内容 --> | |||
</div> | |||
<div class="container"> | |||
<div class="box red"></div> | |||
<div class="box white"></div> | |||
<div class="box green"></div> | |||
<div class="resizable-box" v-if="isResizing" @mousedown="handleResize"> | |||
字幕 | |||
</div> | |||
<button @click="handleRotate">旋转</button> | |||
</div> | |||
</template> | |||
<script setup> | |||
//#region 导入 | |||
import { fabric } from "fabric"; | |||
import { ref, reactive } from "vue"; | |||
//#endregion --------------------------- | |||
const image = ref(null); | |||
const box = ref(null); | |||
const boxPosition = reactive({ top: 0, left: 0 }); | |||
const isResizing = ref(false); | |||
const rotationAngle = ref(0); | |||
//#region 画布交互 | |||
// let canvas = new fabric.Canvas("myCanvas"); | |||
let canvas = null; | |||
function setCanvasText() { | |||
// let imgElement = document.getElementById("bjImg"); //声明我们的图片 | |||
const myText = ref("hello world"); | |||
const iText = new fabric.IText(myText.value, { | |||
left: 100, | |||
top: 100, | |||
}); | |||
canvas.add(iText); | |||
iText.on("mouseup", function () { | |||
console.log(iText, "iText"); | |||
console.log(iText.angle.toFixed(0), "angle"); | |||
console.log(iText.left.toFixed(0), "left"); | |||
console.log(iText.top.toFixed(0), "top"); | |||
console.log(iText.width.toFixed(0), "width"); | |||
console.log(iText.height.toFixed(0), "height"); | |||
}); | |||
} | |||
//#endregion --------------------------- | |||
// 盒子位置拖动 | |||
const handleDrag = (event) => { | |||
event.preventDefault(); | |||
const boxElement = box.value; | |||
const imageElement = image.value; | |||
onMounted(() => { | |||
canvas = new fabric.Canvas("myCanvas"); | |||
setCanvasText(); | |||
// let imgInstance = new fabric.Image(imgElement, { | |||
// //设置图片位置和样子 | |||
// // left: 100, | |||
// // top: 100, | |||
// // width: 400, | |||
// // height: 200, | |||
// // angle: 30, //设置图形顺时针旋转角度 | |||
// }); | |||
// console.log(imgInstance); | |||
// canvas.add(imgInstance); | |||
}); | |||
</script> | |||
const offsetX = event.clientX - imageElement.offsetLeft; | |||
const offsetY = event.clientY - imageElement.offsetTop; | |||
<style lang="scss" scoped> | |||
.page { | |||
position: relative; | |||
padding: 10px; | |||
} | |||
.canvasBox { | |||
// position: absolute; | |||
// top: 50%; | |||
// left: 50%; | |||
// transform: translate(-50%); | |||
width: 100%; | |||
height: 100%; | |||
.canvas-container { | |||
} | |||
background-color: green; | |||
.canvas { | |||
background-color: skyblue; | |||
.bjImg { | |||
position: absolute; | |||
top: 0; | |||
left: 0; | |||
width: 100px; | |||
height: 100px; | |||
z-index: 999; | |||
img { | |||
width: 100px; | |||
height: 100px; | |||
} | |||
} | |||
.people { | |||
} | |||
.other { | |||
} | |||
} | |||
} | |||
</style> | |||
const onMouseMove = (event) => { | |||
const newX = event.clientX - offsetX; | |||
const newY = event.clientY - offsetY; | |||
<style lang="scss" scoped> | |||
.container { | |||
display: flex; | |||
align-items: center; | |||
justify-content: space-between; | |||
width: 80px; | |||
height: 20px; | |||
perspective: 500px; | |||
transition: transform 0.5s; | |||
} | |||
boxPosition.left = newX; | |||
boxPosition.top = newY; | |||
}; | |||
.box { | |||
width: 20px; | |||
height: 20px; | |||
transition: transform 0.5s; | |||
transform-style: preserve-3d; | |||
} | |||
const onMouseUp = () => { | |||
document.removeEventListener("mousemove", onMouseMove); | |||
document.removeEventListener("mouseup", onMouseUp); | |||
}; | |||
.red { | |||
background-color: red; | |||
} | |||
document.addEventListener("mousemove", onMouseMove); | |||
document.addEventListener("mouseup", onMouseUp); | |||
}; | |||
.white { | |||
background-color: white; | |||
transform: translateZ(20px); | |||
} | |||
// 边框拖动 | |||
const handleResize = () => { | |||
isResizing.value = true; | |||
const onMouseMove = (event) => { | |||
// 处理拉伸逻辑 | |||
}; | |||
const onMouseUp = () => { | |||
isResizing.value = false; | |||
document.removeEventListener("mousemove", onMouseMove); | |||
document.removeEventListener("mouseup", onMouseUp); | |||
}; | |||
.green { | |||
background-color: green; | |||
transform: translateZ(-20px); | |||
document.addEventListener("mousemove", onMouseMove); | |||
document.addEventListener("mouseup", onMouseUp); | |||
}; | |||
// 图片旋转 | |||
const handleRotate = () => { | |||
rotationAngle.value += 90; | |||
}; | |||
</script> | |||
<style> | |||
.box { | |||
position: absolute; | |||
width: 100px; | |||
height: 100px; | |||
background-color: rgba(0, 0, 0, 0.5); | |||
cursor: move; | |||
} | |||
.container:hover .box { | |||
transform: rotateY(180deg); | |||
.resizable-box { | |||
border: 2px dashed #000; | |||
resize: both; | |||
overflow: auto; | |||
} | |||
</style> |