打造自己的图表控件5

上一次加入了鼠标拖拽功能,这回加入我来加入一个区域框,并且框内数据的阴影,效果如下

具体实现思路就是,可以响应鼠标事件来移动和调整大小,并且获取覆盖的数据,画出阴影.

响应鼠标事件可以改造上次写的鼠标移动的功能

区域框的设计为2部分,一部分是上下的区域,用来移动整个区域框,一部分为左右的线,用来改变区域框的大小.

鼠标在这两个部分上点击时才会捕获鼠标事件.

具体实现如下

class VerticalRangeNavigation extends MouseNavigation {
    constructor() {
        super()
        this.start = []
        this.draging = false
        this.screen = []
        this.range = []
        this._area = {
            "topBar":"topBar",
            "bottomBar":"bottomBar",
            "left":"left",
            "right":"right",
            "none":"none"
        }
        this.barSize = 20
        this.hitTarget = this._area.none
        this.color =  "#FF00FF"
    }
    setRange(from, to){
        this.range = [from , to]
    }
    hit(mouseEventArgs) {
        if (this.draging) return true
        let e = mouseEventArgs
        if (e.area != this.area) return false
        
        if(this._getHitArea(mouseEventArgs) !=  this._area.none)
            return true
        return false
    }
    onMouseDown(mouseEventArgs) {
        let e = mouseEventArgs
        e.prevent()
        let ieLeftDown = this._isLeftDown(e)
        if (ieLeftDown) {
            this._startDrag(e)
            this.hitTarget = this._getHitArea(mouseEventArgs)
        }
    }
    _getHitArea(mouseEventArgs){        
        let e = mouseEventArgs
        let x1 = this.viewport.transformX(this.range[0],e.screen)
        let x2 = this.viewport.transformX(this.range[1],e.screen)
        let offset = 5
        if(e.x > Math.min(x1 ,x2) && e.x < Math.max( x1 ,x2) && e.y > e.screen[1] && e.y < e.screen[1]  + this.barSize) return this._area.topBar
        if(e.x > Math.min(x1 ,x2) && e.x < Math.max( x1 ,x2) && e.y > e.screen[1] + e.screen[3] -  this.barSize && e.y < e.screen[1] + e.screen[3] ) return this._area.bottomBar
        if  (Math.abs(e.x - x1) < offset) return this._area.left
        if  (Math.abs(e.x - x2) < offset) return this._area.right
        return this._area.none
    }
    _startDrag(e) {
        this.start = [e.x, e.y]
        this.draging = true
        this.screen = e.screen

    }
    _stopDrag() {
        this.start = [0, 0]
        this.draging = false
    }
    _isLeftDown(e) {
        return e.event.button == 0
    }
    onMouseMove(mouseEventArgs) {
        let e = mouseEventArgs
        if (this.draging && this._isLeftDown(e)) {
            e.prevent()
            let startX = this.start[0]
            let width = this.screen[2]
            let height = this.screen[3]
            var deltaX = e.x - startX

            let visibleLeft = this.viewport.visible[0]
            let visibleWidth = this.viewport.visible[2] - visibleLeft
            
            var offsetX = deltaX / width * visibleWidth
            switch (this.hitTarget){
                case this._area.left:
                    this.range[0] += offsetX
                    break;
                case this._area.right:
                    this.range[1] += offsetX
                    break;
                case this._area.topBar:
                case this._area.bottomBar:
                    this.range[0] += offsetX
                    this.range[1] += offsetX
                    break;
                default:
                    break;
            }
            
            this.start = [e.x, e.y]
        }
        let target = this.hitTarget
        if (target == this._area.none){
            target = this._getHitArea(mouseEventArgs)
        }
        switch (target){
            case this._area.left:
                mouseEventArgs.cursor = "w-resize"
                break;
            case this._area.right:
                mouseEventArgs.cursor = "e-resize"
                break;
            case this._area.topBar:
            case this._area.bottomBar:
                mouseEventArgs.cursor = "move"
                break;
            default:
                break;
        }
    }
    onMouseUp(mouseEventArgs) {
        let e = mouseEventArgs
        if (this.draging) {
            e.prevent()
            this._stopDrag()
            this.hitTarget = this._area.none
        }
    }
}

基本上就是完全拷贝了之前的 MouseNavigationDrawing .加入了鼠标捕获区域的判定.鼠标移动时改变区域的位置.

但是现在的区域框是看不到的.所以还要实现显示的功能,

之前我有 CanvasDrawingElement 类型,所以继承它的类都有 render 方法进行绘图,可是js并不支持多继承,也没有接口.

不过并不妨碍我们使用 render , 我们稍稍改造一下绘制的逻辑,在里面判断有没有 render 方法,只要有 render 都进行绘制就可以了.

一下是实现绘制的方法,这样区域框就可以被显示出来了.

class VerticalRangeNavigation extends MouseNavigation {
...
    
    render(context, [left, top, width, height]) {
        context.strokeStyle = this.color

        let x1 =  this.viewport.transformX( this.range[0],[left, top, width, height])
        let x2 =  this.viewport.transformX( this.range[1],[left, top, width, height])
        context.beginPath()
        context.moveTo(x1, top)
        context.lineTo(x1 , top + height)
        context.stroke()

        context.beginPath()
        context.moveTo(x2, top)
        context.lineTo(x2 , top + height)
        context.stroke()

        context.fillStyle = this.color
        context.fillRect(x1, top, x2 - x1,this.barSize)
        context.fillRect(x1, top + height - this.barSize , x2 - x1, this.barSize)
       
    }
}

鸭子万岁.

现在已经可以实现移动的功能了,再加入数据阴影的功能.

class VerticalRangeNavigation extends MouseNavigation {
...
    _getAreaData(){
        let elements = [...this.chart.getElements()] || []
        let result = []
        for (let element of elements.filter(e => Array.isArray(e.data) )) {
            let rangeData = []
            for (let i = 0; i < element.data.length / 2; i++) {
                let x = element.data[2 * i]
                let y = element.data[2 * i + 1]
                let x1 = this.range[0]
                let x2 = this.range[1]
                if(x > Math.min(x1,x2) &&  x < Math.max(x1,x2) ){
                    rangeData.push(x)
                    rangeData.push(y)
                }
            }
            result.push({data:rangeData,color:element.color})
        }
        return result
    }
 
    render(context, [left, top, width, height]) {
        let rangeDatas = this._getAreaData()
        for (let index = 0; index < rangeDatas.length; index++) {
            const rangeData = rangeDatas[index]
            let points = this.viewport.transform(rangeData.data, [left, top, width, height])
            let centerY = this.viewport.transformY(0, [left, top, width, height])
            for (let j = 0; j < points.length / 2; j++) {
                const x = points[j * 2];
                const y = points[j * 2 + 1];
                context.strokeStyle = rangeData.color || "#FF0000"
                context.beginPath()
                context.moveTo(x, y)
                context.lineTo(x , centerY)
                context.stroke()
            }
        }
        ...
       
    }
}

最后的效果

 

 

收工

posted @ 2019-07-09 11:50  长蘑菇星人  阅读(123)  评论(0编辑  收藏  举报