• vue拖动缩放组件(vue-drag-zoom) 增加禁止缩放拖拽功能


    前段时间写了一个类似于百度ICOR,可拖拽缩放图片并在图片上框选文字的功能,这里的拖拽缩放功能就用到了vue-drag-zoom组件,组件是从npm下载的VUE2代码,放在VUE3+vite项目里面也是可以兼容的,但是组件仅提供了禁止缩放的API且存在一定问题

    附npm地址:vue-drag-zoom - npm该组件适用于对一个元素在某个区域内进行拖动/缩放. Latest version: 1.0.9, last published: 4 months ago. Start using vue-drag-zoom in your project by running `npm i vue-drag-zoom`. There are no other projects in the npm registry using vue-drag-zoom.https://www.npmjs.com/package/vue-drag-zoom
    以下为改造后源码:
    1.新增禁止拖拽API,与禁止缩放API整合
    2.解决了缩放后图片不固定在中心点问题
    3.解决了频繁禁止与恢复拖拽缩放时,图片回到初始位置的问题

    1. <script>
    2. export default {
    3. name: "xx-drag-zoom",
    4. components: {},
    5. props: {
    6. /* 被操作的元素 start */
    7. // X 坐标
    8. left: {
    9. type: Number,
    10. default: 0,
    11. },
    12. // Y 坐标
    13. top: {
    14. type: Number,
    15. default: 0,
    16. },
    17. // 宽度
    18. width: Number,
    19. // 高度
    20. height: Number,
    21. // 允许缩放
    22. allowZoom: Boolean,
    23. // 缩放比例
    24. zoom: {
    25. type: Number,
    26. default: 1,
    27. },
    28. // 最大缩放比例
    29. maxZoom: {
    30. type: Number,
    31. default: 2,
    32. },
    33. // 最小缩放比例
    34. minZoom: {
    35. type: Number,
    36. default: 0.5,
    37. },
    38. // 缩放幅度
    39. range: {
    40. type: Number,
    41. default: 0.1,
    42. },
    43. /* 被操作的元素 end */
    44. /* 活动区域 start */
    45. // 节点 (注: 传入节点后可以自动获取数据, 不需要再传坐标与宽高了)
    46. areaNode: HTMLDivElement,
    47. // X 坐标 (未设置 areaNode 时生效)
    48. areaLeft: {
    49. type: Number,
    50. default: 0,
    51. },
    52. // Y 坐标 (未设置 areaNode 时生效)
    53. areaTop: {
    54. type: Number,
    55. default: 0,
    56. },
    57. // 宽度 (未设置 areaNode 时生效)
    58. areaWidth: {
    59. type: Number,
    60. default: 200,
    61. },
    62. // 高度 (未设置 areaNode 时生效)
    63. areaHeight: {
    64. type: Number,
    65. default: 100,
    66. },
    67. /* 活动区域 end */
    68. },
    69. data() {
    70. return {
    71. currentZoom: this.zoom,
    72. initLeft: this.left,
    73. initTop: this.top,
    74. lastPosition: {},
    75. };
    76. },
    77. computed: {
    78. // 被操作的元素节点
    79. dragZoomNode() {
    80. return this.$refs["xx-drag-zoom"];
    81. },
    82. // 活动区域数据
    83. areaNodeData() {
    84. let obj = {};
    85. // 计算属性无法监听活动区域的宽高变化
    86. const node = this.areaNode;
    87. if (node) {
    88. obj = {
    89. left: node.clientLeft, //活动区域定位 ? 左边框宽度
    90. top: node.clientTop,
    91. width: node.offsetWidth, // 活动区域宽高
    92. height: node.offsetHeight,
    93. };
    94. } else {
    95. obj = {
    96. left: this.areaLeft,
    97. top: this.areaTop,
    98. width: this.areaWidth,
    99. height: this.areaHeight,
    100. };
    101. }
    102. return obj;
    103. },
    104. // 设置样式
    105. dragZoomNodeStyle() {
    106. return {
    107. transform: `scale(${this.currentZoom})`,
    108. left: `${this.initLeft}px`,
    109. top: `${this.initTop}px`,
    110. width: this.width + "px",
    111. height: this.height + "px",
    112. cursor: this.allowZoom ? "move" : "default",
    113. };
    114. },
    115. },
    116. watch: {
    117. zoom(val) {
    118. this.currentZoom = val;
    119. this.initStyle(val);
    120. },
    121. },
    122. created() {},
    123. mounted() {
    124. this.dragZoomNode.addEventListener("mousedown", this.mousedown);
    125. this.dragZoomNode.addEventListener("wheel", this.mousescroll);
    126. this.initStyle("mounted");
    127. },
    128. beforeDestroy() {
    129. this.dragZoomNode.removeEventListener("mousedown", null);
    130. this.dragZoomNode.removeEventListener("wheel", null);
    131. },
    132. methods: {
    133. // 鼠标点击事件
    134. mousedown(evt) {
    135. const areaW = this.areaNode
    136. ? this.areaNode.offsetWidth
    137. : this.areaNodeData.width;
    138. const areaH = this.areaNode
    139. ? this.areaNode.offsetHeight
    140. : this.areaNodeData.height;
    141. const {
    142. offsetLeft: dragL,
    143. offsetTop: dragT,
    144. offsetWidth: dragW,
    145. offsetHeight: dragH,
    146. } = this.dragZoomNode; //缩放内容的宽高与到活动区域的宽高距离
    147. const x = evt.clientX - dragL; //鼠标相对于图片的位置
    148. const y = evt.clientY - dragT;
    149. // 鼠标拖动事件
    150. document.onmousemove = (evt) => {
    151. const zoom = this.currentZoom; //缩放比
    152. // 不允许拖动
    153. if (!this.allowZoom) {
    154. return;
    155. }
    156. let styleL = evt.clientX - x;
    157. let styleT = evt.clientY - y;
    158. // 当拖动元素宽度小于父元素时
    159. if (dragW * zoom < areaW) {
    160. // 注: 使用 scale 缩放后, 元素实际尺寸不会改变
    161. const boundaryL = (dragW * zoom - dragW) / 2;
    162. const boundaryR = areaW - (dragW + boundaryL);
    163. // 左边界
    164. if (styleL < boundaryL) {
    165. styleL = boundaryL;
    166. }
    167. // 右边界
    168. if (styleL > boundaryR) {
    169. styleL = boundaryR;
    170. }
    171. } else {
    172. // 注: 使用 scale 缩放后, 元素实际尺寸不会改变
    173. const boundaryL = (dragW * zoom - dragW) / 2;
    174. const boundaryR = -(dragW * zoom - areaW - boundaryL);
    175. // 左边界
    176. if (styleL > boundaryL) {
    177. styleL = boundaryL;
    178. }
    179. // 右边界
    180. if (styleL < boundaryR) {
    181. styleL = boundaryR;
    182. }
    183. }
    184. // 当拖动元素高度小于父元素时
    185. if (dragH * zoom < areaH) {
    186. // 注: 使用 scale 缩放后, 元素实际尺寸不会改变
    187. const boundaryT = (dragH * zoom - dragH) / 2;
    188. const boundaryB = areaH - (dragH + boundaryT);
    189. // 上边界
    190. if (styleT < boundaryT) {
    191. styleT = boundaryT;
    192. }
    193. // 下边界
    194. if (styleT > boundaryB) {
    195. styleT = boundaryB;
    196. }
    197. } else {
    198. // 注: 使用 scale 缩放后, 元素实际尺寸不会改变
    199. const boundaryT = (dragH * zoom - dragH) / 2;
    200. const boundaryB = -(dragH * zoom - areaH - boundaryT);
    201. // 上边界
    202. if (styleT > boundaryT) {
    203. styleT = boundaryT;
    204. }
    205. // 下边界
    206. if (styleT < boundaryB) {
    207. styleT = boundaryB;
    208. }
    209. }
    210. this.dragZoomNode.style.left = styleL + "px";
    211. this.dragZoomNode.style.top = styleT + "px";
    212. this.lastPosition.left = styleL;
    213. this.lastPosition.top = styleT;
    214. this.$emit("mousemove", evt);
    215. };
    216. document.onmouseup = () => {
    217. document.onmousemove = null;
    218. };
    219. },
    220. // 鼠标滚轮事件
    221. mousescroll(evt) {
    222. // 阻止默认行为
    223. if (evt.preventDefault) {
    224. evt.preventDefault();
    225. } else {
    226. evt.returnValue = false;
    227. }
    228. const { deltaY } = evt;
    229. const {
    230. left: areaL,
    231. top: areaT,
    232. // width: areaW,
    233. // height: areaH,
    234. } = this.areaNodeData;
    235. const areaW = this.areaNode
    236. ? this.areaNode.offsetWidth
    237. : this.areaNodeData.width;
    238. const areaH = this.areaNode
    239. ? this.areaNode.offsetHeight
    240. : this.areaNodeData.height;
    241. const {
    242. offsetLeft: dragL,
    243. offsetTop: dragT,
    244. offsetWidth: dragW,
    245. offsetHeight: dragH,
    246. } = this.dragZoomNode;
    247. let zoom = this.currentZoom;
    248. // 不允许缩放
    249. if (!this.allowZoom) {
    250. return;
    251. }
    252. // 上滑
    253. if (deltaY < 0) {
    254. if (zoom >= this.maxZoom) {
    255. return;
    256. }
    257. zoom += this.range;
    258. } else {
    259. if (zoom <= this.minZoom) {
    260. return;
    261. }
    262. zoom -= this.range;
    263. }
    264. this.currentZoom = Number(zoom.toFixed(1));
    265. /* 边界判定 */
    266. const subtractW = (dragW * this.currentZoom - dragW) / 2;
    267. const subtractH = (dragH * this.currentZoom - dragH) / 2;
    268. const currentL = dragL - subtractW;
    269. const currentT = dragT - subtractW;
    270. const currentR = dragL + dragW + subtractW;
    271. const currentB = dragT + dragH + subtractH;
    272. // 当拖动元素宽度小于父元素时
    273. if (dragW * zoom < areaW) {
    274. // 左边界判定
    275. if (currentL < areaL) {
    276. this.dragZoomNode.style.left = areaL + subtractW + "px";
    277. }
    278. // 右边界判定
    279. if (currentR > areaW) {
    280. this.dragZoomNode.style.left = areaW - dragW - subtractW + "px";
    281. }
    282. } else {
    283. // 左边界判定
    284. if (currentL > areaL) {
    285. this.dragZoomNode.style.left = areaL + subtractW + "px";
    286. }
    287. // 右边界判定
    288. if (currentR < areaW) {
    289. this.dragZoomNode.style.left = areaW - dragW - subtractW + "px";
    290. }
    291. }
    292. // 当拖动元素高度小于父元素时
    293. if (dragH * zoom < areaH) {
    294. // 上边界判定
    295. if (currentT < areaT) {
    296. this.dragZoomNode.style.top = areaT + subtractH + "px";
    297. }
    298. // 下边界判定
    299. if (currentB > areaH) {
    300. this.dragZoomNode.style.top = areaH - dragH - subtractH + "px";
    301. }
    302. } else {
    303. // 上边界判定
    304. if (currentT > areaT) {
    305. this.dragZoomNode.style.top = areaT + subtractH + "px";
    306. }
    307. // 下边界判定
    308. if (currentB < areaH) {
    309. this.dragZoomNode.style.top = areaH - dragH - subtractH + "px";
    310. }
    311. }
    312. this.$emit("mousescroll", evt);
    313. },
    314. // 样式初始化
    315. initStyle(title) {
    316. let tmpLeft = this.left;
    317. let tmpTop = this.top;
    318. const { offsetWidth: dragW, offsetHeight: dragH } = this.dragZoomNode;
    319. tmpLeft = this.left - (dragW * (1 - this.zoom)) / 2;
    320. tmpTop = this.left - (dragH * (1 - this.zoom)) / 2;
    321. this.initLeft = tmpLeft || this.lastPosition.left;
    322. this.initTop = tmpTop || this.lastPosition.top;
    323. },
    324. },
    325. };
    326. script>
    327. <style scoped>
    328. .xx-drag-zoom {
    329. position: absolute;
    330. user-select: none;
    331. }
    332. style>

  • 相关阅读:
    [Java] 乐观锁?公平锁?可重入锁?盘点Java中锁相关的概念
    Offer II 102+LC667+1306+2321
    将数字的每一位倒序存入数组,再正序输出,为什么这样是错误的?
    如何求候选码、属性集的闭包
    EMC Unity存储系统如何查看SSD的使用寿命
    腾讯春招后端一面(算法篇)
    zotero通过DOI快速导入文献
    数据结构:KMP算法的原理图解和代码解析
    基于材料生成算法优化概率神经网络PNN的分类预测 - 附代码
    GMT安装与使用
  • 原文地址:https://blog.csdn.net/SunFlower914/article/details/126467601