vue-drag-resize
vue-drag-resize:和拖拽相关的库
- vuedraggable:主要用于列表项的拖拽排序。
- vue-drag-resize:主要用于需要用户交互调整大小和位置的元素,如看板、图表、可视化编辑器等。
基本使用
安装这个库:
bash
npm install vue-drag-resize
安装的版本信息:"vue-drag-resize": "^1.5.4"
接下来从 vue-drag-resize/src 中可以导入一个组件 VueDragResize,该组件提供一个插槽,可以存放要 resize 的模板内容。
基本示例核心代码:
vue
<template>
<div id="app">
<VueDragResize
:w="200"
:h="150"
:x="100"
:y="100"
:min-width="50"
:min-height="50"
@resizing="resizeHandle"
@dragging="() => console.log('拖拽中')"
>
<div class="content">可拖拽和调整大小的元素</div>
</VueDragResize>
</div>
</template>
<script setup>
// 注意,这里是从vue-drag-resize下面的src目录导出的组件
import VueDragResize from "vue-drag-resize/src";
const resizeHandle = (size) => {
console.log("调整了元素大小");
console.log(size);
};
</script>
场景示例
用户选择图片,然后可以自由的对图片进行裁剪。
vue
<template>
<div id="app">
<h1>照片编辑器</h1>
<div class="controls">
<!-- 文件输入控件,用于选择图像文件 -->
<input type="file" accept="image/*" @change="onFileChange" />
<!-- 裁剪按钮,当没有选择图像时禁用 -->
<button @click="onCropHandle" :disabled="!imageUrl">裁剪</button>
</div>
<!-- 当有图像被选择时显示编辑器容器 -->
<div class="editor-container" v-if="imageUrl">
<div class="image-container">
<!-- 显示用户选择的图像 -->
<img :src="imageUrl" class="photo" alt="用户选择的图像" />
<!-- 裁剪区域 -->
<vue-drag-resize
:key="imageUrl"
v-model="cropArea"
class="crop-area"
:resizable="true"
:draggable="true"
:min-width="50"
:min-height="50"
:parent="true"
:parent-limitation="true"
@resizing="onResizing"
@dragging="onDragging"
/>
</div>
</div>
<!-- 当有裁剪后的图像时显示预览容器 -->
<div class="preview" v-if="croppedImageUrl">
<h2>裁剪后的图片</h2>
<!-- 显示裁剪后的图像 -->
<img :src="croppedImageUrl" alt="裁剪后的图像" />
</div>
</div>
</template>
<script setup>
import { ref, nextTick } from "vue";
import VueDragResize from "vue-drag-resize/src";
const imageUrl = ref(null); // 存储图像的 URL
const photoRef = ref(null); // 图像的引用
const croppedImageUrl = ref(null); // 存储裁剪后的图像的 URL
// 裁剪区域的一些信息:坐标和尺寸
const cropArea = ref({
x: 100,
y: 100,
w: 200,
h: 100,
});
// 用户改变裁剪区域尺寸的时候触发
const onResizing = ({ left, top, width, height }) => {
cropArea.value = { x: left, y: top, w: width, h: height };
};
// 用户拖动裁剪区域的时候触发
const onDragging = ({ left, top }) => {
cropArea.value = { ...cropArea.value, x: left, y: top };
};
// 处理文件选择的事件处理方法
const onFileChange = async (event) => {
// 首先获取用户上传的文件
const file = event.target.files[0];
// 如果有文件,并且文件类型是图像类型
if (file && file.type.startsWith("image/")) {
// 里面主要是要将图片显示出来,这里通过 FileReader 来实现
const reader = new FileReader(); // 创建一个 FileReader 实例对象,用于读取文件
// 当文件读取完成后就会触发 onload 事件
reader.onload = async (e) => {
imageUrl.value = e.target.result; // 读取的文件内容,就是图像的 base64 编码
await nextTick(); // 等待下一次 DOM 更新,因为需要确保图像已经显示出来
photoRef.value = document.querySelector(".photo"); // 获取图像元素
};
reader.readAsDataURL(file); // 读取文件,读取完成后会触发 onload 事件
}
};
// 处理裁剪的事件处理方法
const onCropHandle = () => {
const photo = photoRef.value; // 获取图像元素
console.log(photo, "photo");
if (!photo) return;
// 创建一个新的 image 对象,用于存储裁剪后的图像
const image = new Image();
image.src = imageUrl.value; // 设置图像的 URL
// 该事件会在图像加载完成后触发
image.onload = () => {
// 主要需要做的工作,是将裁剪后的图像绘制到 canvas 上
const canvas = document.createElement("canvas"); // 创建一个 canvas 元素
const ctx = canvas.getContext("2d"); // 获取 canvas 的 2d 上下文对象
// 从裁剪区域获取信息:坐标和尺寸
const { x, y, w, h } = cropArea.value;
// 获取原始图像的尺寸
const imageWidth = image.width;
const imageHeight = image.height;
// 获取显示图像的容器元素
const containerWidth = photo.clientWidth;
const containerHeight = photo.clientHeight;
// 接下来来计算两个宽高比
// 图像的宽高比
const imageAspectRatio = imageWidth / imageHeight;
// 容器元素的宽高比
const containerAspectRatio = containerWidth / containerHeight;
// 计算图像在容器中显示的宽高
let displayWidth, displayHeight;
if (imageAspectRatio > containerAspectRatio) {
// 图像更宽
displayWidth = containerWidth;
displayHeight = containerWidth / imageAspectRatio;
} else {
// 图像更高
displayHeight = containerHeight;
displayWidth = containerHeight * imageAspectRatio;
}
// 接下来需要计算图像在容器中的一个偏移量
const offsetX = (containerWidth - displayWidth) / 2;
const offsetY = (containerHeight - displayHeight) / 2;
// 接下来还需要计算水平和垂直缩放比例
const scaleX = imageWidth / displayWidth;
const scaleY = imageHeight / displayHeight;
// 为什么需要这些数据呢?因为一会儿绘制canvas的时候,需要使用这些数据
// 设置 canvas 画布的宽高
canvas.width = w * scaleX;
canvas.height = h * scaleY;
// 数据准备完毕后,接下来调用 canvas 方法来进行绘制
ctx.drawImage(
image, // 绘制的图像
(x - offsetX) * scaleX, // 裁剪区域的 x 坐标
(y - offsetY) * scaleY, // 裁剪区域的 y 坐标
w * scaleX, // 裁剪区域的宽度
h * scaleY, // 裁剪区域的高度
0, // 绘制到 canvas 的 x 坐标
0, // 绘制到 canvas 的 y 坐标
w * scaleX, // 绘制到 canvas 的宽度
h * scaleY // 绘制到 canvas 的高度
);
croppedImageUrl.value = canvas.toDataURL("image/png"); // 将 canvas 转换为 base64 编码的 URL
};
};
</script>
<style scoped>
#app {
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.controls {
margin-bottom: 20px;
}
.editor-container {
width: 80%;
height: 60vh;
background-color: #fff;
border: 1px solid #ccc;
position: relative;
overflow: hidden;
}
.image-container {
width: 100%;
height: 100%;
position: relative;
}
.photo {
width: 100%;
height: 100%;
object-fit: contain;
}
.crop-area {
border: 2px dashed #fff;
background-color: rgba(133, 130, 130, 0.3);
}
.preview {
margin-top: 20px;
text-align: center;
}
.preview img {
max-width: 100%;
height: auto;
}
</style>
-EOF-