该文本描述了一个基于Vue.js的拖动功能和画布设置的Web应用。用户可以通过左侧的功能菜单添加文字或图片元素到画布中,并可以调整这些元素的样式和位置。应用提供了设置画布的宽度、高度、背景颜色和背景图片的功能,同时支持预览和下载画布内容的功能。整体设计旨在提供一个直观的用户界面,方便用户进行图形设计。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>拖动功能与画布设置</title>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
<style>
.preview-area {
position: relative;
margin: 20px 0;
background-color: #f8f9fa;
border: 1px solid #ccc;
padding: 10px;
overflow: hidden;
}
.draggable-element {
position: absolute;
cursor: move;
display: flex;
justify-content: center;
align-items: center;
}
.modal-content {
width: 100%;
}
.settings-panel input,
.settings-panel label {
width: 100%;
margin-bottom: 10px;
}
#canvasContainer {
margin: 20px auto;
border: 1px solid #ccc;
position: relative;
background-color: white;
}
.sidebar {
background-color: #f4f4f4;
padding: 20px;
border-right: 1px solid #ddd;
height: 100vh;
}
.tool {
margin-bottom: 15px;
margin-right: 10px;
}
.preview-title {
text-align: center;
font-weight: bold;
margin-bottom: 15px;
}
.btn-group {
margin-bottom: 15px;
}
</style>
</head>
<body>
<div id="app" class="container-fluid">
<!-- 顶部菜单栏 -->
<div class="row bg-light py-2 mb-3">
<div class="col-md-12 text-center">
<div class="btn-group">
<button class="btn btn-primary" @click="showSettings = true">设置</button>
<button class="btn btn-secondary" @click="previewCanvas">预览</button>
<button class="btn btn-success" @click="downloadCanvas">下载</button>
</div>
</div>
</div>
<div class="row">
<!-- 左侧功能区域 -->
<div class="col-md-2 sidebar">
<h3>功能菜单</h3>
<div class="tool btn btn-outline-dark" v-for="tool in tools" :key="tool.type" @dragstart="onDragStart(tool)" draggable="true">
{{ tool.name }}
</div>
</div>
<!-- 中间预览区域 -->
<div class="col-md-7">
<h4 class="preview-title">画布预览区域</h4>
<div id="canvasContainer"
:style="{ width: canvasWidth + 'px', height: canvasHeight + 'px', backgroundColor: canvasBgColor, backgroundImage: canvasBgImage ? 'url(' + canvasBgImage + ')' : 'none' }"
class="preview-area"
@dragover.prevent
@drop="onDrop">
<div
v-for="(element, index) in elements"
:key="index"
class="draggable-element"
:style="{ left: element.x + 'px', top: element.y + 'px', fontSize: element.size + 'px', color: element.color, width: element.width + 'px', fontWeight: element.bold ? 'bold' : 'normal', height: element.height + 'px', borderRadius: element.borderRadius + 'px' }"
@mousedown="onMouseDown($event, index)"
>
<template v-if="element.type === 'text'">
{{ element.content }}
</template>
<template v-if="element.type === 'image'">
<img :src="element.src" :style="{ width: '100%', height: '100%', borderRadius: element.borderRadius + 'px' }" />
</template>
</div>
</div>
</div>
<!-- 右侧参数修改区域 -->
<div class="col-md-3 bg-light py-2">
<h4>参数设置</h4>
<div v-if="selectedElement !== null">
<div v-if="elements[selectedElement].type === 'text'">
<label>文字内容:</label>
<input v-model="elements[selectedElement].content" class="form-control"/>
<label>文字大小:</label>
<input type="range" v-model="elements[selectedElement].size" min="10" max="100" class="form-control"/>
<label>文字宽度:</label>
<input type="range" v-model="elements[selectedElement].width" min="50" max="500" class="form-control"/>
<label>文字颜色:</label>
<input type="color" v-model="elements[selectedElement].color" class="form-control"/>
<label>文字加粗:</label>
<input type="checkbox" v-model="elements[selectedElement].bold" class="form-check-input"/>
</div>
<div v-if="elements[selectedElement].type === 'image'">
<label>上传图片:</label>
<input type="file" @change="onImageUpload" class="form-control"/>
<label>宽度:</label>
<input type="number" v-model="elements[selectedElement].width" class="form-control"/>
<label>高度:</label>
<input type="number" v-model="elements[selectedElement].height" class="form-control"/>
<label>圆角:</label>
<input type="range" v-model="elements[selectedElement].borderRadius" min="0" max="50" class="form-control"/>
</div>
</div>
</div>
</div>
<!-- 设置弹出层 -->
<div v-if="showSettings" class="modal" tabindex="-1" role="dialog" style="display: block;">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">画布设置</h5>
<button type="button" class="close" @click="showSettings = false">
<span>×</span>
</button>
</div>
<div class="modal-body">
<label>画布宽度:</label>
<input type="number" v-model="canvasWidth" class="form-control" />
<label>画布高度:</label>
<input type="number" v-model="canvasHeight" class="form-control" />
<label>背景颜色:</label>
<input type="color" v-model="canvasBgColor" class="form-control" />
<label>背景图片:</label>
<input type="file" @change="onCanvasBgUpload" class="form-control" />
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" @click="showSettings = false">保存</button>
</div>
</div>
</div>
</div>
<!-- 预览弹出层 -->
<div v-if="showPreview" class="modal" tabindex="-1" role="dialog" style="display: block;">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">预览效果</h5>
<button type="button" class="close" @click="showPreview = false">
<span>×</span>
</button>
</div>
<div class="modal-body">
<div :style="{ width: canvasWidth + 'px', height: canvasHeight + 'px', backgroundColor: canvasBgColor, backgroundImage: canvasBgImage ? 'url(' + canvasBgImage + ')' : 'none' }" class="preview-area">
<div
v-for="(element, index) in elements"
:key="index"
class="draggable-element"
:style="{ left: element.x + 'px', top: element.y + 'px', fontSize: element.size + 'px', color: element.color, width: element.width + 'px', fontWeight: element.bold ? 'bold' : 'normal', height: element.height + 'px', borderRadius: element.borderRadius + 'px' }"
>
<template v-if="element.type === 'text'">
{{ element.content }}
</template>
<template v-if="element.type === 'image'">
<img :src="element.src" :style="{ width: '100%', height: '100%', borderRadius: element.borderRadius + 'px' }" />
</template>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" @click="showPreview = false">关闭</button>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.min.js"></script>
<script src="https://html2canvas.hertzen.com/dist/html2canvas.min.js"></script>
<script>
new Vue({
el: "#app",
data: {
tools: [
{ name: "文字功能", type: "text" },
{ name: "图片功能", type: "image" }
],
elements: [],
selectedElement: null,
draggedElement: null,
isDragging: false,
dragOffsetX: 0,
dragOffsetY: 0,
showSettings: false,
showPreview: false,
canvasWidth: 800,
canvasHeight: 800,
canvasBgColor: "#ffffff",
canvasBgImage: null
},
methods: {
onDragStart(tool) {
this.draggedElement = tool;
},
onDrop(event) {
if (this.draggedElement) {
if (this.draggedElement.type === "text") {
this.elements.push({
type: "text",
content: "输入文字",
size: 16,
color: "#000000",
x: event.offsetX,
y: event.offsetY,
width: 150,
height: 50,
bold: false,
borderRadius: 0
});
} else if (this.draggedElement.type === "image") {
this.elements.push({
type: "image",
src: "",
x: event.offsetX,
y: event.offsetY,
width: 100,
height: 100,
borderRadius: 0
});
}
this.draggedElement = null;
}
},
selectElement(index) {
this.selectedElement = index;
},
onImageUpload(event) {
const file = event.target.files[0];
const reader = new FileReader();
reader.onload = (e) => {
this.elements[this.selectedElement].src = e.target.result;
};
reader.readAsDataURL(file);
},
onCanvasBgUpload(event) {
const file = event.target.files[0];
const reader = new FileReader();
reader.onload = (e) => {
this.canvasBgImage = e.target.result;
};
reader.readAsDataURL(file);
},
onMouseDown(event, index) {
this.isDragging = true;
this.selectedElement = index;
const element = this.elements[index];
this.dragOffsetX = event.clientX - element.x;
this.dragOffsetY = event.clientY - element.y;
window.addEventListener("mousemove", this.onMouseMove);
window.addEventListener("mouseup", this.onMouseUp);
},
onMouseMove(event) {
if (this.isDragging && this.selectedElement !== null) {
const element = this.elements[this.selectedElement];
element.x = event.clientX - this.dragOffsetX;
element.y = event.clientY - this.dragOffsetY;
}
},
onMouseUp() {
this.isDragging = false;
window.removeEventListener("mousemove", this.onMouseMove);
window.removeEventListener("mouseup", this.onMouseUp);
},
downloadCanvas() {
html2canvas(document.querySelector("#canvasContainer")).then((canvas) => {
const link = document.createElement("a");
link.href = canvas.toDataURL();
link.download = "canvas.png";
link.click();
});
},
previewCanvas() {
this.showPreview = true;
}
}
});
</script>
</body>
</html>