当前位置: 首页 > news >正文

基于 Vue 的拖拽缩放卡片组件:实现思路、方法及使用指南

引言

在前端开发中,实现可交互的组件能够极大地提升用户体验。本文将介绍一个基于 Vue 封装的可缩放卡片组件,从实现思路、代码具体实现以及使用方法等方面进行详细阐述,帮助开发者更好地理解和运用这一组件。项目源码地址:https://gitcode.com/Jiaberrr/vue3-pc-template

实现思路

  1. 定位与布局:通过position: absolute对卡片进行定位,利用lefttoprightbottom属性确定其在页面中的位置,同时设置widthheight来定义卡片的初始大小。
  2. 缩放控制点:在卡片的四个角(左上角、右上角、左下角、右下角)添加可交互的缩放控制点,通过监听这些控制点的鼠标事件(mousedownmousemovemouseup)来实现卡片的缩放功能。
  3. 状态跟踪:使用变量来记录卡片的初始大小、位置以及鼠标的初始位置,在缩放过程中根据鼠标的移动距离计算卡片新的大小和位置。

代码实现

模板部分(template)

<template><div class="absolute" :id="idName" :style="{width: width,height: height,top: top + 'px',left: left + 'px',right: right + 'px',bottom: bottom + 'px'}"><slot></slot><div class="resize-handle-tl" :class="'resize-handle'+ idName"></div><div class="resize-handle-tr" :class="'resize-handle'+ idName"></div><div class="resize-handle-bl" :class="'resize-handle'+ idName"></div><div class="resize-handle-br" :class="'resize-handle'+ idName"></div></div>  
</template>

在模板中,外层div通过idstyle绑定来设置卡片的位置和大小。slot用于插入卡片的内容,四个角的div分别代表缩放控制点,通过动态绑定类名来标识不同的控制点。

script 部分(script setup)

import { onMounted } from "vue";const porp = defineProps({idName: {Type: String,required: true},width: {type: [Number, String],default: "100%", // 默认宽度},height: {type: [Number, String],default: "100%", // 默认高度},top: {type: Number,default: null,},left: {type: Number,default: null,},bottom: {type: Number,default: null,},right: {type: Number,default: null,}
})let originalWidth = 0;
let originalHeight = 0;
let originalX = 0;
let originalY = 0;
let originalMouseX = 0;
let originalMouseY = 0;
let resizableBox = null;
let resizeHandle = [];
let resizeType = "";onMounted(() => {resizableBox = document.getElementById(porp.idName);resizeHandle = document.querySelectorAll(".resize-handle"+ porp.idName);resizeHandle.forEach((handle) => {handle.addEventListener("mousedown", function (e) {e.preventDefault();originalWidth = parseFloat(getComputedStyle(resizableBox).width);originalHeight = parseFloat(getComputedStyle(resizableBox).height);originalMouseX = e.clientX;originalMouseY = e.clientY;resizeType = this.className;window.addEventListener("mousemove", resize);window.addEventListener("mouseup", stopResize);});});
});
let firstLeft = porp.left;
let firstTop = porp.top;
let firstBottom = porp.bottom;
let firstRight = porp.right
let lastTop = 0;
let lastLeft = 0;
let lastBottom = 0;
let lastRight = 0;
const resize = (e) => {const deltaX = e.clientX - originalMouseX;const deltaY = e.clientY - originalMouseY;resizableBox = document.getElementById(porp.idName);if (resizeType.includes("resize-handle-tl")) {if (resizableBox.style.left) {resizableBox.style.left = `${originalX + deltaX + lastLeft + firstLeft}px`;resizableBox.style.top = `${originalY + deltaY + lastTop + firstTop}px`;}resizableBox.style.width = `${originalWidth - deltaX}px`;resizableBox.style.height = `${originalHeight - deltaY}px`;} else if (resizeType.includes("resize-handle-tr")) {if(resizableBox.style.top) {resizableBox.style.top = `${originalY + deltaY + firstTop + lastTop}px`;}else {resizableBox.style.right = `${ originalX - deltaX + firstRight -lastRight}px`;}resizableBox.style.width = `${originalWidth + deltaX}px`;resizableBox.style.height = `${originalHeight - deltaY}px`;} else if (resizeType.includes("resize-handle-bl")) {if( resizableBox.style.left) {resizableBox.style.left = `${originalX + deltaX + firstLeft + lastLeft}px`;}else {resizableBox.style.bottom = `${originalY - deltaY + firstBottom - lastBottom}px`;}resizableBox.style.width = `${originalWidth - deltaX}px`;resizableBox.style.height = `${originalHeight + deltaY}px`;} else if (resizeType.includes("resize-handle-br")) {if(resizableBox.style.right) {resizableBox.style.right = `${ originalX - deltaX + firstRight -lastRight}px`;resizableBox.style.bottom = `${originalY - deltaY + firstBottom - lastBottom}px`;}resizableBox.style.width = `${originalWidth + deltaX}px`;resizableBox.style.height = `${originalHeight + deltaY}px`;}
};const stopResize = (e) => {if(e.target.classList.contains('resize-handle-tl')) {lastTop += e.pageY - originalMouseY;lastLeft += e.pageX - originalMouseX;}else if(e.target.classList.contains('resize-handle-tr')) {lastTop += e.pageY - originalMouseY;lastRight += e.pageX - originalMouseX;}else if(e.target.classList.contains('resize-handle-bl')) {lastLeft += e.pageX - originalMouseX;lastBottom += e.pageY - originalMouseY}else if(e.target.classList.contains('resize-handle-br')) {lastBottom += e.pageY - originalMouseYlastRight += e.pageX - originalMouseX;}window.removeEventListener("mousemove", resize);window.removeEventListener("mouseup", stopResize);
};
  1. 属性定义:通过defineProps定义组件接受的属性,包括idName(必选,用于唯一标识卡片)、widthheighttopleftbottomright,并设置了默认值。
  2. 变量初始化:声明了一系列变量用于跟踪卡片的初始状态和缩放过程中的状态。
  3. 生命周期钩子:在onMounted钩子函数中,获取卡片元素和缩放控制点元素,并为每个缩放控制点添加mousedown事件监听器。当鼠标按下时,记录卡片的初始大小和鼠标位置,同时添加mousemovemouseup事件监听器。
  4. 缩放函数resize函数根据鼠标移动的距离和缩放控制点的类型来计算并更新卡片的大小和位置。
  5. 停止缩放函数stopResize函数在鼠标松开时,移除mousemovemouseup事件监听器,并更新卡片位置的累计偏移量。

样式部分(style scoped)

.resize-handle-br {width: 10px;height: 10px;position: absolute;bottom: 0;right: 0;cursor: se-resize;
}
.resize-handle-bl {width: 10px;height: 10px;position: absolute;bottom: 0;left: 0;cursor: sw-resize;
}
.resize-handle-tl {width: 10px;height: 10px;position: absolute;top: 0;left: 0;cursor: nw-resize;
}
.resize-handle-tr {width: 10px;height: 10px;position: absolute;top: 0;right: 0;cursor: ne-resize;
}

样式部分定义了四个缩放控制点的大小、位置和鼠标悬停时的光标样式。

使用方法

在 Vue 项目中使用该组件,首先确保组件已正确引入和注册。例如,在父组件的模板中:

<template><div id="app"><ScalableCardidName="myCard"width="300px"height="200px"top="100"left="100"><p>这是卡片的内容</p></ScalableCard></div>
</template><script setup>
import ScalableCard from './components/ScalableCard.vue';
</script>

在上述示例中,通过传入idNamewidthheighttopleft等属性来定制卡片的初始状态,并在组件内部插入卡片内容。

总结

通过上述的实现思路、代码实现和使用方法介绍,我们可以看到这个基于 Vue 的可缩放卡片组件为前端开发中实现可交互的卡片功能提供了一个有效的解决方案。你也可以根据实际需求进一步扩展和优化该组件,以满足不同项目的需求。希望本文能对大家有所帮助。

相关文章:

基于 Vue 的拖拽缩放卡片组件:实现思路、方法及使用指南

引言 在前端开发中&#xff0c;实现可交互的组件能够极大地提升用户体验。本文将介绍一个基于 Vue 封装的可缩放卡片组件&#xff0c;从实现思路、代码具体实现以及使用方法等方面进行详细阐述&#xff0c;帮助开发者更好地理解和运用这一组件。项目源码地址&#xff1a;https…...

nginx 实现 正向代理、反向代理 、SSL(证书配置)、负载均衡 、虚拟域名 ,使用其他中间件监控

我们可以详细地配置 Nginx 来实现正向代理、反向代理、SSL、负载均衡和虚拟域名。同时&#xff0c;我会介绍如何使用一些中间件来监控 Nginx 的状态和性能。 1. 安装 Nginx 如果你还没有安装 Nginx&#xff0c;可以通过以下命令进行安装&#xff08;以 Ubuntu 为例&#xff0…...

Kafka客户端-“远程主机强迫关闭了一个现有的连接”故障排查及解决

Kafka客户端-“远程主机强迫关闭了一个现有的连接”故障排查及解决 1. 故障现象 Kafka客户端发送数据时&#xff0c;出现“远程主机强迫关闭了一个现有的连接”错误&#xff0c;导致数据发送失败。错误信息如下&#xff1a; 2. 故障排查 【1】. 查看服务网络状态 出现故障…...

Node.js - Express框架

1. 介绍 Express 是一个基于 Node.js 的 Web 应用程序框架&#xff0c;主要用于快速、简便地构建 Web 应用程序 和 API。它是目前最流行的 Node.js Web 框架之一&#xff0c;具有轻量级、灵活和功能丰富的特点。 核心概念包括路由&#xff0c;中间件&#xff0c;请求与响应&a…...

AWS Lambda

AWS Lambda 是 Amazon Web Services&#xff08;AWS&#xff09;提供的无服务器计算服务&#xff0c;它让开发者能够运行代码而不需要管理服务器或基础设施。AWS Lambda 会自动处理代码的执行、扩展和计费&#xff0c;开发者只需关注编写和部署代码&#xff0c;而无需担心底层硬…...

mysql 如何快速删除表数据

在数据库管理中, 经常会遇到需要删除大量数据的情况. 对于 MySQL 数据库而言, 如何高效快速地删除数据是一个值得深入探讨的问题. 本文将详细介绍几种在 MySQL 中快速删除数据的方法及相关注意事项. delete 语句 delete 语句可以删除符合条件的指定数据, 但是在删除大量数据…...

物联网网关Web服务器--lighttpd服务器部署与应用测试

以下是在国产ARM处理器E2000飞腾派开发板上部署 lighttpd 并进行 CGI 应用开发的步骤&#xff1a; 1、lighttpd简介 Lighttpd 是一款轻量级的开源 Web 服务器软件&#xff0c;具有以下特点和功能&#xff1a; 特点 轻量级&#xff1a;Lighttpd 在设计上注重轻量级和高效性&a…...

vmware虚拟机配置ubuntu 18.04(20.04)静态IP地址

VMware版本 &#xff1a;VMware Workstation 17 Pro ubuntu版本&#xff1a;ubuntu-18.04.4-desktop-amd64 主机环境 win11 1. 修改 VMware虚拟网络编辑器 打开vmware&#xff0c;点击顶部的“编辑"菜单&#xff0c;打开 ”虚拟化网络编辑器“ 。 选择更改设置&#…...

智能家居篇 一、Win10 VM虚拟机安装 Home Assistant 手把手教学

智能家居篇 一、Win10 VM虚拟机安装 Home Assistant 手把手教学 文章目录 [智能家居篇]( )一、Win10 VM虚拟机安装 Home Assistant 手把手教学 前言一.下载Vm版本的HomeAsistant安装包 二.打开Vmware选择新建虚拟机1.选择自定义高级2.选择16.x及以上3.选择稍后安装4.根据官网的…...

Flutter插件制作、本地/远程依赖及缓存机制深入剖析(原创-附源码)

Flutter插件在开发Flutter项目的过程中扮演着重要的角色&#xff0c;我们从 ​​​​​​https://pub.dev 上下载添加到项目中的第三方库都是以包或者插件的形式引入到代码中的&#xff0c;这些第三方工具极大的提高了开发效率。 深入的了解插件的制作、发布、工作原理和缓存机…...

Python猜数小游戏

Python 实现的《猜数游戏》 介绍 本文将展示如何使用 Python 编写一个简单的《猜数游戏》。这个游戏将会生成一个1到10之间的随机数&#xff0c;用户有最多三次机会来猜测正确的数字。如果用户猜对了&#xff0c;游戏将结束并显示恭喜信息&#xff1b;如果没有猜对&#xff0…...

--- 用java实现一个计时器 ---

这里的计时器值得是当线程设定的时间过了之后&#xff0c;自动执行该线程的工作 设计 MyTimer 既然是要在指定的时间之后执行任务&#xff0c;那么传入的参数就应该有run方法&#xff08;需要执行的任务&#xff09;&#xff0c;time&#xff08;在多少时间之后执行&#xff…...

OPI4A,目标检测,口罩检测,mnn,YoloX

记得之前&#xff0c;使用了bubbling导师复现的python版yolox&#xff0c;训练了自建的口罩数据集&#xff0c;得到了h5文件&#xff0c;又转换成pb文件&#xff0c;再使用阿里巴巴的MNN&#xff0c;使用它的MNNConvert&#xff0c;转换成mnn文件 最终实现了&#xff0c;在树莓…...

C#与Vue2上传下载Excel文件

1、上传文件流程&#xff1a;先上传文件&#xff0c;上传成功&#xff0c;返回文件名与url&#xff0c;然后再次发起请求保存文件名和url到数据库 前端Vue2代码&#xff1a; 使用element的el-upload组件&#xff0c;action值为后端接收文件接口&#xff0c;headers携带session信…...

Linux(Centos7)安装Mysql/Redis/MinIO

安装Mysql 安装Redis 搜索Redis最先版本所在的在线安装yum库 查看以上两个组件是否是开机自启 安装MinIO 开源的对象存储服务&#xff0c;存储非结构化数据&#xff0c;兼容亚马逊S3协议。 minio --help #查询命令帮助minio --server --help #查询--server帮助minio serve…...

港科夜闻 | 香港科大与微软亚洲研究院签署战略合作备忘录,推动医学健康教育及科研协作...

关注并星标 每周阅读港科夜闻 建立新视野 开启新思维 1、香港科大与微软亚洲研究院签署战略合作备忘录&#xff0c;推动医学健康教育及科研协作。根据备忘录&#xff0c;双方将结合各自于科研领域的优势&#xff0c;携手推动医学健康领域的交流与合作。合作方向将涵盖人才培训、…...

森林网络部署,工业4G路由器实现林区组网远程监控

在广袤无垠的林区&#xff0c;每一片树叶的摇曳、每一丝空气的流动&#xff0c;都关乎着生态的平衡与安宁。林区监控正以强大的力量&#xff0c;为这片绿色家园筑起一道坚固的防线。 工业 4G 路由器作为林区监控组网的守护者&#xff0c;凭借着卓越的通讯性能&#xff0c;突破…...

ASP.NET Core - 配置系统之自定义配置提供程序

ASP.NET Core - 配置系统之自定义配置提供程序 4. 自定义配置提供程序IConfigurationSourceIConfigurationProvider 4. 自定义配置提供程序 在 .NET Core 配置系统中封装一个配置提供程序关键在于提供相应的 IconfigurationSource 实现和 IConfigurationProvider 接口实现&…...

npm、yarn、pnpm包安装器差异性对比

特性npmyarnpnpm发布年份2010 年发布2016 年发布2017 年发布安装速度较慢&#xff08;旧版本&#xff09;&#xff0c;但自 npm 5 后有所改善较快&#xff0c;尤其是在缓存方面极快&#xff0c;使用硬链接和全局缓存来提高速度包管理模式扁平化依赖&#xff0c;可能会发生重复依…...

正点原子repo放到自己的git服务器

atk-rk3568_android11 导出project-objects对应仓库 .repo/repo/repo list -n > project-object.txt将project-object.txt格式化&#xff0c;并通过gitolite.conf创建对应仓库 atk-rk3568_android11_repo atk-rk3568_android11/RKTools atk-rk3568_android11_repo atk-…...

Git-RSCLIP入门到精通:从基础地物识别到复杂场景分析全流程解析

Git-RSCLIP入门到精通&#xff1a;从基础地物识别到复杂场景分析全流程解析 1. 遥感智能分析的新利器 在遥感图像分析领域&#xff0c;传统方法往往需要大量标注数据和复杂的模型训练流程。Git-RSCLIP的出现彻底改变了这一局面&#xff0c;它基于先进的SigLIP架构&#xff0c…...

河海大学材料科学与工程及材料与化工专业考研复试资料(含《材料分析方法》笔试专项)

温馨提示&#xff1a;文末有联系方式河海大学材料类考研复试资料全面升级 本套资料专为报考河海大学材料科学与工程、材料与化工两个硕士专业的考生设计&#xff0c;聚焦复试核心笔试科目——《材料分析方法》&#xff0c;助力精准高效备考。由2025届一志愿录取考生权威整理 所…...

当知识有了‘关系网‘:LightRAG如何让大模型‘秒懂‘你的文档?

想象一下&#xff0c;你有一座藏书万卷的图书馆&#xff0c;但你找书的方式只有一种——记住每本书某个页面的关键词&#xff0c;然后靠"猜"来定位。 这&#xff0c;就是传统RAG系统的尴尬处境。 今天要介绍的这个开源项目LightRAG&#xff0c;被顶会EMNLP 2025接收…...

工业以太网双雄:从协议原理到选型落地,EtherCAT与PROFINET实战解析

1. 工业以太网的双雄之争&#xff1a;为什么选型这么难&#xff1f; 第一次接触工业以太网协议选型时&#xff0c;我盯着EtherCAT和PROFINET的参数表发呆了整整一上午。就像面对两个各有所长的武林高手&#xff0c;一个轻功了得&#xff0c;一个内力深厚&#xff0c;实在难以抉…...

华为交换机等保2.0实战:手把手配置身份鉴别,从密码策略到登录超时

华为交换机等保2.0身份鉴别全流程配置指南 当企业网络面临等保2.0合规检查时&#xff0c;身份鉴别环节往往是整改重点。作为网络安全工程师&#xff0c;我曾协助多家企业通过等保测评&#xff0c;发现华为交换机的身份鉴别配置存在不少易忽略的细节。本文将分享一套经过实战验证…...

从仿真到实战:在CST/HFSS中如何设置周期性边界条件评估紧耦合天线阵元性能

从仿真到实战&#xff1a;在CST/HFSS中设置周期性边界条件评估紧耦合天线阵元性能 天线阵列设计中最具挑战性的环节之一&#xff0c;是如何准确预测单个阵元在阵列环境中的真实工作状态。当我在设计第一个超宽带相控阵时&#xff0c;曾因忽视阵元间互耦效应导致实物测试结果与仿…...

云计算算力价格波动:行业重构与竞争新格局

云计算价格反转&#xff1a;从价格战到集体涨价2025年4月&#xff0c;阿里云率先发起价格战&#xff0c;京东云、腾讯云、华为云等纷纷跟进&#xff0c;“最高降幅达60%”的口号让行业陷入价格混战。然而&#xff0c;到了2026年3月&#xff0c;市场风向突变&#xff0c;谷歌云、…...

AI爱好者成长手册:从“会用”到“用好”主流大模型的进阶之路

许多AI爱好者在使用大模型一段时间后会陷入一个困惑&#xff1a;为什么别人能用AI写出惊艳的文案、完成复杂的分析&#xff0c;而自己却总是得到平庸的结果&#xff1f;答案往往不在于模型的选择&#xff0c;而在于使用方式。经过在RskAi&#xff08;www.rsk.cn&#xff09; 平…...

新手福音:借力卓晴式AI,在快马平台轻松完成你的首个网页项目

作为一个刚接触编程的新手&#xff0c;想要创建个人网页却不知从何下手是很常见的情况。最近我发现了一个特别适合新手的组合方案&#xff1a;用AI生成代码在线平台实时调试。下面记录我的完整实践过程&#xff0c;希望能帮到同样想入门的朋友。 明确需求清单 首先梳理出网页需…...

苏州沃虎电子(VOOHU)10/100M集成式RJ45连接器SYT111B002BA2A1D产品介绍

苏州沃虎电子科技有限公司&#xff08;品牌&#xff1a;VOOHU&#xff09;供应的 SYT111B002BA2A1D 是一款高可靠性集成式RJ45连接器&#xff0c;专为10/100M以太网应用设计。该产品采用90侧插DIP封装&#xff0c;内置网络变压器&#xff0c;具备工业级宽温工作能力和优异的信号…...