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


     

    特性:

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

    sgTileImage源码 

    1. <template>
    2. <div :class="$options.name" :fullscreen="fullScreenAll">
    3. <template>
    4. <div class="sg-ctrl" v-if="tiles.length">
    5. <label>缩放百分比label>
    6. <el-input-number
    7. style="width: 150px"
    8. v-model.trim="scaleValue"
    9. :precision="0"
    10. :step="10"
    11. :min="10"
    12. :max="100"
    13. :controls-position="`left`"
    14. @change="changeScaleValue"
    15. />
    16. <div class="btn" v-if="!$store.getters._global.inWindow">
    17. <fullscreenHtml :size="`default`" @change="(d) => (fullScreenAll = d)" />
    18. div>
    19. <el-button class="btn" type="primary" @click="showreviewImg = true"
    20. >查看预览图
    21. >
    22. div>
    23. <div class="sg-tile-img" ref="scrollContainer">
    24. <div
    25. ref="dragContainer"
    26. :style="{ width: `${width * colCount}px`, height: `${height * rowCount}px` }"
    27. >
    28. <img
    29. v-for="(a, i) in tiles"
    30. :key="i"
    31. :ref="`tile${i}`"
    32. :loaded="a.loaded"
    33. :width="width"
    34. :height="height"
    35. draggable="false"
    36. />
    37. div>
    38. div>
    39. template>
    40. <template v-if="previewImgURL && showreviewImg">
    41. <div class="preview-sm-image">
    42. <img :src="previewImgURL" />
    43. <el-tooltip
    44. :content="`查看瓦片图`"
    45. :effect="`light`"
    46. :enterable="false"
    47. :placement="`top-start`"
    48. :popper-class="`sg-el-tooltip`"
    49. :transition="`none`"
    50. ><el-button
    51. class="sg-closeModalBtn"
    52. type="primary"
    53. icon="el-icon-close"
    54. @click="showreviewImg = false"
    55. circle
    56. />
    57. el-tooltip>
    58. <div class="bg" @click="showreviewImg = false">div>
    59. div>
    60. template>
    61. <sgDragMoveTile :data="dragMoveTileData" @dragMove="dragMove" />
    62. div>
    63. template>
    64. <script>
    65. import fullscreenHtml from "@/vue/components/admin/fullscreenHtml";
    66. import sgDragMoveTile from "@/vue/components/admin/sgDragMoveTile";
    67. export default {
    68. name: "sgTileImage",
    69. components: {
    70. fullscreenHtml,
    71. sgDragMoveTile,
    72. },
    73. data() {
    74. return {
    75. fullScreenAll: false,
    76. dragMoveTileData: {},
    77. scaleValue: 100,
    78. orginWidth: 0, //瓦片图原始宽度
    79. orginHeight: 0, //瓦片图原始高度
    80. width: 0, //瓦片图实时宽度
    81. height: 0, //瓦片图实时高度
    82. colCount: 0,
    83. rowCount: 0,
    84. tiles: [], //瓦片图数组
    85. previewImgURL: ``, //预览图片路径
    86. showreviewImg: true,
    87. mousePoint_bk: null,
    88. };
    89. },
    90. props: [
    91. "data",
    92. /* data格式:{
    93. width:500,//瓦片图原始宽度
    94. height:500,//瓦片图原始高度
    95. colCount: 20,//行数
    96. rowCount: 20,//列数
    97. scaleValue: 100,//缩放百分比
    98. tiles: [],//瓦片图数组
    99. } */
    100. ],
    101. watch: {
    102. data: {
    103. handler(newValue, oldValue) {
    104. if (newValue && Object.keys(newValue).length) {
    105. newValue = JSON.parse(JSON.stringify(newValue));
    106. newValue.width && (this.orginWidth = newValue.width); //记录原始宽度
    107. newValue.height && (this.orginHeight = newValue.height); //记录原始高度度
    108. newValue.width && (this.width = newValue.width);
    109. newValue.height && (this.height = newValue.height);
    110. newValue.colCount && (this.colCount = newValue.colCount);
    111. newValue.rowCount && (this.rowCount = newValue.rowCount);
    112. newValue.tiles && (this.tiles = newValue.tiles);
    113. newValue.scaleValue && (this.scaleValue = newValue.scaleValue);
    114. newValue.previewImgURL && (this.previewImgURL = newValue.previewImgURL);
    115. this.$nextTick(() => {
    116. this.loadScreenViewTiles();
    117. });
    118. }
    119. },
    120. deep: true, //深度监听
    121. immediate: true, //立即执行
    122. },
    123. scaleValue: {
    124. handler(newValue, oldValue) {
    125. this.width = (this.orginWidth * newValue) / 100;
    126. this.height = (this.orginHeight * newValue) / 100;
    127. this.$nextTick(() => {
    128. this.loadScreenViewTiles();
    129. });
    130. },
    131. deep: true, //深度监听
    132. immediate: true, //立即执行
    133. },
    134. },
    135. destroyed() {
    136. removeEventListener("mousewheel", this.mousewheel);
    137. },
    138. mounted() {
    139. this.dragMoveTileData = {
    140. scrollContainer: this.$refs.scrollContainer,
    141. dragContainer: this.$refs.dragContainer,
    142. };
    143. let rect_scrollContainer = this.$refs.scrollContainer.getBoundingClientRect();
    144. this.mousePoint_bk = {
    145. x: rect_scrollContainer.width / 2,
    146. y: rect_scrollContainer.height / 2,
    147. };
    148. setTimeout(() => {
    149. this.loadScreenViewTiles();
    150. addEventListener("mousewheel", this.mousewheel, { passive: false });
    151. }, 1000);
    152. },
    153. methods: {
    154. // 校正放大缩小后,瓦片图的坐标(目的是为了让缩放看起来是以鼠标坐标为中心点)
    155. centerPosition(
    156. e,
    157. { rect_dragContainer_orign, scrollLeft_orgin, scrollTop_orgin, mousePoint }
    158. ) {
    159. let scrollContainer = this.$refs.scrollContainer;
    160. let dragContainer = this.$refs.dragContainer;
    161. let rect_dragContainer = dragContainer.getBoundingClientRect();
    162. let scale = rect_dragContainer.width / rect_dragContainer_orign.width; //缩放比例
    163. let mouse_left_dis_orgin = scrollLeft_orgin + mousePoint.x; //缩放前鼠标距离瓦片图最左侧的距离
    164. let mouse_top_dis_orgin = scrollTop_orgin + mousePoint.y; //缩放前鼠标距离瓦片图最顶部的距离
    165. let mouse_left_dis = mouse_left_dis_orgin * scale; //缩放后鼠标距离瓦片图最左侧的距离
    166. let mouse_top_dis = mouse_top_dis_orgin * scale; //缩放后鼠标距离瓦片图最顶部的距离
    167. let scrollLeft = mouse_left_dis - mousePoint.x;
    168. let scrollTop = mouse_top_dis - mousePoint.y;
    169. scrollContainer.scrollLeft = scrollLeft;
    170. scrollContainer.scrollTop = scrollTop;
    171. this.mousePoint_bk = mousePoint;
    172. },
    173. mousewheel(e) {
    174. if (!this.$refs.dragContainer || this.showreviewImg) return false;
    175. // 记录缩放前的数据
    176. let rect_dragContainer_orign = this.$refs.dragContainer.getBoundingClientRect();
    177. let scrollContainer = this.$refs.scrollContainer;
    178. let mousePoint = { x: e.x, y: e.y };
    179. let scrollLeft_orgin = scrollContainer.scrollLeft;
    180. let scrollTop_orgin = scrollContainer.scrollTop;
    181. // 开始缩放
    182. e.deltaY < 0 && (this.scaleValue += 10);
    183. e.deltaY > 0 && (this.scaleValue -= 10);
    184. // 开始计算坐标
    185. this.$nextTick(() => {
    186. this.centerPosition(e, {
    187. rect_dragContainer_orign,
    188. scrollLeft_orgin,
    189. scrollTop_orgin,
    190. mousePoint,
    191. });
    192. });
    193. e.preventDefault && e.preventDefault(); //阻止默认的滚轮事件
    194. return false;
    195. },
    196. // 获取浏览器可视范围的瓦片图,并加载图片
    197. loadScreenViewTiles(d) {
    198. let scrollContainer = this.$refs.scrollContainer;
    199. if (scrollContainer) {
    200. let rect_scrollContainer = scrollContainer.getBoundingClientRect();
    201. this.tiles.forEach((v, i) => {
    202. let tile = this.$refs[`tile${i}`];
    203. if (tile) {
    204. tile = tile[0];
    205. let rectTile = tile.getBoundingClientRect();
    206. if (
    207. rectTile.x + rectTile.width > rect_scrollContainer.x - rectTile.width &&
    208. rectTile.y + rectTile.height > rect_scrollContainer.y - rectTile.height &&
    209. rectTile.x < rect_scrollContainer.x + rect_scrollContainer.width &&
    210. rectTile.y < rect_scrollContainer.y + rect_scrollContainer.height
    211. ) {
    212. tile.onload = (d) => {
    213. v.loaded = true;
    214. };
    215. tile.src = v.img;
    216. }
    217. }
    218. });
    219. }
    220. },
    221. dragMove(d) {
    222. this.loadScreenViewTiles();
    223. },
    224. changeScaleValue(d) {
    225. this.mousewheel(this.mousePoint_bk);
    226. },
    227. },
    228. };
    229. script>
    230. <style lang="scss" scoped>
    231. .sgTileImage {
    232. .sg-ctrl {
    233. position: absolute;
    234. left: 10px;
    235. top: 10px;
    236. z-index: 1;
    237. box-sizing: border-box;
    238. padding: 10px 20px;
    239. background-color: white;
    240. box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
    241. border-radius: 4px;
    242. display: flex;
    243. align-items: center;
    244. justify-content: flex-end;
    245. label {
    246. margin-right: 10px;
    247. }
    248. .btn {
    249. margin-left: 10px;
    250. }
    251. }
    252. .sg-tile-img {
    253. position: absolute;
    254. left: 0;
    255. top: 0;
    256. overflow: auto;
    257. width: 100%;
    258. height: 100%;
    259. div {
    260. display: flex;
    261. flex-wrap: wrap;
    262. img {
    263. border: none;
    264. opacity: 0;
    265. transition: opacity 0.382s;
    266. &[loaded] {
    267. opacity: 1;
    268. }
    269. }
    270. }
    271. }
    272. .preview-sm-image {
    273. position: absolute;
    274. left: 0;
    275. top: 0;
    276. width: 100%;
    277. height: 100%;
    278. // background-color: #000000cc;
    279. display: flex;
    280. justify-content: center;
    281. align-items: center;
    282. flex-direction: column;
    283. z-index: 2;
    284. img {
    285. width: 100%;
    286. height: 100%;
    287. object-position: center;
    288. object-fit: contain;
    289. pointer-events: none;
    290. z-index: 1;
    291. }
    292. .bg {
    293. background-color: #000000cc;
    294. width: 100%;
    295. height: 100%;
    296. position: absolute;
    297. left: 0;
    298. top: 0;
    299. }
    300. }
    301. &[fullscreen] {
    302. position: fixed;
    303. width: 100%;
    304. height: 100%;
    305. left: 0;
    306. top: 0;
    307. z-index: 2000;
    308. background-color: white;
    309. }
    310. }
    311. style>

     用例

    1. <template>
    2. <div>
    3. <sgTileImage :data="tileImageData" />
    4. div>
    5. template>
    6. <script>
    7. import sgTileImage from "@/vue/components/admin/sgTileImage";
    8. export default {
    9. components: { sgTileImage },
    10. data() {
    11. return {
    12. tileImageData: {
    13. width: 500, //瓦片图原始宽度(API提供)
    14. height: 500, //瓦片图原始高度(API提供)
    15. colCount: 0, //行数(API提供)
    16. rowCount: 0, //列数(API提供)
    17. tiles: [], //瓦片图数组(API提供)
    18. },
    19. };
    20. },
    21. created() {
    22. this.tileImageData.tiles = [
    23. ...Array(this.tileImageData.colCount * this.tileImageData.rowCount),
    24. ].map((v, i) => ({
    25. loaded: false,
    26. img: `http://rp.wedoyun.cn/tiles/${i}.jpg`,
    27. })); //瓦片图数组
    28. },
    29. };
    30. script>

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

  • 相关阅读:
    2022中国机器人产业报告发布 企业如何应对新阶段下的增长与竞争?
    嵌入式数据库Sqlite
    SpringBatch(7):监听器
    Echarts 散点图的详细配置过程
    关于接口|常见电商API接口种类|接口数据类型|接口请求方法
    【运维】在 Docker 容器中指定 UTF-8 编码:方法与技巧
    带宽、采样率、存储深度的理解
    08、http协议和dubbo协议的区别
    【2022国赛模拟】相似序列问题——DFA(DP套DP)
    详解RecyclerView的预布局
  • 原文地址:https://blog.csdn.net/qq_37860634/article/details/133304598