App.vue 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. <template>
  2. <div id="app">
  3. <!-- 展示 -->
  4. <div id="openseadragon" />
  5. <!-- 参数调试 -->
  6. <div class="params">
  7. <label>
  8. <Input v-model="Width" readonly>
  9. <template #prepend>
  10. <span>CAD宽度</span>
  11. </template>
  12. </Input>
  13. </label>
  14. <label>
  15. <Input v-model="Height" readonly>
  16. <template #prepend>
  17. <span>CAD高度</span>
  18. </template>
  19. </Input>
  20. </label>
  21. <label>
  22. <Input v-model="scale" @on-blur="initViewer">
  23. <template #prepend>
  24. <span>隧道比例</span>
  25. </template>
  26. </Input>
  27. </label>
  28. </div>
  29. <div class="text">
  30. <p style="font-size: 17px">
  31. 注:
  32. </p>
  33. <p>1. CAD宽高 = 切图宽高 * X_Y.JPG,自动计算</p>
  34. <p>2. 比例使用 1/比例</p>
  35. </div>
  36. </div>
  37. </template>
  38. <script setup>
  39. import { onMounted, ref, watchEffect } from 'vue'
  40. import { Input } from 'view-ui-plus'
  41. import 'view-ui-plus/dist/styles/viewuiplus.css'
  42. import OpenSeadragon from 'openseadragon'
  43. import { svgOverlay } from '@/utils/openseadragon-svg-overlay'
  44. import axios from '@/utils/axios'
  45. // 数据
  46. const scale = ref(0)
  47. const Width = ref(process.env.imgSize.W)
  48. const Height = ref(process.env.imgSize.H)
  49. const SIZE = process.env.imgSize.S
  50. let overlay = null
  51. let viewer = null
  52. const CADPATH = 'CAD_1/'
  53. const SVGPATH = 'tunnel/1.svg'
  54. const RINGWIDTH = 70
  55. const STARTRing = 1
  56. const TOTALRING = 8000
  57. const currentRing = 4500
  58. const MAXZOOM = process.env.imgSize.M
  59. let tunnelPath = null
  60. // 获取svg路径
  61. const getTunnelPath = async () => {
  62. const txt = await axios.get(SVGPATH)
  63. const objE = document.createElement('div')
  64. objE.innerHTML = txt
  65. let svg = objE.lastElementChild
  66. svg = document.importNode(svg, true)
  67. const path = svg.getElementsByTagName('path')
  68. // 隧道比例默认SVG宽度
  69. const num = +svg.getAttribute('width').replace(/[^0-9]/gi, '')
  70. scale.value = num
  71. tunnelPath = path
  72. }
  73. /**
  74. * 获取角度
  75. * @params x0 上一环x
  76. * @params y0 上一环y
  77. * @params x1 当前环x
  78. * @params y2 当前环y
  79. * @returns [角度,x,y]
  80. */
  81. const getDeg = (x0, y0, x1, y1) => {
  82. const x = (x0 + x1) / 2
  83. const y = (y0 + y1) / 2
  84. const xX = x1 - x0
  85. const yY = y0 - y1
  86. const degrees = Math.abs(
  87. (Math.asin(yY / Math.sqrt(yY * yY + xX * xX)) * 180) / Math.PI
  88. )
  89. let MyAngle
  90. if (xX >= 0 && yY >= 0) MyAngle = 270 + (90 - degrees)
  91. if (xX >= 0 && yY <= 0) MyAngle = degrees
  92. if (xX <= 0 && yY <= 0) MyAngle = 90 + (90 - degrees)
  93. if (xX <= 0 && yY >= 0) MyAngle = 180 + degrees
  94. return [ MyAngle, x, y ]
  95. }
  96. /**
  97. * 根据环号获取点位
  98. * @params sRing 开始环
  99. * @params eRing 结束环
  100. * @params path 隧道路径 默认上路径
  101. * @returns [{x,y}]
  102. */
  103. const getPoint = (sRing, eRing, path = tunnelPath[0]) => {
  104. if (!path) return []
  105. const StartRingPnt = [] // 通风里程点位
  106. const length = path.getTotalLength()
  107. for (let k = sRing; k < eRing; k++) {
  108. const Pnts = path.getPointAtLength(length * (k / TOTALRING))
  109. StartRingPnt.push(Pnts)
  110. }
  111. return StartRingPnt
  112. }
  113. /**
  114. * 画通风管道
  115. * @params sRing 开始环
  116. * @params eRing 结束环
  117. * @params type 类型 1 直通道 | 2 转弯通道 默认转弯通道
  118. * @returns dom-line
  119. */
  120. const drawLines = (sRing, eRing, type = 2, sRing2 = '22715.705078125,11780.609375', eRing2 = '23547.28515625,11636.23828125') => {
  121. // 通风里程点位
  122. const StartRingPnt = getPoint(sRing, eRing)
  123. // 添加通风管道=>转弯里程一定要是车行道开始里程
  124. const line = document.createElementNS('http://www.w3.org/2000/svg', 'polyline')
  125. line.setAttributeNS(null, 'fill', 'none')
  126. line.setAttributeNS(null, 'stroke', '#696969')
  127. line.setAttributeNS(null, 'stroke-width', 25)
  128. let txt2 = ''
  129. for (let k = 0; k < StartRingPnt.length; k++) {
  130. const el = StartRingPnt[k]
  131. if (type === 2) {
  132. if (k < StartRingPnt.length) {
  133. txt2 += `${el.x + RINGWIDTH / 2 - 20},${el.y + RINGWIDTH / 2 - 20} `
  134. } else {
  135. txt2 += `${el.x},${el.y} `
  136. }
  137. }
  138. if (type === 1) {
  139. txt2 += `${el.x - RINGWIDTH / 2},${el.y - RINGWIDTH / 2} `
  140. }
  141. }
  142. if (type === 2) txt2 += `${sRing2} ${eRing2}` // 另一个隧道开始点位
  143. line.setAttributeNS(null, 'transform', `translate(0 0) scale(${1 / scale.value})`)
  144. line.setAttributeNS(null, 'points', txt2)
  145. return line
  146. }
  147. /**
  148. * 画隧道
  149. * @params sRing 开始环
  150. * @params eRing 结束环
  151. * @params path 隧道路径
  152. * @returns obj-dom
  153. */
  154. const drawTunnel = (sRing, eRing, path = tunnelPath[0]) => {
  155. // 未挖段->根据开始结束区分开挖和未开挖
  156. const StartRingPnt = getPoint(sRing, eRing)
  157. const line = document.createElementNS('http://www.w3.org/2000/svg', 'polyline')
  158. line.setAttributeNS(null, 'fill', 'none')
  159. line.setAttributeNS(null, 'stroke', '#696969')
  160. line.setAttributeNS(null, 'stroke-width', RINGWIDTH)
  161. let txt = ''
  162. for (let k = 0; k < StartRingPnt.length; k++) {
  163. const el = StartRingPnt[k]
  164. txt += `${el.x},${el.y} `
  165. }
  166. line.setAttributeNS(null, 'transform', `translate(0 0) scale(${1 / scale.value})`)
  167. line.setAttributeNS(null, 'points', txt)
  168. // 当前环
  169. const cRing = sRing >= 0 ? sRing : eRing
  170. const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect')
  171. const length = path.getTotalLength()
  172. const PrePnt = path.getPointAtLength(length * ((cRing - 1) / TOTALRING))
  173. const Pnt = path.getPointAtLength(length * (cRing / TOTALRING))
  174. const deg = getDeg(PrePnt.x, PrePnt.y, Pnt.x, Pnt.y)
  175. const ringWidth = (1 / TOTALRING) * length * STARTRing
  176. rect.setAttributeNS(null, 'x', deg[1] - ringWidth / 2)
  177. rect.setAttributeNS(null, 'y', deg[2] - RINGWIDTH / 2)
  178. rect.setAttributeNS(null, 'height', RINGWIDTH)
  179. rect.setAttributeNS(null, 'width', ringWidth)
  180. rect.setAttributeNS(null, 'fill', '#87CEFA')
  181. rect.setAttributeNS(null, 'transform', `translate(0 0) scale(${1 / scale.value}) rotate(${deg[0]} ${deg[1]} ${deg[2]})`)
  182. // 点击事件
  183. viewer.svgOverlay().onClick(rect, (e) => {
  184. console.log(666, e)
  185. })
  186. return { line, rect }
  187. }
  188. /**
  189. * 文字
  190. * @params cRing 当前环
  191. * @params path 隧道路径
  192. * @returns dom-text
  193. */
  194. const drawText = (cRing, path = tunnelPath[0]) => {
  195. const length = path.getTotalLength()
  196. const PrePnt = path.getPointAtLength(length * ((cRing - 1) / TOTALRING))
  197. const Pnt = path.getPointAtLength(length * (cRing / TOTALRING))
  198. const deg = getDeg(PrePnt.x, PrePnt.y, Pnt.x, Pnt.y)
  199. const text = document.createElementNS('http://www.w3.org/2000/svg', 'text')
  200. text.innerHTML = '666666'
  201. text.setAttributeNS(null, 'x', Pnt.x)
  202. text.setAttributeNS(null, 'y', Pnt.y)
  203. text.setAttributeNS(null, 'fill', 'red')
  204. text.setAttribute('transform', `scale(${1 / scale.value}) rotate(${deg[0]} ${deg[1]} ${deg[2]}) `)
  205. return text
  206. }
  207. /**
  208. * 文字自身旋转
  209. * @params cRing 当前环
  210. * @params path 隧道路径
  211. * @returns dom-text
  212. */
  213. const drawText2 = (cRing, path = tunnelPath[0]) => {
  214. const length = path.getTotalLength()
  215. const Pnt = path.getPointAtLength(length * (cRing / TOTALRING))
  216. const text = document.createElementNS('http://www.w3.org/2000/svg', 'text')
  217. text.innerHTML = '555555'
  218. text.setAttributeNS(null, 'x', Pnt.x)
  219. text.setAttributeNS(null, 'y', Pnt.y)
  220. text.setAttributeNS(null, 'fill', 'red')
  221. // 测试自身旋转
  222. text.setAttribute('transform', `scale(${1 / scale.value}) rotate(${80} ${Pnt.x} ${Pnt.y}) `)
  223. return text
  224. }
  225. // 初始化
  226. svgOverlay(OpenSeadragon)
  227. const initViewer = async () => {
  228. if (viewer) {
  229. viewer.destroy()
  230. viewer = null
  231. overlay = null
  232. }
  233. // 获取CAD切图地址
  234. viewer = OpenSeadragon({
  235. id: 'openseadragon',
  236. prefixUrl: '/planIcon/',
  237. showNavigationControl: true,
  238. showZoomControl: false,
  239. showHomeControl: false,
  240. showFullPageControl: false,
  241. showFlipControl: false,
  242. showRotationControl: false,
  243. zoomPerClick: 1,
  244. navigationControlAnchor: OpenSeadragon.ControlAnchor.TOP_LEFT,
  245. showNavigator: true,
  246. navigatorPosition: 'BOTTOM_RIGHT',
  247. navigatorSizeRatio: 0.125,
  248. minZoomLevel: 1,
  249. maxZoomLevel: MAXZOOM,
  250. defaultZoomLevel: 4,
  251. debugMode: false,
  252. visibilityRatio: 1.0,
  253. constrainDuringPan: false,
  254. tileSources: {
  255. height: Height.value,
  256. width: Width.value,
  257. tileSize: SIZE,
  258. minLevel: 9,
  259. maxLevel: 15,
  260. getTileUrl(level, x, y) {
  261. level -= 9
  262. level = 2 ** level
  263. // return `http://cp.caner.top:9080/public/admin/cad_config/CAD_1/${level}/${x}_${y}.jpg`
  264. return `${CADPATH + level}/${x}_${y}.jpg`
  265. }
  266. }
  267. })
  268. overlay = viewer.svgOverlay().node()
  269. // 添加隧道
  270. const { line, rect } = drawTunnel(currentRing, TOTALRING)
  271. overlay.append(line)
  272. overlay.append(rect)
  273. // 添加通风管道
  274. const line2 = drawLines(3798.5 - 150, 3798.5)
  275. overlay.append(line2)
  276. // 添加直通隧道
  277. const line3 = drawLines(150, 1500, 1)
  278. overlay.append(line3)
  279. // 添加wenz
  280. const txt = drawText(currentRing)
  281. const txt2 = drawText2(currentRing)
  282. overlay.append(txt)
  283. overlay.append(txt2)
  284. }
  285. // 监听缩放
  286. watchEffect(() => {
  287. console.log('scale 被修改', scale.value)
  288. })
  289. // 生命周期
  290. onMounted(async () => {
  291. await getTunnelPath()
  292. initViewer()
  293. })
  294. </script>
  295. <style lang="less" scoped>
  296. #app {
  297. width: 1000px;
  298. height: 800px;
  299. margin: 0 auto;
  300. text-align: center;
  301. color: #2c3e50;
  302. user-select: none;
  303. #openseadragon {
  304. width: 1000px;
  305. height: 600px;
  306. border: solid 1px #ccc;
  307. }
  308. .params {
  309. width: 100%;
  310. display: flex;
  311. margin-top: 10px;
  312. // justify-content: center;
  313. & > label {
  314. margin-right: 10px;
  315. }
  316. }
  317. .text {
  318. color: red;
  319. font-size: 13px;
  320. margin-top: 10px;
  321. text-align: left;
  322. & > p:not(:first-child) {
  323. text-indent: 20px;
  324. }
  325. }
  326. }
  327. </style>
  328. <style>
  329. .openseadragon-canvas {
  330. outline: none !important;
  331. }
  332. </style>