特性:
- 支持缩放瓦片图,定义瓦片图初始缩放比例,以鼠标所在位置为中心缩放
- 支持局部拖拽加载
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>
依赖组件