| | |
| | | <template> |
| | | <div class="instrument-content"> |
| | | <div class="left-img" :data-text="`${attitude_pitch}°`"> |
| | | <div class="scaleImg"> |
| | | <p class="scale" :style="{ top: 45 + ScaleTop + 'px' }"></p> |
| | | <img src="../../../../assets/images/rightmapidentification.png" /> |
| | | </div> |
| | | </div> |
| | | <div class="instrument-center"> |
| | | <div class="compass-box" :data-text="`${prevRotate?.toFixed(2)}°`"> |
| | | <div v-for="(item, index) in str" :key="index" class="scale" |
| | | :style="{ '--rotate': 30 * index - prevRotate + 'deg' }"> |
| | | <span class="text">{{ item }}</span> |
| | | </div> |
| | | </div> |
| | | <div class="center-show"> |
| | | <img src="../../../../assets/images/mapidentification.png" /> |
| | | </div> |
| | | <div class="rotat-btn"></div> |
| | | </div> |
| | | <div class="right-img" :data-text="`${height}m`"> |
| | | <div class="ident-arrow"> |
| | | <img src="../../../../assets/images/leftmapidentification.png" /> |
| | | <div class="arrow-box" :style="{ bottom: realHeight }"> |
| | | <div class="arrow"></div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | <script> |
| | | import _ from 'lodash'; |
| | | export default { |
| | | data () { |
| | | return { |
| | | str: ['W', 30, 33, 'N', 3, 6, 'E', 12, 15, 'S', 21, 24], |
| | | // 俯仰角度数 |
| | | attitude_pitch: 0, |
| | | // 真空高度 |
| | | height: 0, |
| | | // 旋转方向角度 |
| | | prevRotate: 0, |
| | | dockHeight: 0, |
| | | } |
| | | }, |
| | | watch: { |
| | | roamPoint: { |
| | | handler (val) { |
| | | // this.prevRotate = val.arrowHeading |
| | | // this.attitude_pitch = val.roll |
| | | // this.height = _.round(val.altitude,1) |
| | | }, |
| | | }, |
| | | }, |
| | | computed: { |
| | | // roamPoint: vuexStateSimplify('pointsWayLine', 'roamPoint'), |
| | | ScaleTop () { |
| | | return (-this.attitude_pitch * 30) / 90 |
| | | }, |
| | | realHeight () { |
| | | return 0 |
| | | // 无人机高度 |
| | | const maxHeight = 240 |
| | | // 真空高度 |
| | | const vacuumHeight = 120 |
| | | // 机场高度 |
| | | const dockHeightConfig = { |
| | | 'e3dea0f5-37f2-4d79-ae58-490af3228069': 14.7, |
| | | '4a574db8-4ad3-48f7-9f16-3edbcd8056e1': 54, |
| | | // 'f47ac10b-58cc-4372-a567-0e02b2c3d479': 81, |
| | | } |
| | | const workspaceId = |
| | | this.$store.state.drone.selectedWorkSpaceId || |
| | | window.localStorage.getItem('bs_workspace_id') |
| | | const dockHeight = dockHeightConfig?.[workspaceId] || 1 |
| | | <div class="instrument-content"> |
| | | <div class="left-img"> |
| | | <div class="valueBox">{{ props?.options?.pitchAngle || 0 }}°</div> |
| | | <img src="@/assets/images/rightmapidentification.png" alt="" /> |
| | | <div class="triangle" :style="pitchAngleStyle"></div> |
| | | <div class="nameBox">俯仰角度</div> |
| | | </div> |
| | | |
| | | let proportion = 0 |
| | | // 处于真空高度和最大高度之间 |
| | | if (this.height < maxHeight && this.height >= vacuumHeight) { |
| | | const height = this.height - vacuumHeight |
| | | const proport = height / maxHeight |
| | | proportion = Math.round(proport * 50 + 50) |
| | | } |
| | | // 小于等于真空高度计算 |
| | | if (this.height < vacuumHeight && this.height >= dockHeight) { |
| | | const height = this.height - dockHeight |
| | | const copyVacuumHeight = vacuumHeight - dockHeight |
| | | const proport = height / copyVacuumHeight |
| | | proportion = Math.round(proport * 25 + 25) |
| | | } |
| | | // 小于机场高度计算 |
| | | if (this.height < dockHeight && this.height >= 0) { |
| | | const proport = this.height / dockHeight |
| | | proportion = Math.round(proport * 25) |
| | | } |
| | | // 大于最大高度计算 |
| | | if (this.height >= maxHeight) { |
| | | proportion = 95 |
| | | } |
| | | return proportion + '%' |
| | | // const proportion = Math.ceil((this.height / maxHeight) * 100) || 50; |
| | | }, |
| | | }, |
| | | methods: { |
| | | getAngle (currentLngLat, targetLngLat) { |
| | | const { longitude: lng_a, latitude: lat_a } = currentLngLat |
| | | const { longitude: lng_b, latitude: lat_b } = targetLngLat |
| | | var a = ((90 - lat_b) * Math.PI) / 180 |
| | | var b = ((90 - lat_a) * Math.PI) / 180 |
| | | var AOC_BOC = ((lng_b - lng_a) * Math.PI) / 180 |
| | | var cosc = |
| | | Math.cos(a) * Math.cos(b) + |
| | | Math.sin(a) * Math.sin(b) * Math.cos(AOC_BOC) |
| | | var sinc = Math.sqrt(1 - cosc * cosc) |
| | | var sinA = (Math.sin(a) * Math.sin(AOC_BOC)) / sinc |
| | | var A = (Math.asin(sinA) * 180) / Math.PI |
| | | var res = 0 |
| | | if (lng_b > lng_a && lat_b > lat_a) res = A |
| | | else if (lng_b > lng_a && lat_b < lat_a) res = 180 - A |
| | | else if (lng_b < lng_a && lat_b < lat_a) res = 180 - A |
| | | else if (lng_b < lng_a && lat_b > lat_a) res = 360 + A |
| | | else if (lng_b > lng_a && lat_b == lat_a) res = 90 |
| | | else if (lng_b < lng_a && lat_b == lat_a) res = 270 |
| | | else if (lng_b == lng_a && lat_b > lat_a) res = 0 |
| | | else if (lng_b == lng_a && lat_b < lat_a) res = 180 |
| | | return res |
| | | }, |
| | | }, |
| | | } |
| | | <div class="instrument-center"> |
| | | <div class="compass-box" :style="compassStyle"> |
| | | <div v-for="(item, index) in str" :key="index" class="scale" :style="{ '--rotate': 30 * index + 'deg' }"> |
| | | <span class="text">{{ item }}</span> |
| | | </div> |
| | | </div> |
| | | <div class="center-show"> |
| | | <img src="@/assets/images/mapidentification.png" alt="" /> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="right-img"> |
| | | <div class="valueBox">{{ props.options?.trueAltitude }}m</div> |
| | | <img src="@/assets/images/leftmapidentification.png" alt="" /> |
| | | <div class="rightTriangle" :style="trueAltitudeStyle"></div> |
| | | <div class="nameBox">真空高度</div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | const str = ['W', 30, 33, 'N', 3, 6, 'E', 12, 15, 'S', 21, 24] |
| | | |
| | | const props = defineProps(['options']) |
| | | |
| | | const pitchAngleStyle = computed(() => { |
| | | const pitchAngle = props?.options?.pitchAngle || 0 |
| | | // 将 [-90, 90] 映射到 [0%, 100%] |
| | | const percentage = (((pitchAngle + 90) / 180) * 100).toFixed(2) |
| | | return { |
| | | bottom: `${percentage}%`, |
| | | } |
| | | }) |
| | | |
| | | const compassStyle = computed(() => { |
| | | return { transform: `rotate(${props?.options?.yawAngle || 0}deg)` } |
| | | }) |
| | | |
| | | const trueAltitudeStyle = computed(() => { |
| | | const trueAltitude = props?.options?.trueAltitude || 0 |
| | | // 将 [0,240] 映射到 [0%, 100%] |
| | | const percentage = ((trueAltitude / 240) * 100).toFixed(2) |
| | | return { |
| | | bottom: `${percentage}%`, |
| | | } |
| | | }) |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .instrument-content { |
| | | height: 100%; |
| | | width: 100%; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | position: relative; |
| | | transform: translateY(20px); |
| | | height: 100%; |
| | | width: 100%; |
| | | display: flex; |
| | | gap: 0 30px; |
| | | justify-content: center; |
| | | align-items: center; |
| | | position: relative; |
| | | |
| | | .left-img, |
| | | .right-img { |
| | | position: relative; |
| | | height: 100%; |
| | | display: flex; |
| | | align-items: center; |
| | | width: 40px; |
| | | .left-img, |
| | | .right-img { |
| | | position: relative; |
| | | width: 9px; |
| | | height: 127px; |
| | | |
| | | img { |
| | | height: 100px; |
| | | } |
| | | } |
| | | img { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | |
| | | .left-img { |
| | | .text { |
| | | position: absolute; |
| | | left: 0px; |
| | | top: 50px; |
| | | width: 60px; |
| | | text-align: center; |
| | | } |
| | | .triangle { |
| | | width: 0px; |
| | | height: 0px; |
| | | margin: auto; |
| | | border: 6px solid transparent; |
| | | border-left-color: #0fff7b; |
| | | position: absolute; |
| | | left: -10px; |
| | | |
| | | .scaleImg { |
| | | position: relative; |
| | | width: 100%; |
| | | height: 100px; |
| | | transform: translateY(50%); |
| | | } |
| | | |
| | | img { |
| | | margin-left: 25px; |
| | | } |
| | | .rightTriangle { |
| | | width: 0px; |
| | | height: 0px; |
| | | margin: auto; |
| | | border: 6px solid transparent; |
| | | border-left-color: #0fff7b; |
| | | position: absolute; |
| | | right: -10px; |
| | | transform: translateY(50%) rotate(180deg); |
| | | } |
| | | |
| | | .scale { |
| | | width: 10px; |
| | | height: 10px; |
| | | background-color: #1fa3f6; |
| | | position: absolute; |
| | | margin-left: 10px; |
| | | .valueBox { |
| | | position: absolute; |
| | | top: -20px; |
| | | left: 50%; |
| | | transform: translateX(-50%); |
| | | } |
| | | |
| | | &::before { |
| | | content: ''; |
| | | position: absolute; |
| | | width: 0; |
| | | height: 0; |
| | | top: 0px; |
| | | left: 10px; |
| | | border-top: solid 5px transparent; |
| | | border-left: solid 5px #1fa3f6; |
| | | border-bottom: solid 5px transparent; |
| | | } |
| | | } |
| | | } |
| | | .nameBox { |
| | | position: absolute; |
| | | bottom: -20px; |
| | | left: 50%; |
| | | width: auto; |
| | | transform: translateX(-50%); |
| | | font-family: Segoe UI, Segoe UI; |
| | | font-weight: 400; |
| | | font-size: 12px; |
| | | white-space: nowrap; |
| | | color: #ffffff; |
| | | } |
| | | } |
| | | |
| | | &::before { |
| | | content: attr(data-text); |
| | | position: absolute; |
| | | right: 0px; |
| | | top: 50px; |
| | | font-size: 14px; |
| | | font-weight: bolder; |
| | | font-family: none; |
| | | } |
| | | .instrument-center { |
| | | position: relative; |
| | | |
| | | &::after { |
| | | content: '俯仰角度'; |
| | | position: absolute; |
| | | right: 0px; |
| | | text-align: right; |
| | | bottom: 30px; |
| | | font-size: 14px; |
| | | font-weight: bolder; |
| | | font-family: none; |
| | | } |
| | | } |
| | | .compass-box { |
| | | width: 180px; |
| | | height: 180px; |
| | | border-radius: 50%; |
| | | position: relative; |
| | | border: 30px solid rgba($color: #323931, $alpha: 0.5); |
| | | box-shadow: 0 2px 12px 0 #158aff; |
| | | user-select: none; |
| | | |
| | | .right-img { |
| | | &::before { |
| | | content: attr(data-text); |
| | | position: absolute; |
| | | left: 0px; |
| | | top: 50px; |
| | | font-size: 14px; |
| | | font-weight: bolder; |
| | | font-family: none; |
| | | } |
| | | .scale { |
| | | width: 135%; |
| | | position: absolute; |
| | | top: 50%; |
| | | left: 50%; |
| | | font-weight: bold; |
| | | color: #c1c3c4; |
| | | text-align: left; |
| | | transform: translate(-50%, -50%) rotate(var(--rotate)); |
| | | |
| | | &::after { |
| | | content: '真空高度'; |
| | | position: absolute; |
| | | left: 0px; |
| | | bottom: 30px; |
| | | font-size: 14px; |
| | | font-weight: bolder; |
| | | font-family: none; |
| | | } |
| | | &:nth-child(3n - 2) { |
| | | color: #fff; |
| | | font-weight: bolder; |
| | | font-size: 20px; |
| | | } |
| | | } |
| | | |
| | | .ident-arrow { |
| | | position: relative; |
| | | .scale { |
| | | .text { |
| | | display: inline-block; |
| | | // rotate: -90deg; |
| | | transform: rotate(-90deg); |
| | | -ms-transform: rotate(-90deg); |
| | | -moz-transform: rotate(-90deg); |
| | | -webkit-transform: rotate(-90deg); |
| | | -o-transform: rotate(-90deg); |
| | | } |
| | | } |
| | | } |
| | | |
| | | .arrow-box { |
| | | position: absolute; |
| | | bottom: 0; |
| | | left: 20px; |
| | | .rotat-btn { |
| | | width: 16px; |
| | | height: 16px; |
| | | background-color: rgba($color: #1fa3f6, $alpha: 1); |
| | | position: absolute; |
| | | top: 10px; |
| | | left: 50%; |
| | | transform: translateX(-50%); |
| | | |
| | | .arrow { |
| | | position: relative; |
| | | width: 10px; |
| | | height: 10px; |
| | | background-color: #1fa3f6; |
| | | &::before { |
| | | content: ''; |
| | | display: block; |
| | | width: 0; |
| | | height: 0; |
| | | border-left: 8px solid transparent; |
| | | border-right: 8px solid transparent; |
| | | border-bottom: 8px solid #1fa3f6; |
| | | position: absolute; |
| | | bottom: 100%; |
| | | } |
| | | } |
| | | |
| | | &::before { |
| | | content: ''; |
| | | position: absolute; |
| | | left: -9px; |
| | | top: 0; |
| | | .center-show { |
| | | width: 30px; |
| | | height: 40px; |
| | | position: absolute; |
| | | left: 50%; |
| | | top: 50%; |
| | | transform: translate(-50%, -50%); |
| | | |
| | | border: 5px solid transparent { |
| | | right: solid 5px #1fa3f6; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | img { |
| | | width: 100%; |
| | | height: 100%; |
| | | transform: rotate(var(--rotate)); |
| | | transition: all 0.5s linear; |
| | | } |
| | | } |
| | | |
| | | .instrument-center { |
| | | position: relative; |
| | | &::after { |
| | | content: ''; |
| | | position: absolute; |
| | | width: 40px; |
| | | top: 50%; |
| | | left: 0; |
| | | z-index: 99; |
| | | } |
| | | |
| | | .compass-box { |
| | | width: 180px; |
| | | height: 180px; |
| | | border-radius: 50%; |
| | | position: relative; |
| | | border: 30px solid rgba($color: #323931, $alpha: 0.5); |
| | | box-shadow: 0 2px 12px 0 #158aff; |
| | | user-select: none; |
| | | transform: none !important; |
| | | |
| | | .scale { |
| | | width: 135%; |
| | | position: absolute; |
| | | top: 50%; |
| | | left: 50%; |
| | | font-weight: bold; |
| | | color: #c1c3c4; |
| | | text-align: left; |
| | | transform: translate(-50%, -50%) rotate(var(--rotate)); |
| | | |
| | | &:nth-child(3n - 2) { |
| | | color: #fff; |
| | | font-weight: bolder; |
| | | font-size: 20px; |
| | | } |
| | | } |
| | | |
| | | .scale { |
| | | .text { |
| | | display: inline-block; |
| | | // rotate: -90deg; |
| | | transform: rotate(-90deg); |
| | | -ms-transform: rotate(-90deg); |
| | | -moz-transform: rotate(-90deg); |
| | | -webkit-transform: rotate(-90deg); |
| | | -o-transform: rotate(-90deg); |
| | | } |
| | | } |
| | | |
| | | &::before { |
| | | content: ''; |
| | | position: absolute; |
| | | top: -45px; |
| | | left: 50%; |
| | | transform: translateX(-50%); |
| | | width: 7px; |
| | | height: 15px; |
| | | background: #00ee8b; |
| | | margin: 0 auto; |
| | | box-shadow: 0 0 4px rgba(0, 0, 0, 0.5), -1px -1px 0 rgba(0, 0, 0, 0.5), |
| | | 1px -1px 0 rgba(0, 0, 0, 0.5), -1px 1px 0 rgba(0, 0, 0, 0.5), |
| | | 1px 1px 0 rgba(0, 0, 0, 0.5); |
| | | } |
| | | |
| | | &::after { |
| | | content: attr(data-text); |
| | | position: absolute; |
| | | top: -60px; |
| | | font-size: 16px; |
| | | line-height: 16px; |
| | | font-weight: 600; |
| | | color: #00ee8b; |
| | | left: 50%; |
| | | transform: translateX(-50%); |
| | | } |
| | | } |
| | | |
| | | .rotat-btn { |
| | | width: 16px; |
| | | height: 16px; |
| | | background-color: rgba($color: #1fa3f6, $alpha: 1); |
| | | position: absolute; |
| | | top: 10px; |
| | | left: 50%; |
| | | transform: translateX(-50%); |
| | | |
| | | &::before { |
| | | content: ''; |
| | | display: block; |
| | | width: 0; |
| | | height: 0; |
| | | border-left: 8px solid transparent; |
| | | border-right: 8px solid transparent; |
| | | border-bottom: 8px solid #1fa3f6; |
| | | position: absolute; |
| | | bottom: 100%; |
| | | } |
| | | } |
| | | |
| | | .center-show { |
| | | width: 30px; |
| | | height: 40px; |
| | | position: absolute; |
| | | left: 50%; |
| | | top: 50%; |
| | | transform: translate(-50%, -50%); |
| | | |
| | | img { |
| | | width: 100%; |
| | | height: 100%; |
| | | transform: rotate(var(--rotate)); |
| | | transition: all 0.5s linear; |
| | | } |
| | | } |
| | | |
| | | &::after { |
| | | content: ''; |
| | | position: absolute; |
| | | width: 40px; |
| | | top: 50%; |
| | | left: 0; |
| | | z-index: 99; |
| | | } |
| | | |
| | | &::before { |
| | | content: ''; |
| | | position: absolute; |
| | | width: 40px; |
| | | top: 50%; |
| | | right: 0; |
| | | z-index: 99; |
| | | } |
| | | } |
| | | &::before { |
| | | content: ''; |
| | | position: absolute; |
| | | width: 40px; |
| | | top: 50%; |
| | | right: 0; |
| | | z-index: 99; |
| | | } |
| | | } |
| | | } |
| | | </style> |