菜单

canvas图表(贰) – 折线图

2019年5月5日 - JavaScript

原来的书文地址:canvas图表(2) –
折线图

话说那天气1冷啊, 就患懒癌, 就不想码代码, 就想着在床上舒舒服服看录像.
那顺便就看blender摄像, 学习下3D建立模型, 要是学会了建3D模型,
那作者的webGL手艺就大有用处啊,能够独自开荒小游戏了,
当然是玩笑了。但第三依旧把canvas图表种类先弄完吧, 明天就弄折线图。

功效请看:柱状图https://edwardzhong.github.io/sites/demo/dist/chartline.html
图片 1

重要功效点包含:

  1. 集体数量;
  2. 绘制;
    3. 数据动画的达成;
    4. 清屏相提并论美术面;
    5. 鼠标事件的拍卖。

绝大许多的手艺在上壹节的canvas图表(1) –
柱状图
贯彻了,
所以那节内容实在是相比较轻松的。相比费力一点的即是折线图的卡通片了,所以最重要就看一下那1部分的代码。

利用格局

运用办法,和柱状图基本是一致的,大家这边代表的是空气温度变化图。

    var con=document.getElementById('container');
    var line = new Line(con);
    line.init({
        title:'未来一周气温变化',
        xAxis:{
            data:['周一','周二','周三','周四','周五','周六','周日']
        },
        yAxis:{
            name:'温度',
            formatter:'{value} °C'
        },
        series:[
            {
                name:'最高气温',
                data:[11, 11, 15, 13, 12, 13, 10]
            },
            {
                name:'最低气温',
                data:[1, -2, 2, 5, 3, 2, 0]
            }   
        ]
    })

代码结构

折线图对象大意和柱状图一律,只是局地方法通过重构。

    class Line extends Chart{
        constructor(container){
            super(container);
        }
        // 初始化
        init(opt){

        }
        // 绑定事件
        bindEvent(){

        }
        // 显示信息
        showInfo(pos,arr){

        }
        // 清除内容再绘制
        clearGrid(index){

        }
        // 执行数据动画
        animate(){

        }
        // 执行
        create(){

        }
        // 组织数据
        initData(){

        }
        // 绘制
        draw(){

        }
    }

数量动画

折线图动画达成的是路线绘制特效,懂canvas的主干都知情原理,正是用lineTo绘制路线,最终stroke出来。但那么些折线图是分支的,所以要分情形管理,主要困难就是获得多少个点时期的坐标。

周密想想下什么样实现绘制路线动画,因为大家通晓x轴总市长度,所以能够让x依次递增,再求出x对应的y坐标就能够。既然知道了五个点的坐标,还清楚了x坐标,依照同角度等比例三角形原理,很轻松求出y坐标。

还有便是翻新意况的位移动画了,这些就一发简便易行了,依据近期地点和要将在运动到的职位比较,举办对应的增减就可以。

    animate(){
        var that=this,
            ctx=this.ctx,
            obj,h=0,
            isStop=true;

        (function run(){
            ctx.clearRect(0,that.padding+that.paddingTop-5,that.W,that.H-2*that.padding-that.paddingTop+4);
            that.drawY();

            ctx.save();
            ctx.translate(that.padding,that.H-that.padding);
            isStop=true;
            for(var i=0,item;i<that.animateArr.length;i++){
                item=that.animateArr[i];
                if(item.hide) continue;
                ctx.strokeStyle=item.color;
                ctx.lineWidth=item.data[0].w;
                item.isStop=true;
                if(item.create){//新增绘制路径动画
                    for(var j=0,jl=item.data.length;j<jl;j++){
                        obj=item.data[j];
                        if(obj.y>=obj.h){
                            obj.y=obj.p=obj.h;
                        } else {
                            obj.y+=obj.vy;
                            item.isStop=false;
                        }
                        ctx.beginPath();
                        ctx.moveTo(obj.x+obj.w/2,-obj.y);
                        ctx.lineTo(obj.x+obj.w/2,-1);
                        ctx.stroke();
                    }
                } else { //更新位移动画
                    for(var j=0,jl=item.data.length;j<jl;j++){
                        obj=item.data[j];
                        if(obj.p>obj.h){
                            h=obj.y-4;
                            if(h<obj.h){
                                obj.y=obj.p=obj.h;
                            }
                        } else {
                            h=obj.y+4;
                            if(h>obj.h){
                                obj.y=obj.p=obj.h;
                            }
                        }
                        if(obj.p!=obj.h){
                            obj.y=h;
                            item.isStop=false;
                        }

                        ctx.beginPath();
                        ctx.moveTo(obj.x+obj.w/2,-obj.y);
                        ctx.lineTo(obj.x+obj.w/2,-1);
                        ctx.stroke();
                    }
                }
                if(!item.isStop){isStop=false; }
            }
            ctx.restore();
            if(isStop)return;
            requestAnimationFrame(run);
        }())
    }

清屏天公地道摄影面

在画面上要落到实处动态效果的时候,需求清屏,重新绘制画面,要是钦定了有些区间,就在该区间上画标识线,同时该区间的圆心放大。

    clearGrid(index){
        var that=this,
            obj, r=5,
            ctx=this.ctx;
        ctx.clearRect(0,0,that.W,that.H);
        // 画坐标系
        this.drawAxis();
        // 画标签
        this.drawTag();
        // 画y轴刻度
        this.drawY();

        ctx.save();
        ctx.translate(that.padding,that.H-that.padding);
        // 画标志线
        if(typeof index== 'number'){
            obj=that.animateArr[0].data[index];
            ctx.lineWidth=1;
            ctx.strokeStyle='hsla(0,0%,70%,1)';
            ctx.moveTo(obj.x,-that.H+that.paddingTop+2*that.padding);
            ctx.lineTo(obj.x,0);
            ctx.stroke();
        }

        for(var i=0,item,il=that.animateArr.length;i<il;i++){
            item=that.animateArr[i];
            if(item.hide)continue;
            ctx.lineWidth=4;
            ctx.strokeStyle=item.color;
            ctx.fillStyle='#fff';
            ctx.beginPath();
            for(var j=0,obj,jl=item.data.length;j<jl;j++){
                obj=item.data[j];
                if(j==0){
                    ctx.moveTo(obj.x,-obj.h);
                } else {
                    ctx.lineTo(obj.x,-obj.h);
                }
            }
            ctx.stroke();

            //画完曲线后再画圆球
            for(var j=0,jl=item.data.length;j<jl;j++){
                obj=item.data[j];
                ctx.strokeStyle=item.color;
                ctx.lineWidth=index===j?6:4;
                r=index===j?10:5;
                ctx.beginPath();
                ctx.arc(obj.x,-obj.h,r,0,Math.PI*2,false);
                ctx.stroke();
                ctx.fill();
            }
        }
        ctx.restore();
    }

事件管理

mousemove
1是触动标签展现手形,贰是滑过画面区域的时候擦除同等看待美术面,选中的折线的圆形扩张,同时绘制提醒线,具体看clearGrid方法。

mousedown某些击标签就能够显得隐藏对应分组,创设状态实行路线绘制动画,而立异情状那是实践位移动画。

    bindEvent(){
        var that=this,
            ctx=that.ctx,
            canvas=that.canvas,
            xl=this.xAxis.data.length,
            xs=(that.W-2*that.padding)/(xl-1),
            index=0;
        this.canvas.addEventListener('mousemove',function(e){
            var isLegend=false;
            // todo ...

            if(isLegend) return;
            // 鼠标位置在图表中时
            if(pos.y*2>that.padding+that.paddingTop && pos.y*2<that.H-that.padding && pos.x*2>that.padding && pos.x*2<that.W-that.padding){
                canvas.style.cursor='pointer';
                for(var i=0;i<xl;i++){
                    if(pos.x*2>i*xs){
                        index=i;
                    }
                }
                // 重绘并标志选中信息
                that.clearGrid(index);

                // 获取处于当前位置的信息
                var arr=[];
                for(var j=0,item,l=that.animateArr.length;j<l;j++){
                    item=that.animateArr[j];
                    if(item.hide)continue;
                    arr.push({name:item.name, num:item.data[index].num})
                }
                that.showInfo(pos,arr);
                ctx.restore();
            } else {
                that.tip.style.display='none';
                that.clearGrid();
            }

        },false);

        this.canvas.addEventListener('mousedown',function(e){
            e.preventDefault();
            var box=that.canvas.getBoundingClientRect();
            var pos = {
                x:e.clientX-box.left,
                y:e.clientY-box.top
            };
            for(var i=0,item,len=that.legend.length;i<len;i++){
                item=that.legend[i];
                roundRect(ctx,item.x,item.y,item.w,item.h,item.r);
                if(ctx.isPointInPath(pos.x*2,pos.y*2)){
                    that.series[i].hide=!that.series[i].hide;
                    that.create();
                    break;
                }
            }
        },false);
    }

最后

装有图表代码请看chart.js

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图