【es6】原生js在页面上画矩形添加选中状态高亮及显示调整大小控制框(三)

server/2024/11/29 20:39:59/

接上篇文章,这篇实现下选中当前元素显示调整大小的控制框,点击document取消元素的选中高亮状态效果。

实现效果

请添加图片描述

代码逻辑

  • 动态生成控制按钮矩形,并设置响应的css
// 动态添加一个调整位置的按钮addScaleBtn(target) {const w = target.offsetWidth;const h = target.offsetHeight;const lt = document.createElement("div");lt.className = "scale-btn";lt.style = `position: absolute;left: -5px;top: -5px;width: 10px;height: 10px;background:red;cursor:resize`;target.appendChild(lt);const lm = document.createElement("div");lm.className = "scale-btn";lm.style = `position: absolute;top: ${(h - 5) / 2}px;left: -5px;width: 10px;height: 10px;background:red;cursor:resize`;target.appendChild(lm);const lb = document.createElement("div");lb.className = "scale-btn";lb.style = `position: absolute;bottom: -5px;left: -5px;width: 10px;height: 10px;background:red;cursor:resize`;target.appendChild(lb);const tm = document.createElement("div");tm.className = "scale-btn";tm.style = `position: absolute;left: ${(w - 5) / 2}px;top: -5px;width: 10px;height: 10px;background:red;cursor:resize`;target.appendChild(tm);const bm = document.createElement("div");bm.className = "scale-btn";bm.style = `position: absolute;left: ${(w - 5) / 2}px;bottom: -5px;width: 10px;height: 10px;background:red;cursor:resize`;target.appendChild(bm);const rt = document.createElement("div");rt.className = "scale-btn";rt.style = `position: absolute;right: -5px;top: -5px;width: 10px;height: 10px;background:red;cursor:resize`;target.appendChild(rt);const rm = document.createElement("div");rm.className = "scale-btn";rm.style = `position: absolute;top: ${(h - 5) / 2}px;right: -5px;width: 10px;height: 10px;background:red;cursor:resize`;target.appendChild(rm);const rb = document.createElement("div");rb.className = "scale-btn";rb.style = `position: absolute;right: -5px;bottom: -5px;width: 10px;height: 10px;background:red;cursor:resize`;target.appendChild(rb);}
  • 点击document恢复样式
resetAllBorderColor(target) {// 改变边框颜色,当前选中的高亮this.allRect.forEach((item) => {item.style.border = "1px solid #ccc";item.querySelectorAll(".scale-btn").forEach((i) => {i.remove();});});}
  • 点击当前元素显示控制框按钮,其他的都隐藏
setCurrentBorderColor(target) {// 改变边框颜色,当前选中的高亮this.allRect.forEach((item) => {if (item != target) {item.style.border = "1px solid #ccc";// 删除8个调整位置的按钮,只保留当前的元素的8个调整位置的按钮item.querySelectorAll(".scale-btn").forEach((i) => {i.remove();});}});target.style.border = "1px solid blue";}
  • 在每次画完矩形和拖动矩形后,都要重置下数据,不然会导致直接画出一个莫名其妙的新矩形
// 画完后重置初始化数据reset() {this.x = 0;this.y = 0;this.disX = 0;this.disY = 0;this.startX = 0;this.startY = 0;}
  • 完整代码
class Draw {constructor() {this.x = 0;this.y = 0;this.disX = 0;this.disY = 0;this.startX = 0;this.startY = 0;this.offsetX = 0;this.offsetY = 0;this.nowMoveTarget = null;this.mouseDown = this.mouseDown.bind(this);this.mouseMove = this.mouseMove.bind(this);this.mouseUp = this.mouseUp.bind(this);this.handleRectMove = this.handleRectMove.bind(this);this.handleRectUp = this.handleRectUp.bind(this);this.zIndex = 0;this.allRect = [];this.shadowBox = document.createElement("div");this.init();}init() {this.draw();}draw() {document.addEventListener("mousedown", this.mouseDown, false);}mouseDown(e) {console.log("🚀 ~ Draw ~ mouseDown ~ e:", e);if (e.target.className == "delete-btn" || e.target.className == "scale-btn")return;// 校验点击的是不是画的的元素if (e.target.className == "draw-rect") {// 改变边框颜色this.changeBorderColor(e.target);this.handleRectDown(e);return false;} else {this.x = e.clientX;this.y = e.clientY;document.addEventListener("mousemove", this.mouseMove);document.addEventListener("mouseup", this.mouseUp);// 清除所有选中的边框颜色,恢复默认边框颜色,及8个选中按钮this.allRect.forEach((item) => {this.resetAllBorderColor(item);});}}mouseMove(e) {// 不要选中文字e.preventDefault();// this.disX = e.clientX - this.x// this.disY = e.clientY - this.y// const startX = e.clientX < this.x ? e.clientX : this.x// const startY = e.clientY < this.y ? e.clientY : this.y// this.disX = e.clientX > this.x ? e.clientX - this.x : this.x - e.clientX// this.disY = e.clientY > this.y ? e.clientY - this.y : this.y - e.clientYthis.startX = Math.min(e.clientX, this.x);this.startY = Math.min(e.clientY, this.y);this.disX = Math.abs(e.clientX - this.x);this.disY = Math.abs(e.clientY - this.y);// console.log('🚀 ~ Draw ~ mouseMove ~ e:', this.disX, this.disY)this.drawShadeRect();}mouseUp(e) {document.removeEventListener("mousemove", this.mouseMove);document.removeEventListener("mouseup", this.mouseUp);this.drawRect();this.shadowBox && this.shadowBox.remove();}drawShadeRect(startX, startY) {this.shadowBox.style = `width: ${this.disX}px;height: ${this.disY}px;border:1px solid red;background:rgba(94,243,243,.5);position: absolute;left: ${this.startX}px;top: ${this.startY}px;z-index:${this.zIndex++}`;document.body.appendChild(this.shadowBox);}drawRect() {if (this.disX < 20 || this.disY < 20) return;const div = document.createElement("div");div.className = "draw-rect";div.style = `position:relative;width: ${this.disX}px;height: ${this.disY}px;border:1px solid #ccc;position: absolute;left: ${this.startX}px;top: ${this.startY}px;z-index:${this.zIndex++};background:greenyellow`;div.appendChild(this.addDeleteBtn());document.body.appendChild(div);this.allRect.push(div);this.setCurrentBorderColor(div);this.reset();}// 画完后重置初始化数据reset() {this.x = 0;this.y = 0;this.disX = 0;this.disY = 0;this.startX = 0;this.startY = 0;}handleRectDown(e) {this.startX = e.clientX;this.startY = e.clientY;this.offsetX = e.clientX - this.nowMoveTarget.offsetLeft;this.offsetY = e.clientY - this.nowMoveTarget.offsetTop;document.addEventListener("mousemove", this.handleRectMove);document.addEventListener("mouseup", this.handleRectUp);}handleRectMove(e) {this.disX = e.clientX - this.offsetX;this.disY = e.clientY - this.offsetY;this.nowMoveTarget.style.left = `${this.disX}px`;this.nowMoveTarget.style.top = `${this.disY}px`;}handleRectUp() {document.removeEventListener("mousemove", this.handleRectMove);document.removeEventListener("mouseup", this.handleRectUp);this.reset();}changeBorderColor(target) {this.nowMoveTarget = target;this.setCurrentBorderColor(target);// 改变鼠标指针target.style.cursor = "move";target.style.zIndex = ++this.zIndex;// 当前元素没有添加8个调整位置的按钮,则添加if (!target.querySelector(".scale-btn")) {this.addScaleBtn(target);}}// 动态添加一个调整位置的按钮addScaleBtn(target) {const w = target.offsetWidth;const h = target.offsetHeight;const lt = document.createElement("div");lt.className = "scale-btn";lt.style = `position: absolute;left: -5px;top: -5px;width: 10px;height: 10px;background:red;cursor:resize`;target.appendChild(lt);const lm = document.createElement("div");lm.className = "scale-btn";lm.style = `position: absolute;top: ${(h - 5) / 2}px;left: -5px;width: 10px;height: 10px;background:red;cursor:resize`;target.appendChild(lm);const lb = document.createElement("div");lb.className = "scale-btn";lb.style = `position: absolute;bottom: -5px;left: -5px;width: 10px;height: 10px;background:red;cursor:resize`;target.appendChild(lb);const tm = document.createElement("div");tm.className = "scale-btn";tm.style = `position: absolute;left: ${(w - 5) / 2}px;top: -5px;width: 10px;height: 10px;background:red;cursor:resize`;target.appendChild(tm);const bm = document.createElement("div");bm.className = "scale-btn";bm.style = `position: absolute;left: ${(w - 5) / 2}px;bottom: -5px;width: 10px;height: 10px;background:red;cursor:resize`;target.appendChild(bm);const rt = document.createElement("div");rt.className = "scale-btn";rt.style = `position: absolute;right: -5px;top: -5px;width: 10px;height: 10px;background:red;cursor:resize`;target.appendChild(rt);const rm = document.createElement("div");rm.className = "scale-btn";rm.style = `position: absolute;top: ${(h - 5) / 2}px;right: -5px;width: 10px;height: 10px;background:red;cursor:resize`;target.appendChild(rm);const rb = document.createElement("div");rb.className = "scale-btn";rb.style = `position: absolute;right: -5px;bottom: -5px;width: 10px;height: 10px;background:red;cursor:resize`;target.appendChild(rb);}// 动态添加一个删除按钮addDeleteBtn() {const btn = document.createElement("button");btn.innerHTML = "删除";btn.className = "delete-btn";btn.style = `position: absolute;right: 0px;bottom: -25px`;// 绑定事件btn.onclick = function () {this.parentElement.remove();};return btn;}setCurrentBorderColor(target) {// 改变边框颜色,当前选中的高亮this.allRect.forEach((item) => {if (item != target) {item.style.border = "1px solid #ccc";// 删除8个调整位置的按钮,只保留当前的元素的8个调整位置的按钮item.querySelectorAll(".scale-btn").forEach((i) => {i.remove();});}});target.style.border = "1px solid blue";}resetAllBorderColor(target) {// 改变边框颜色,当前选中的高亮this.allRect.forEach((item) => {item.style.border = "1px solid #ccc";item.querySelectorAll(".scale-btn").forEach((i) => {i.remove();});});}
}const d = new Draw();
d.init();

总结

  • 要注意控制按钮的显示时机,最好是点击按钮时显示,因为矩形未画完,我们无法获得矩形的宽高,来定位控制矩形的位置
  • 下步实现拖动控制点来调整矩形的大小

http://www.ppmy.cn/server/145991.html

相关文章

Flutter:encrypt插件 AES加密处理

1、pubspec.yaml导入插件 cupertino_icons: ^1.0.8 # 密码加密 encrypt: 5.0.3encrypt封装 import package:encrypt/encrypt.dart; /// 加密类 class EncryptUtil {static final EncryptUtil _instance EncryptUtil._internal();factory EncryptUtil() > _instance;Encrypt…

C#身份证识别接口集成、身份证文字信息提取、身份证信息录入

身份证识别接口为什么会受到互联网平台的青睐&#xff1f;传统的身份验证方式往往需要用户手动输入个人信息&#xff0c;这不仅耗时耗力&#xff0c;还容易出现误操作影响身份认证结果的现象。翔云身份证识别接口通过先进的OCR&#xff08;光学字符识别&#xff09;技术&#x…

使用 Python 的 pdfplumber 库高效解析 PDF 文件

使用 Python 的 pdfplumber 库高效解析 PDF 文件 PDF 文件是日常办公和数据处理中常见的文件格式&#xff0c;而 pdfplumber 是一个专为 PDF 文件解析设计的 Python 库&#xff0c;可以轻松提取文本、表格、图像等内容。本文将介绍 pdfplumber 的基本功能、使用方法&#xff0…

Rust如何编制前端路由

目的&#xff1a;根据前端build文件夹下的目录结构&#xff0c;生成路由&#xff0c;将前端html文件返回。 /// 设置 HTML 文件路由 pub fn route(cfg: &mut web::ServiceConfig) {if Path::new("client/build/index.html").exists() {let index_content match…

阅读《基于蒙特卡洛法的破片打击无人机易损性分析》_笔记

目录 基本信息 1 引言 1.1 主要研究内容 1.2 研究必要性&#xff08;为什么要研究&#xff09; 1.3 该领域研究现状&#xff08;别人做了什么/怎么做的&#xff09; 2 主要研究过程&#xff08;我们做了什么&#xff09; 2.1 建立目标仿真模型 2.2 确定毁伤依据 2.3 无…

Android.mk里如何指定编译模块的输出路径

在 Android.mk 文件中&#xff0c;LOCAL_MODULE_PATH_32、LOCAL_MODULE_PATH_64 和 LOCAL_MODULE_RELATIVE_PATH 可以一起使用&#xff0c;以灵活地控制不同架构模块的安装路径。下面是一个详细的示例&#xff0c;展示如何结合使用这些变量。 示例项目结构 假设你的项目结构如…

怎么样才算得上熟悉高并发编程?

提到并发编程很多人就会头疼了&#xff1b;首先就是一些基础概念&#xff1a;并发&#xff0c;并行&#xff0c;同步&#xff0c;异步&#xff0c;临界区&#xff0c;阻塞&#xff0c;非阻塞还有各种锁全都砸你脸上&#xff0c;随之而来的就是要保证程序运行时关键数据在多线程…

Taro React小程序开发框架 总结

目录 一、安装 二、目录结构 三、创建一个自定义页面 四、路由 1、API 2、传参 3、获取路由参数 4、设置TabBar 五、组件 六、API Taro非常好用的小程序框架&#xff0c;React开发者无缝衔接上。 一、安装 官方文档&#xff1a;Taro 文档 注意&#xff0c;项目创建…