Cesium实现带帧率显示的状态栏,cesium显示帧率

实现效果

在这里插入图片描述

源码

1、PerformaceDisplay.js

/**
 * 显示帧率的插件
 */
export default class PerformanceDisplay {
    constructor() {
        this._lastFpsSampleTime = Cesium.getTimestamp()
        this._fpsFrameCount = 0
        this._fpsText = document.createTextNode('')
    }

    update(container) {
        const time = Cesium.getTimestamp()

        this._fpsFrameCount++
        const fpsElapsedTime = time - this._lastFpsSampleTime

        if (fpsElapsedTime > 1000) {
            let fps = ((this._fpsFrameCount * 1000) / fpsElapsedTime) | 0

            this._fpsText.nodeValue = `${fps} FPS`
            this._lastFpsSampleTime = time
            this._fpsFrameCount = 0

            if (fps < 60) {
                container.style.color = '#ff0'
            } else if (fps < 30) {
                container.style.color = '#f00'
            } else {
                container.style.color = '#0f0'
            }

            container.appendChild(this._fpsText)
        }
    }

    destroy() {
        return Cesium.destroyObject(this)
    }
}

2、StatusBar.js

import PerformanceDisplay from "./PerformaceDisplay";

export default class StatusBar {
    constructor(viewer, el) {
        this.el = el // 挂载的节点
        this.viewer = viewer

        this.divDom = undefined

        this.longitude = '--'
        this.latitude = '--'
        this.pitch = '--'
        this.heading = '--'
        this._handler = undefined
        this._cameraHeight = '--'
        this.scale = '--'

        this.performanceDisplay = undefined

        this.width = this.viewer.scene.canvas.clientWidth
        this.height = this.viewer.scene.canvas.clientHeight

        this.scaleList = [
            1, 2, 3, 5, 10, 20, 30, 50, 100, 200, 300, 500, 1000, 2000, 3000, 5000, 10000, 20000,
            30000, 50000, 100000, 200000, 300000, 500000, 1000000, 2000000, 3000000, 5000000,
            10000000, 20000000, 30000000, 50000000,
        ]
        this.createStatusBarDomStyle()
        this.createStatusBarDomElement()
    }

    /**
     * 创建dom元素
     */
    createStatusBarDomElement() {
        this.divDom = document.createElement('div')
        this.divDom.id = 'statusBarDomElement'

        const performaceDom = document.createElement('div')
        performaceDom.id = 'performaceDom'
        this.divDom.appendChild(performaceDom)

        const otherContainer = document.createElement('div')
        otherContainer.id = 'otherContainer'
        this.divDom.appendChild(otherContainer)

        const langLatDom = document.createElement('span')
        langLatDom.id = 'langLatDom'
        otherContainer.appendChild(langLatDom)

        const headingPitchCameraScleDom = document.createElement('span')
        headingPitchCameraScleDom.id = 'headingPitchCameraScleDom'
        otherContainer.appendChild(headingPitchCameraScleDom)

        const scaleBarDom = document.createElement('div')
        scaleBarDom.id = 'scaleBarDom'
        otherContainer.appendChild(scaleBarDom)

        this.el.style.position = 'relative'
        this.el.appendChild(this.divDom)

        this.setLangLatDomInnerHTML()
        this.setHeadingPitchCameraScleDom()

        this.getLongAndLat()

        this.performanceDisplay = new PerformanceDisplay()

        this.viewer.scene.camera.changed.addEventListener(this.getHeadingPitchCamera.bind(this))
        this.viewer.scene.postRender.addEventListener(this.showPerformance.bind(this))
    }

    showPerformance() {
        this.performanceDisplay.update(document.getElementById('performaceDom'))
    }

    createStatusBarDomStyle() {
        let style = document.getElementById('StatusBarDomStyle')
        if (!style) {
            style = document.createElement('style')
            style.id = 'StatusBarDomStyle'
            const css = /*css*/ `
                #statusBarDomElement{
                    position: absolute;
                    bottom:0;
                    width:100%;
                    height:26px;
                    color:#fff;
                    overflow:hidden;
                    font-size:14px;
                    line-height:40px;
                    background-color:rgba(58,58,58,0.8);
                    z-index:100;
                    display:flex;
                    align-items:center;
                    justify-content: space-between;
                    padding:0 15px;
                    box-sizing:border-box;
                }

                #otherContainer{
                    display:flex;
                    align-items:center;
                }

                #performaceDom{
                    color:#0f0;
                }

                #scaleBarDom{
                    font-size:12px;
                    text-align:center;
                    position:relative;
                    line-height:1.5em;
                }
                #scaleBarDom::after{
                    content: "";
                    position: absolute;
                    width: 100%;
                    height: 10px;
                    border: 1px solid #fff;
                    border-top: none;
                    left: 0;
                    bottom: 0;
                }
            `

            const cssText = document.createTextNode(css)

            style.appendChild(cssText)

            document.body.appendChild(style)
        }
    }

    setLangLatDomInnerHTML() {
        this.divDom.querySelector('#langLatDom').innerHTML = `
            <span>经度:${this.longitude}&nbsp;&nbsp;</span>
            <span>经度:${this.latitude}&nbsp;&nbsp;</span>`
    }

    setHeadingPitchCameraScleDom() {
        this.divDom.querySelector('#headingPitchCameraScleDom').innerHTML = `
            <span>距地面:${this._cameraHeight}&nbsp;&nbsp;</span>
            <span>方向:${this.pitch}&nbsp;&nbsp;</span>
            <span>俯仰角:${this.heading}&nbsp;&nbsp;</span>`
    }

    computedScaleLineWidth(surfaceDistance) {
        //定义比例尺最大的宽度100px
        let maxScaleLineWidth = 100

        let distance

        for (let i = this.scaleList.length - 1; i >= 0; --i) {
            if (this.scaleList[i] / surfaceDistance < maxScaleLineWidth) {
                distance = this.scaleList[i]
                break
            }
        }

        if (distance) {
            const label = distance >= 1000 ? distance / 1000 + ' km' : distance + ' m'
            const scaleLineWidth = (distance / surfaceDistance) | 0

            this.setScaleLineWidth(label, scaleLineWidth)
        }
    }

    setScaleLineWidth(label, scaleLineWidth) {
        const scaleBarDom = this.divDom.querySelector('#scaleBarDom')
        scaleBarDom.innerText = label
        scaleBarDom.style.width = scaleLineWidth + 'px'
    }

    getLongAndLat() {
        this._handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas)
        this._handler.setInputAction((movement) => {
            const endPosition = movement.endPosition
            const cartesian = this.viewer.scene.camera.pickEllipsoid(
                endPosition,
                this.viewer.scene.globe.ellipsoid,
            )

            if (Cesium.defined(cartesian)) {
                const cartographic = Cesium.Cartographic.fromCartesian(cartesian)
                this.longitude = Cesium.Math.toDegrees(cartographic.longitude).toFixed(5)
                this.latitude = Cesium.Math.toDegrees(cartographic.latitude).toFixed(5)
                this.setLangLatDomInnerHTML()
            }
        }, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
    }

    getHeadingPitchCamera() {
        const scene = this.viewer.scene
        const globe = scene.globe

        const left = scene.camera.getPickRay(
            new Cesium.Cartesian2((this.width / 2) | 0, this.height - 1),
        )
        const right = scene.camera.getPickRay(
            new Cesium.Cartesian2((1 + this.width / 2) | 0, this.height - 1),
        )

        const leftPosition = globe.pick(left, scene)
        const rightPosition = globe.pick(right, scene)

        if (leftPosition && rightPosition) {
            const geodesic = new Cesium.EllipsoidGeodesic()
            const leftCartographic = globe.ellipsoid.cartesianToCartographic(leftPosition)
            const rightCartographic = globe.ellipsoid.cartesianToCartographic(rightPosition)
            geodesic.setEndPoints(leftCartographic, rightCartographic)

            const surfaceDistance = geodesic.surfaceDistance

            this.computedScaleLineWidth(surfaceDistance)
            this.setHeadingPitchCameraScleDom()
        }

        const cameraHeight = this.viewer.camera.positionCartographic.height
        this._cameraHeight =
            cameraHeight < 1000
                ? cameraHeight.toFixed(2) + 'm'
                : (cameraHeight / 1000).toFixed(2) + 'km'

        this.pitch = this.viewer.scene.camera.pitch.toFixed(4)
        this.heading = this.viewer.scene.camera.heading.toFixed(4)
    }

    destroy() {
        this.viewer.scene.camera.changed.removeEventListener(this.getHeadingPitchCamera.bind(this))

        this.viewer.scene.postRender.removeEventListener(this.showPerformance.bind(this))

        this._handler?.destroy()

        this.performanceDisplay.destroy()

        this.divDom?.remove()

        document.body.removeChild(document.getElementById('StatusBarDomStyle'))
        // @ts-ignore
        this.viewer = null
        return Cesium.destroyObject(this)
    }

}

使用方式

导入

import StatusBar from '@/cesium/status-bar/StatusBar';

viewer = new Cesium.Viewer('map', {
        
});
// 创建
// 状态栏组件
const statusbar = new StatusBar(viewer, document.getElementById('map'));

// 销毁,不使用时调用destroy方法销毁
// statusbar.destroy()
posted @ 2024-11-27 14:15  青花·  阅读(96)  评论(0)    收藏  举报  来源