index.vue 9.7 KB

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