Vue 框选区域放大(纯JavaScript实现)

news2024/11/18 19:38:46
需求:长按鼠标左键框选区域,松开后放大该区域,继续框选继续放大,反向框选恢复原始状态
实现思路:根据鼠标的落点,放大要显示的内容(内层盒子),然后利用水平偏移和垂直偏移,让外层展示的窗口(外层盒子)只看到刚刚框选的大概区域,具体代码如下
<template>
  <div>
    <div
      class="selectable_container"
      @mousedown="handleMouseDown"
      @mousemove="handleMouseMove"
      @mouseup="handleMouseUp"
    >
      <div
        class="zoomable_element"
        :style="{
          userSelect: 'none',
          left: innerLeft + 'px',
          top: innerTop + 'px',
          width: innerWidth + 'px',
          height: innerHeight + 'px',
        }"
      >
        <img
          src="./img/test1.jpg"
          style="
            width: 100%;
            height: 100%;
            user-select: none;
            pointer-events: none;
          "
          alt=""
        />
      </div>
      <div class="selectable_element" id="selectable_element"></div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      startX: 0,
      startY: 0,
      endX: 0,
      endY: 0,
      isSelecting: false, //是否正在款选
      closeFlag: false, //是否退出放大状态
      offsetinner_left: 0, //外层容器水平偏移
      offsetinner_top: 0, //外层容器垂直偏移

      outerWidth: 0, //外层盒子宽度
      outerHeight: 0, //外层盒子高度

      zoomRatio: 1,
      innerWidth: "100%", //内层盒子宽度 初始状态等于外层盒子
      innerHeight: "100%", //内层盒子高度
      innerTop: 0, //内层盒子垂直偏移
      innerLeft: 0, //内层盒子水平偏移

      selectionLeft: 0, //框选区域水平偏移
      selectionTop: 0, //框选区域垂直偏移
      selectionWidth: 0, //框选区域宽度
      selectionHeight: 0, //框选区域高度,
    };
  },
  mounted() {
    const dom_mask = window.document.querySelector(".selectable_container");
    const rect_select = dom_mask.getClientRects()[0];
    this.offsetinner_left = rect_select.left; //水平偏移
    this.offsetinner_top = rect_select.top; //垂直偏移
    this.outerWidth = Math.ceil(rect_select.width);
    this.outerHeight = Math.ceil(rect_select.height);
    this.innerWidth = this.outerWidth;
    this.innerHeight = this.outerHeight;
  },
  methods: {
    handleMouseDown(event) {
      if (event.button === 0) {
        // 判断是否为鼠标左键按下
        this.startX = event.clientX - this.offsetinner_left;
        this.startY = event.clientY - this.offsetinner_top;
        this.isSelecting = true;
        var dom = document.getElementById("selectable_element");
        if (dom) {
          dom.style.left = this.startX + "px";
          dom.style.top = this.startY + "px";
        }
      }
    },
    handleMouseMove(event) {
      if (this.isSelecting) {
        this.closeFlag = false;
        this.endX = event.clientX - this.offsetinner_left;
        this.endY = event.clientY - this.offsetinner_top;
        var selectionLeft, selectionTop, selectionWidth, selectionHeight;
        selectionWidth = Math.abs(this.endX - this.startX);
        selectionHeight = Math.abs(this.endY - this.startY);
        // 右下
        if (this.endY >= this.startY && this.endX >= this.startX) {
          selectionLeft = this.startX;
          selectionTop = this.startY;
        }
        // 左下
        else if (this.endY >= this.startY && this.endX <= this.startX) {
          selectionLeft = this.endX;
          selectionTop = this.startY;
        }
        // 右上
        else if (this.endY <= this.startY && this.endX >= this.startX) {
          selectionLeft = this.startX;
          selectionTop = this.endY;
        }
        // 左上
        else if (this.endY <= this.startY && this.endX <= this.startX) {
          selectionLeft = this.endX;
          selectionTop = this.endY;
          this.closeFlag = true;
        }
        selectionLeft = Math.ceil(selectionLeft);
        selectionTop = Math.ceil(selectionTop);
        selectionWidth = Math.ceil(selectionWidth);
        selectionHeight = Math.ceil(selectionHeight);
        var dom = document.getElementById("selectable_element");
        if (dom) {
          dom.style.left = selectionLeft + "px";
          dom.style.top = selectionTop + "px";
          dom.style.width = selectionWidth + "px";
          dom.style.height = selectionHeight + "px";
        }
        this.selectionLeft = 0 - this.innerLeft + selectionLeft;
        this.selectionTop = 0 - this.innerTop + selectionTop;
        this.selectionWidth = selectionWidth;
        this.selectionHeight = selectionHeight;
      }
    },
    handleMouseUp(event) {
      // 判断是否为鼠标左键松开
      if (event.button === 0 && this.isSelecting) {
        // 左上清除
        if (this.closeFlag) {
          this.isSelecting = false;
          this.closeFlag = false;

          var dom = document.getElementById("selectable_element");
          if (dom) {
            dom.style.left = "0px";
            dom.style.top = "0px";
            dom.style.width = "0px";
            dom.style.height = "0px";
          }

          this.innerWidth = this.outerWidth;
          this.innerHeight = this.outerHeight;
          this.innerLeft = 0;
          this.innerTop = 0;

          return;
        }
        this.isSelecting = false;
        this.zoomRatio = Math.min(
          this.outerWidth / this.selectionWidth,
          this.outerHeight / this.selectionHeight
        ).toFixed(2);
        this.zoomRatio = Number(this.zoomRatio);
        // console.log(this.zoomRatio);
        var innerWidth = Math.ceil(this.innerWidth * this.zoomRatio);
        var innerHeight = Math.ceil(this.innerHeight * this.zoomRatio);
        var innerLeft = 0 - this.selectionLeft * this.zoomRatio;
        var innerTop = 0 - this.selectionTop * this.zoomRatio;

        // 居中处理
        innerLeft =
          innerLeft +
          (this.outerWidth - this.selectionWidth * this.zoomRatio) / 2;
        innerTop =
          innerTop +
          (this.outerHeight - this.selectionHeight * this.zoomRatio) / 2;

        // 补位处理
        if (innerWidth + innerLeft < this.outerWidth) {
          // console.log("水平补位");
          innerLeft = innerLeft + this.outerWidth - (innerWidth + innerLeft);
        }
        if (innerHeight + innerTop < this.outerHeight) {
          // console.log("垂直补位");
          innerTop = innerTop + this.innerHeight - (innerHeight + innerTop);
        }

        this.innerWidth = innerWidth;
        this.innerHeight = innerHeight;
        this.innerLeft = innerLeft;
        this.innerTop = innerTop;

        var dom = document.getElementById("selectable_element");
        if (dom) {
          dom.style.left = "0px";
          dom.style.top = "0px";
          dom.style.width = "0px";
          dom.style.height = "0px";
        }
      }
    },
  },
};
</script>
<style lang="scss" scoped>
// 外层可视窗口
.selectable_container {
  position: relative;
  width: 800px;
  height: 450px;
  border: 1px solid #ccc;
  overflow: hidden;
}
// 框选动作临时盒子
.selectable_element {
  position: absolute;
  border: 1px solid red;
}
// 内层内容盒子 需要缩放
.zoomable_element {
  position: absolute;
  left: 0;
  top: 0;
}
</style>

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1720954.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

25. 悲观锁 和 乐观锁

文章目录 悲观锁 和 乐观锁1.基于CAS实现乐观锁2.自旋锁2.1.不可重入自旋锁2.2.可重入自旋锁2.3.CLH自旋锁 悲观锁 和 乐观锁 Java中的synchronized就是悲观锁的一个实现&#xff0c;悲观锁可以确保无论哪个线程持有锁&#xff0c;都能独占式的访问临界区代码&#xff0c;虽然悲…

Linux基本命令的使用(ls cd touch)

一、Windows系统常见的文件类型 • 文本文件格式&#xff1a;txt、doc、pdf、html等。 • 图像文件格式&#xff1a;jpg、png、bmp、gif等。 • 音频文件格式&#xff1a;mp3、wav、wma等。 • 视频文件格式&#xff1a;mp4、avi、wmv、mov等。 • 压缩文件格式&#xff1a;zip…

连通块中点的数量-java

本次我们通过连通块中点的数量来加深我们对并查集的基本操作和原理&#xff0c;并且知道如何在并查集中添加附属信息。 目录 前言☀ 一、连通块中点的数量☀ 二、算法思路☀ 1.无向图&#x1f319; 2.在a b之间连一条边&#xff0c;a b可能相等&#x1f319; 3.询问a和b是否在一…

sudo命令的隐患-要注意安全使用!!严格管理!!严格控制

前言 众所周知&#xff0c;sudo命令非常方便&#xff0c;而且有一定的优点。比如不需要知道root密码就可以执行一些root的命令。相比于su 必须知道root密码来说&#xff0c;减少了root密码泄露的风险。 但是sudo也是一把非常锋利的双刃剑&#xff0c;需要加以限制&#xff0c;…

重庆人文科技学院建立“软件安全产学研基地”,推动西南地区软件安全发展

5月29日&#xff0c;重庆人文科技学院与开源网安签订了《产学研校企合作协议》&#xff0c;并举行了“重庆人文科技学院产学研基地”授牌仪式&#xff0c;此次合作不仅深化了双方在软件安全领域的产学研紧密联结&#xff0c;更是对川渝乃至西南地区软件供应链安全发展起到重要的…

微信小程序 npm构建+vant-weaap安装

微信小程序&#xff1a;工具-npm构建 报错 解决&#xff1a; 1、新建miniprogram文件后&#xff0c;直接进入到miniprogram目录&#xff0c;再次执行下面两个命令&#xff0c;然后再构建npm成功 npm init -y npm install express&#xff08;Node js后端Express开发&#xff…

【mysql】ssl_choose_client_version:unsupported protocol

起因&#xff1a;项目上的DolphinScheduler连接不上数据库&#xff0c;查看worker日志提到SSL协议问题&#xff1a; com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failureCaused by: java.io.EOFException: SSL peer shut down incorrectly 我…

KMPlayer v2024.4.25.13 官方版 (万能播放器)

前言 KMPlaye通过各种插件扩展KMP可以支持层出不穷的新格式。KMPlaye强大的插件功能&#xff0c;直接从Winamp继承的插件功能&#xff0c;能够直接使用Winamp的音频&#xff0c;输入&#xff0c;视觉效果插件&#xff0c;而通过独有的扩展能力&#xff0c;只要你喜欢&#xff…

工厂条码仓库管理系统是做什么的?

工厂条码仓库管理系统&#xff0c;可以分为两个概念&#xff1a;一个是仓库管理系统、一个是工厂条码。 在了解仓库管理和工厂条码之前&#xff0c;题主先了解一下企业的信息化建设&#xff1a; 企业信息化建设是企业提升生产效率、优化管理的重要手段。企业实现生产流程的数…

UE4 使用自带的插件制作音频可视化

1.插件默认为开启 2.新建共感NRT&#xff0c;选择要使用的音频 3.添加音频组件&#xff0c;添加共感NRT变量&#xff0c;选择新建的共感NRT对象 4.编写蓝图

从零到一建设数据中台 - 关键技术汇总

一、数据中台关键技术汇总 语言框架&#xff1a;Java、Maven、Spring Boot 数据分布式采集&#xff1a;Flume、Sqoop、kettle 数据分布式存储&#xff1a;Hadoop HDFS 离线批处理计算&#xff1a;MapReduce、Spark、Flink 实时流式计算&#xff1a;Storm/Spark Streaming、…

算法每日一题(python,2024.06.01)

题目来源&#xff1a;&#xff08;力扣. - 力扣&#xff08;LeetCode&#xff09;&#xff0c;简单&#xff09; 解题思路&#xff1a; 注意题目说的俩个是异位&#xff0c;所以需要加上俩个是否完全相等的判断 法一&#xff1b;排序比对法 如果俩个字符串中每个字符出现的次…

TiDB-从0到1-部署篇

TiDB从0到1系列 TiDB-从0到1-体系结构TiDB-从0到1-分布式存储TiDB-从0到1-分布式事务TiDB-从0到1-MVCCTiDB-从0到1-部署篇 一、TiUP TiUP是TiDB4.0版本引入的集群运维工具&#xff0c;通过TiUP可以进行TiDB的日常运维工作&#xff0c;包括部署、启动、关闭、销毁、弹性扩缩容…

网络安全基础技术扫盲篇 — 名词解释之“数据包“

用通俗易懂的话说&#xff1a; 数据包就像是一个信封。当你写信给某个人时&#xff0c;你将内容写在一张纸上&#xff0c;然后将纸叠起来并放入信封中&#xff0c;就形成了一个完整要发送的数据内容。信封上有发件人和收件人的详细地址&#xff0c;还有一些其他必要的信息&…

交易员摩拳擦掌,就在今年夏天,极端气候引爆商品?

有史以来最严重的高温炙烤下&#xff0c;从农业到能源到航运都可能受到严重负面影响&#xff0c;大宗商品市场波动将大幅加剧。 2024年有望成为有史以来最炎热的一年&#xff0c;随着北半球步入夏季&#xff0c;世界各地都将遭受由全球变暖造成的极端高温困扰。极端天气不仅给民…

WalleWeb简化你的DevOps部署流程

walle-web&#xff1a;简化部署流程&#xff0c;提升开发效率&#xff0c;Walle Web让DevOps触手可及 - 精选真开源&#xff0c;释放新价值。 概览 Walle Web是一个功能强大且免费开源的DevOps平台&#xff0c;旨在简化和自动化代码部署流程。它支持多种编程语言&#xff0c;包…

Python魔法之旅-魔法方法(07)

目录 一、概述 1、定义 2、作用 二、应用场景 1、构造和析构 2、操作符重载 3、字符串和表示 4、容器管理 5、可调用对象 6、上下文管理 7、属性访问和描述符 8、迭代器和生成器 9、数值类型 10、复制和序列化 11、自定义元类行为 12、自定义类行为 13、类型检…

自动控制系统

文章目录 1234 1 最后是一个水位控制的 &#xff08;感觉最好&#xff09; 2 阻力增大了 就不够了跟古代行军打仗似的感觉设计的话 要考虑的更多 3 4 刚开始转速 为0这调压电路是 因为是直流有刷电机 代码部分 主要看main 其他和测速例程一样 这调电压 好像是和功率相关pfv 负…

Three.js 性能监测工具 Stats.js

目录 前言 性能监控 引入 Stats 使用Stats 代码 前言 通过stats.js库可以查看three.js当前的渲染性能&#xff0c;具体说就是计算three.js的渲染帧率(FPS),所谓渲染帧率(FPS)&#xff0c;简单说就是three.js每秒钟完成的渲染次数&#xff0c;一般渲染达到每秒钟60次为…

AI视频教程下载:给数据分析师的生成式AI课

生成式人工智能知识现已成为数据科学的一项基本技能。根据 Gartner 的数据&#xff0c;"到 2026 年&#xff0c;20% 的顶级数据科学团队将改名为认知科学或科学咨询公司&#xff0c;员工技能的多样性将增加 800%"。 考虑到这一行业趋势&#xff0c;IBM 为您带来了这…