【sgTileImage】自定义组件:瓦片图拖拽局部加载、实现以鼠标为中心缩放

news2024/11/25 12:28:43

 

特性:

  1. 支持缩放瓦片图,定义瓦片图初始缩放比例,以鼠标所在位置为中心缩放
  2. 支持局部拖拽加载

sgTileImage源码 

<template>
    <div :class="$options.name">
        <div class="sg-ctrl">
            <label>缩放百分比</label>
            <el-input-number style="width: 150px;" v-model.trim="scaleValue" :precision="0" :step="10" :min="10" :max="100"
                :controls-position="`left`" @change="changeScaleValue" />
        </div>
        <div class="sg-tile-img" ref="scrollContainer">
            <div ref="dragContainer" :style="{ width: `${tileSize * colCount}px` }">
                <img v-for="(a, i) in tiles" :key="i" :ref="`tile${i}`" :loaded="a.loaded" :width="tileSize"
                    :height="tileSize" draggable="false">
            </div>
        </div>

        <sgDragMoveTile :data="dragMoveTileData" @dragMove="dragMove" />
    </div>
</template>
<script>
import sgDragMoveTile from "@/vue/components/admin/sgDragMoveTile";
export default {
    name: 'sgTileImage',
    components: {
        sgDragMoveTile
    },
    data() {
        return {
            dragMoveTileData: {},
            scaleValue: 100,
            orginTileSize: 0,
            tileSize: 0,
            colCount: 0,
            rowCount: 0,
            tiles: [],//瓦片图数组
            mousePoint_bk: null,
        }
    },
    props: [
        "data",
        /* data格式:{
            orginTileSize:500,//瓦片图原始尺寸
            colCount: 20,//行数
            rowCount: 20,//列数
            scaleValue: 100,//缩放百分比
            tiles: [],//瓦片图数组
        } */
    ],
    watch: {
        data: {
            handler(newValue, oldValue) {
                if (newValue && Object.keys(newValue).length) {
                    newValue.orginTileSize && (this.orginTileSize = newValue.orginTileSize);
                    newValue.colCount && (this.colCount = newValue.colCount);
                    newValue.rowCount && (this.rowCount = newValue.rowCount);
                    newValue.scaleValue && (this.scaleValue = newValue.scaleValue);
                    newValue.tiles && (this.tiles = newValue.tiles);
                    this.$nextTick(() => { this.loadScreenViewTiles(); });
                }
            },
            deep: true,//深度监听
            immediate: true,//立即执行
        },
        scaleValue: {
            handler(newValue, oldValue) {
                this.tileSize = this.orginTileSize * newValue / 100;
                this.$nextTick(() => { this.loadScreenViewTiles(); });
            },
            deep: true,//深度监听
            immediate: true,//立即执行
        },
    },
    destroyed() {
        removeEventListener('mousewheel', this.mousewheel);
    },
    mounted() {
        this.dragMoveTileData = {
            scrollContainer: this.$refs.scrollContainer,
            dragContainer: this.$refs.dragContainer,
        }
        let rect_scrollContainer = this.$refs.scrollContainer.getBoundingClientRect();
        this.mousePoint_bk = {
            x: rect_scrollContainer.width / 2,
            y: rect_scrollContainer.height / 2,
        };
        setTimeout(() => {
            this.loadScreenViewTiles();
            addEventListener('mousewheel', this.mousewheel, { passive: false });
        }, 1000);
    },
    methods: {
        // 校正放大缩小后,瓦片图的坐标(目的是为了让缩放看起来是以鼠标坐标为中心点)
        centerPosition(e, {
            rect_dragContainer_orign,
            scrollLeft_orgin,
            scrollTop_orgin,
            mousePoint }) {
            let scrollContainer = this.$refs.scrollContainer;
            let dragContainer = this.$refs.dragContainer;
            let rect_dragContainer = dragContainer.getBoundingClientRect();
            let scale = rect_dragContainer.width / rect_dragContainer_orign.width;//缩放比例
            let mouse_left_dis_orgin = scrollLeft_orgin + mousePoint.x;//缩放前鼠标距离瓦片图最左侧的距离
            let mouse_top_dis_orgin = scrollTop_orgin + mousePoint.y;//缩放前鼠标距离瓦片图最顶部的距离
            let mouse_left_dis = mouse_left_dis_orgin * scale;//缩放后鼠标距离瓦片图最左侧的距离
            let mouse_top_dis = mouse_top_dis_orgin * scale;//缩放后鼠标距离瓦片图最顶部的距离
            let scrollLeft = mouse_left_dis - mousePoint.x;
            let scrollTop = mouse_top_dis - mousePoint.y;
            scrollContainer.scrollLeft = scrollLeft;
            scrollContainer.scrollTop = scrollTop;
            this.mousePoint_bk = mousePoint;
        },
        mousewheel(e) {
            // 记录缩放前的数据
            let rect_dragContainer_orign = this.$refs.dragContainer.getBoundingClientRect();
            let scrollContainer = this.$refs.scrollContainer;
            let mousePoint = { x: e.x, y: e.y };
            let scrollLeft_orgin = scrollContainer.scrollLeft;
            let scrollTop_orgin = scrollContainer.scrollTop;
            // 开始缩放
            e.deltaY < 0 && (this.scaleValue += 10);
            e.deltaY > 0 && (this.scaleValue -= 10);
            // 开始计算坐标
            this.$nextTick(() => {
                this.centerPosition(e, {
                    rect_dragContainer_orign,
                    scrollLeft_orgin,
                    scrollTop_orgin,
                    mousePoint
                })
            });
            e.preventDefault && e.preventDefault();//阻止默认的滚轮事件
            return false;
        },
        // 获取浏览器可视范围的瓦片图,并加载图片
        loadScreenViewTiles(d) {
            let scrollContainer = this.$refs.scrollContainer;
            if (scrollContainer) {
                let rect_scrollContainer = scrollContainer.getBoundingClientRect();
                this.tiles.forEach((v, i) => {
                    let tile = this.$refs[`tile${i}`];
                    if (tile) {
                        tile = tile[0];
                        let rectTile = tile.getBoundingClientRect();
                        if (
                            rectTile.x + rectTile.width > rect_scrollContainer.x - rectTile.width &&
                            rectTile.y + rectTile.height > rect_scrollContainer.y - rectTile.height &&
                            rectTile.x < rect_scrollContainer.x + rect_scrollContainer.width &&
                            rectTile.y < rect_scrollContainer.y + rect_scrollContainer.height
                        ) {
                            tile.onload = d => { v.loaded = true; };
                            tile.src = v.img;
                        }
                    }
                });
            }
        },
        dragMove(d) {
            this.loadScreenViewTiles();
        },
        changeScaleValue(d) {
            this.mousewheel(this.mousePoint_bk);
        },
    },
};
</script>
<style lang="scss" scoped> .sgTileImage {
     .sg-ctrl {
         position: absolute;
         left: 10px;
         top: 10px;
         z-index: 1;
         box-sizing: border-box;
         padding: 10px 20px;
         background-color: white;
         box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
         border-radius: 4px;
         display: flex;
         align-items: center;
         justify-content: flex-end;

         label {
             margin-right: 10px;
         }
     }

     .sg-tile-img {
         position: absolute;
         left: 0;
         top: 0;
         overflow: auto;
         width: 100%;
         height: 100%;

         div {
             display: flex;
             flex-wrap: wrap;

             img {
                 border: none;
                 opacity: 0;
                 transition: opacity .382s;

                 &[loaded] {
                     opacity: 1;
                 }

             }
         }
     }
 }
</style>

 用例

<template>
  <div>
    <sgTileImage :data="tileImageData" />
  </div>
</template>
<script>
import sgTileImage from "@/vue/components/admin/sgTileImage";
export default {
  components: { sgTileImage },
  data() {
    return {
      tileImageData: {
        orginTileSize: 500,//瓦片图原始尺寸
        colCount: 20,//行数
        rowCount: 20,//列数
        tiles: [],//瓦片图数组
      }
    }
  },
  created() {
    this.tileImageData.tiles = [...Array(this.tileImageData.colCount * this.tileImageData.rowCount)].map((v, i) => ({
      loaded: false,
      img: `http://shuzhiqiang.com/tiles/${i}.jpg`,
    }));//瓦片图数组
  },

};
</script>

依赖组件【sgDragMoveTile】自定义组件:拖拽瓦片图、地图、大图,滚动条对应同步滚动_你挚爱的强哥的博客-CSDN博客【代码】【sgDragMoveTile】自定义组件:拖拽瓦片图、地图、大图,滚动条对应同步滚动。https://blog.csdn.net/qq_37860634/article/details/133292981

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

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

相关文章

电脑怎么用U盘重装系统-电脑用U盘重装Win10系统的步骤

电脑怎么用U盘重装系统&#xff1f;电脑对于当前日常办公生活是特别重要的&#xff0c;但是&#xff0c;随着操作时间的增加&#xff0c;电脑内的操作系统运作可能会变得越来越缓慢了。这时候重装系统成为解决系统问题的有效方法。下面小编给大家介绍利用U盘给电脑重装系统Win1…

Python入门教程48:Pycharm永久镜像源的pip配置方法

国内几个好用的Python镜像服务器地址&#xff1a; 清华大学镜像站&#xff1a;https://pypi.tuna.tsinghua.edu.cn/simple/阿里云镜像站&#xff1a;https://mirrors.aliyun.com/pypi/simple/中科大镜像站&#xff1a;https://pypi.mirrors.ustc.edu.cn/simple/中国科技大学镜…

HTTP 错误 401.3 - Unauthorized 由于 Web 服务器上此资源的访问控制列表(ACL)配置或加密设置,您无权查看此目录或页面。

用IIS 发布网站&#xff0c;不能访问且出现错误&#xff1a;HTTP 错误 401.3 - Unauthorized 由于Web服务器上此资源的访问控制列表(ACL)配置或加密设置。您无权查看此目录或页面 问题截图&#xff1a; 问题描述&#xff1a;HTTP 错误 401.3 - 未经授权&#xff1a;访问由于 A…

anzo capital昂首资本详解MT4和MT5订单执行方式

很多投资者在后台咨询anzo capital昂首资本&#xff0c;MT4和MT5订单执行方式有什么不同&#xff0c;今天一起探讨! MT4平台提供了三种类型的订单执行方式&#xff1a; 第一种是即时执行。当交易者向经纪人发送建立订单的请求时&#xff0c;平台将自动以当前价格录入该订单。…

《YOLOv5:从入门到实战》报错解决 专栏答疑

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。《YOLOv5&#xff1a;从入门到实战》专栏上线后&#xff0c;部分同学在学习过程中提出了一些问题&#xff0c;笔者相信这些问题其他同学也有可能遇到。为了让大家可以更好地学习本专栏内容&#xff0c;笔者特意推出了该篇专…

Java 实现遍历一个文件夹,文件夹有100万数据,获取到修改时间在2天之内的数据

目录 1 需求2 实现1&#xff08;第一种方法&#xff09;2 实现2 &#xff08;推荐使用这个&#xff0c;快&#xff09;3 实现3&#xff08;推荐&#xff09; 1 需求 现在有一个文件夹&#xff0c;里面会一直存数据&#xff0c;动态的存数据&#xff0c;之后可能会达到100万&am…

【接口测试】HTTP协议

一、HTTP 协议基础 HTTP 简介 HTTP 是一个客户端终端&#xff08;用户&#xff09;和服务器端&#xff08;网站&#xff09;请求和应答的标准&#xff08;TCP&#xff09;。通常是由客户端发起一个请求&#xff0c;创建一个到服务器的 TCP 连接&#xff0c;当服务器监听到客户…

【Java 进阶篇】MySQL数据库范式详解

范式是数据库设计中的一种理论方法&#xff0c;旨在通过减少数据冗余来提高数据存储的有效性和完整性。在MySQL数据库中&#xff0c;范式设计是一个重要的概念&#xff0c;它有助于组织和管理数据&#xff0c;确保数据的一致性和可靠性。本文将深入探讨数据库范式&#xff0c;包…

必备基础算法

目录 一、双指针 双指针与链表双指针与链表 二、前缀和 一维二维 三、差分 一维二维 四、深度搜索&#xff0c;dfs dfs数组排列dfs岛屿问题dfs染色法&#xff08;二分图判定&#xff09;dfs路径规划dfs拓扑排序 五、广度搜索&#xff0c;bfs bfs最优路径规划bfsdijkstra 六、单…

Python与数据分析--Pandas操作进阶

目录 1.文件读取方式 1.1.绝对路径读取文件 1.2.相对路径读取文件 2.列表数据操作 2.1.列索引指定 2.2.代码数据对齐 3.创建新CSV文件 4.缺失值处理 4.1.缺失值创建 4.2.缺失值检索 4.3.缺失值查询 4.3.1.isnull()函数判断 4.3.2.notnull()函数判断 4.3.3.any()函数…

数据集笔记:2015上海地铁一卡通数据

数据地址&#xff1a;上海地铁数据_免费高速下载|百度网盘-分享无限制 (baidu.com) 数据介绍 上海2015年几天的地铁一卡通出入站信息 卡号、交易日期、交易时间、公交线路/地铁站点中文名称、行业名称(公交、地铁、出租、轮渡、PR停车场)、交易金额、交易性质(非优惠、优惠、…

3种Renko图表形态FPmarkets3秒轻松判断价格走势

Renko图表形态在交易中的应用并不逊色于其他技术分析方法。相较于普通的烛台图表&#xff0c;使用Renko图表时&#xff0c;有些经典模式更容易被发现和识别&#xff0c;FPmarkets总结这些模式包括&#xff1a; 首先是头和肩膀形态。这是一种价格反转形态&#xff0c;由两个较小…

【软件测试】软件缺陷报告如何编写

废话不多说&#xff0c;三张图说明 软件缺陷报告如何编写 以及 报告的跟踪流程 软件缺陷报告格式 软件缺陷报告内容说明 缺陷状态 - 分为 新建、打开、修复、关闭 - 新建 - 测试人员第一次发现缺陷 - 打开 - 测试将报告交给开发&#xff0c;开发确认缺陷&#xff0c;准备动手…

threejs中模型自定义路线移动

threejs中模型自定义路线移动 生命不息&#xff0c;学习不止 基于r95Threejs版本 此例子中&#xff1a;包括背景设置&#xff1a;天空之盒。 模型的引用&#xff1a;小车和整体 glb模型引用 路线设置(因线line2无法设置宽度,所以选择了用管道&#xff0c;当然也可用点成面&…

MySQL 索引介绍和最佳实践

目录 一、前言二、索引类型1.1 主键索引&#xff08;PRIMARY KEY&#xff09;1.2 唯一索引&#xff08;UNIQUE&#xff09;1.3 普通索引&#xff08;NORMAL&#xff09;1.3.1 单列普通索引1.3.2 单列前缀普通索引1.3.3 多列普通索引1.3.4 多列前缀普通索引 1.4 空间索引&#x…

商场做小程序商城的作用是什么?

商场是众多商家聚集在一起的购物公共场所&#xff0c;大商场也往往入驻着众多行业商家&#xff0c;是每个城市重要的组成部分。 随着互联网电商深入及客户消费行为改变&#xff0c;不少商场如今的客流量非常有限&#xff0c;甚至可以说是员工比客人多&#xff0c;这就导致撤店…

三相Vienna整流器电流畸变的抑制方法

该博客参考丁文龙的博士论文《低成本充电系统高性能多端口Vienna整流器关键控制策略研究》&#xff0c;他的博士论文深入浅出&#xff0c;分析透彻。感谢师妹Miss Young提供的技术指导&#xff0c;她是一位优秀的电力电子工程师&#xff0c;祝她事业顺利&#xff0c;身体健康。…

【教学类-06-06】20230905数字题目随便玩( 加减法、分合、比大小,纸张消耗)

背景需求&#xff1a; 3年前第一次设计加减法题目时&#xff0c;打印了一大堆加减法、数字分合、比大小的纸张。太多了&#xff0c;以至于三年后整理素材库&#xff0c;发现还有很多这样的纸片。这些20以内、50以内的题目难度大、题量多&#xff0c;完全不适合幼儿园孩子做&am…

Linux基础命令汇总

用户管理 su 切换用户&#xff1a;su 用户名 logname 显示当前用户的登录用户名&#xff1a;logname useradd 创建用户&#xff1a;useradd 用户名创建用户时指定用户的主组&#xff1a;useradd -g 组名 用户名 usermod 添加附属组&#xff1a;usermod -G 组…

基于 Python+DenseNet121 算法模型实现一个图像分类识别系统

项目展示 一、介绍 DenseNet&#xff08;Densely Connected Convolutional Networks&#xff09;是一种卷积神经网络&#xff08;CNN&#xff09;架构&#xff0c;2017年由Gao Huang等人提出。该网络的核心思想是密集连接&#xff0c;即每一层都接收其前面所有层的输出作为输…