D3.js画图:3D动态饼图(齿轮图)

JavaScript012

D3.js画图:3D动态饼图(齿轮图),第1张

通常画可视化图的工具很多,除了d3.js,还有echarts.js等。

通过比较,看起来ECharts.js更容易上手,但是因为我需要更灵活更符合个性定制化的工具,所以选了d3.js。

经过一段时间的磨炼,从折线图、闭合路径图、蜂窝图、直角坐标、极坐标都玩了个遍。

那这次就来个3D的吧,其实d3.js做3D的图不是很容易的,有更好的选择,但我认准了d3.js,一条道走到黑吧(想起高中数学老师说的话,当你解题解到一半时发现有更好的办法,不,赶紧忘掉,接着当前的方法,只要方法没错,总能解出来,也许会傻一点,但是一定会有正确的结果;如果中途放弃,也许另一个方法更快更聪明,但也许更慢或者错误,不算到最后,谁都不知道谁最准确。我选择相信他的话,于是。。。我成了程序员O(∩_∩)O哈哈~)。

有人鄙视拿来主义,要我说,你能拿来那是你的本事,如果还能在此基础上做出更好的东西,何乐而不为呢?

每个人时间有限,每个项目也有deadline,不可能从每一个螺丝钉怎么拧开始学起,不然怎么会有那么多五花八门的框架,会有封装好的组件和接口,正因为有人已经做了前期工作,所以时间才能省下来做更有意义的事情,这就是站在巨人肩上的道理所在吧。

但是我们得明白拿来的东西的原理,以及出了问题该怎么解决的能力。然后才能做出更厉害的东西。

首选当然是官网的例子咯,目测搜了一圈,终于找到一个3D Donut。就是你了,我的巨人。

把该地址的donut3d.js拷贝下来作为画3D饼图的基础js,待会会在此基础上修改,以满足我的要求(长的像齿轮的要求)。

那我们就一睹她的芳容吧。

如果这张图符合你的要求,那就打住,不用往下看了,直接看官网例子即可。

注意d3版本的问题,如果你用d3.v3.js,恭喜你,啥也不用改,直接拿来用;如果你用d3.v5.js,那稍微改下方法,比如d3.v5.js没有d3.layout,所以d3.layout.pie改成d3.pie。我就是那个不幸的人,用的d3.v5.js。没关系,改起来很快,运行下,看哪里有错,就改哪里,O(∩_∩)O哈哈~so easy!

还是先上个我已经改好之后的3D饼图(齿轮图)吧,方便说明。

其实显示的时候是个动态的,一节一节显示出齿轮的。

背景是黑乎乎的,据说现在流行黑乎乎的背景,显得有科技感,技术也要赶时髦啊,我这么fashion的人,做出来的东西也要fashion啊O(∩_∩)O~

从以上分析可以看出,难啃的骨头在第4点。这个图断断续续花了3天时间才搞定,为啥是断断续续呢,因为还有其他工作要做嘛,你懂得。

那就按顺序一条一条实现,总有一天我们的愿望都能实现!

首先新建svg及设置宽高。

我是切分成了32个小齿轮(包含透明的),如果你想分的更细,可以分成40或50个,只要你觉得好看就行。

既然要分成32个小立体快,那数据也要切分成32个。

通过以上处理,把数据整合成可以生成齿轮的完整数据dataset。

如果不增加左侧面和右侧面,那调用donut3d.js的draw方法后,会生成什么样的图形呢?

请各位仔细看。

是不是有种被掏空的感觉?如果你觉得这样挺好看,那也行,打住吧,后面就不用再看了;如果你想补齐其他面,请耐心往下看。

经过观察和比较,增加左侧面和右侧面就能填满空虚的心啦啦啦~

这次要在donut3d.js这个巨人身上添砖加瓦咯。

然后再用新增加的两个方法画出左右侧面。

终于填满需要的每一面,看上去像个立体齿轮图了。

这个图是很久之前做的,当时花了很长时间调试,每一个面有4条边,定位2个点,再加上高度和内半径,就可以计算出4个点,然后就可以画出4条边,最后填充颜色,一个面就完成了。

最近整理文档时觉得有必要写出来,方便以后查阅和探讨,也告诉自己积累是一个长期过程,不急不躁,慢慢来,一步一步完成既定目标,总有一天你会走遍技术的每个角落。

现在我整理成vue组件,传一个百分比的参数,就可以显示3D齿轮图了,我的3D齿轮图也成巨人啦。

你的 paintComponent 中用的都是常数, 没用到 myhero 的数据, 当然不会动!

我稍为改一下就会动了:

public void paintComponent(Graphics g)

{

super.paintComponent(g)

int x=myhero.x, y=myhero.y

g.fillRect(x+50, y+80, 7, 40)

g.fillRect(x+57,y+87,20,24)

g.fillRect(x+77,y+80,7,40)

g.setColor(Color.red)

g.fillOval(x+59,y+92,15,15)

g.setColor(Color.black)

g.fillRect(x+65,y+68,5,24)

}

但这种画法永远向著上方, 还不会转向.

你必须再调整写法.

//画图

//上证

var compareRangeListStr = JSON.stringify(compareRangeList)//json转换成字符串

compareRangeListStr = (compareRangeListStr.split("{")[1]).split("}")[0]//去掉大括号

compareRangeListStr = compareRangeListStr.replace(/"/g,"")//去掉转换字符串时多出来的双引号

var compareRangeListArr = (compareRangeListStr.split(",")).sort()//字符串转数组

//组合

var com_dailyRangeListStr = JSON.stringify(com_dailyRangeList)//json转换成字符串

com_dailyRangeListStr = (com_dailyRangeListStr.split("{")[1]).split("}")[0]//去掉大括号

com_dailyRangeListStr = com_dailyRangeListStr.replace(/"/g,"")//去掉转换字符串时多出来的双引号

var com_dailyRangeListArr = (com_dailyRangeListStr.split(",")).sort()//字符串转数组

var CRL_rangeList = []//上证涨跌幅数组

var com_dateList = []//组合日期数组

var com_rangeList = []//组合涨跌幅数组

var maxSize = com_dailyRangeListArr.length < 20 ? com_dailyRangeListArr.length : 20 //加载数据的最大组数

for(var iForCom = 0 iForCom < maxSize iForCom++){

    com_dateList.push(com_dailyRangeListArr[com_dailyRangeListArr.length - maxSize + iForCom].split(":")[0])

    com_rangeList.push((parseFloat(com_dailyRangeListArr[com_dailyRangeListArr.length - maxSize + iForCom].split(":")[1])*100).toFixed(2))

    CRL_rangeList.push((parseFloat(compareRangeListArr[compareRangeListArr.length - maxSize + iForCom].split(":")[1])*100).toFixed(2))

}

$(".legend1 em").text(com_title+":")

$(".legend1 b").text(com_fromBeginIncomeRange)

$(".legend2 em").text("上证:")

$(".legend2 b").text(CRL_rangeList[CRL_rangeList.length-1]+"%")

if(maxSize < 20){

    for(var f = 0 f < (20 - maxSize) f++){

        com_dateList.push("-")

        com_rangeList.push("-")

        CRL_rangeList.push("-")

    }

}

console.log(com_dateList + "<br />" + com_rangeList + "<br />" + CRL_rangeList)

//echarts数据填充

var option = {

    color: ['#237ad2','#d4305d'],

    //animation: false,

    tooltip: {

        show: true,

        trigger: 'axis',

        formatter: function(params)

        {

            if(params[0].name != "-"){

                var res = params[0].name + '<br/>'

                for (var i = 0, l = params.length i < l i++) {

                    if(params[i].value != "-")

                        res += params[i].seriesName + ': ' + params[i].value + '%<br />'

                    else

                        res += params[i].seriesName + ': <br />'

                }

                return res

            }

            else{

                return "暂无数据"

            }

        },

        axisPointer: {

            lineStyle:{

                width: 1,

                color: "#f00"

            }

        }

    },

    grid:{

        x: 50,y: 25,x2: 10,y2: 30

    },

    legend: {

        data:['组合','上证']

    },

    xAxis : [{

        type : 'category',

        scale: false,

        boundaryGap: false,

        axisLine: {

            lineStyle:{

                width: 1

            }

        },

        data : com_dateList

    }],

    yAxis : [{

        type : 'value',

        axisLabel : {

            formatter: '{value} %'

        }

    }],

    series : [

        {

            name:"组合",

            type:"line",

            data:com_rangeList

        },

        {

            name:"上证",

            type:"line",

            data:CRL_rangeList

        }

    ]

}

// 为echarts对象加载数据

myChart.setOption(option)

我的json数据是{"2015-10-30":0.0054}格式,用的是百度的echart折线图