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

发布于:2024-11-27 ⋅ 阅读:(11) ⋅ 点赞:(0)

接上篇文章,这篇实现下选中当前元素显示调整大小的控制框,点击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.clientY
    this.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();

总结

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