├── .gitignore
├── doc
├── img
│ ├── bg_hr.png
│ ├── blacktocat.png
│ ├── carousel1.jpg
│ ├── carousel2.jpg
│ ├── icon_download.png
│ ├── sprite_download.png
│ ├── glyphicons-halflings.png
│ └── glyphicons-halflings-white.png
├── screenshots
│ ├── bar.jpg
│ ├── line.jpg
│ ├── pie.jpg
│ └── polar.jpg
├── css
│ ├── fonts
│ │ ├── glyphicons-halflings-regular.eot
│ │ ├── glyphicons-halflings-regular.ttf
│ │ └── glyphicons-halflings-regular.woff
│ ├── styles.css
│ ├── bootstrap-theme.min.css
│ └── bootstrap-theme.css
├── js
│ ├── application.js
│ ├── doc.js
│ ├── holder.min.js
│ ├── highlight.min.js
│ ├── bootstrap.min.js
│ └── JChart.min.js
└── index.html
├── demo
├── style.css
├── radar.html
├── bar.html
├── polar.html
├── line.html
└── pie.html
├── README.md
├── package.json
├── Gruntfile.js
├── JChart.json
└── src
├── JChart.Bar.js
├── JChart.Line.js
├── JChart.Polar.js
├── JChart.Radar.js
├── JChart.Pie.js
├── JChart.Canvas.js
├── JChart.Chart.js
├── JChart.js
└── JChart.Scale.js
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 |
--------------------------------------------------------------------------------
/doc/img/bg_hr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shixy/JChart/HEAD/doc/img/bg_hr.png
--------------------------------------------------------------------------------
/doc/img/blacktocat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shixy/JChart/HEAD/doc/img/blacktocat.png
--------------------------------------------------------------------------------
/doc/img/carousel1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shixy/JChart/HEAD/doc/img/carousel1.jpg
--------------------------------------------------------------------------------
/doc/img/carousel2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shixy/JChart/HEAD/doc/img/carousel2.jpg
--------------------------------------------------------------------------------
/doc/img/icon_download.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shixy/JChart/HEAD/doc/img/icon_download.png
--------------------------------------------------------------------------------
/doc/screenshots/bar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shixy/JChart/HEAD/doc/screenshots/bar.jpg
--------------------------------------------------------------------------------
/doc/screenshots/line.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shixy/JChart/HEAD/doc/screenshots/line.jpg
--------------------------------------------------------------------------------
/doc/screenshots/pie.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shixy/JChart/HEAD/doc/screenshots/pie.jpg
--------------------------------------------------------------------------------
/doc/screenshots/polar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shixy/JChart/HEAD/doc/screenshots/polar.jpg
--------------------------------------------------------------------------------
/doc/img/sprite_download.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shixy/JChart/HEAD/doc/img/sprite_download.png
--------------------------------------------------------------------------------
/doc/img/glyphicons-halflings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shixy/JChart/HEAD/doc/img/glyphicons-halflings.png
--------------------------------------------------------------------------------
/doc/img/glyphicons-halflings-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shixy/JChart/HEAD/doc/img/glyphicons-halflings-white.png
--------------------------------------------------------------------------------
/demo/style.css:
--------------------------------------------------------------------------------
1 | .chartsContainer{
2 | width : 1000px;
3 | margin: 0 auto;
4 | padding: 10px;
5 | border: 1px solid #eee;
6 | }
--------------------------------------------------------------------------------
/doc/css/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shixy/JChart/HEAD/doc/css/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/doc/css/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shixy/JChart/HEAD/doc/css/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/doc/css/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shixy/JChart/HEAD/doc/css/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | JChart
2 | ======
3 |
4 | Jingle Chart.移动端专属。极简的图表控件,简单的图形+合适的接口,打造交互丰富的图表。
5 |
6 | > 持续开发中...
7 |
8 | 代码参考了chartjs
9 | 地址: http://www.chartjs.org
10 |
11 | 1. 大幅重构了chartjs的代码
12 | 2. 加入了移动端独有的事件(drag\tap\longTap\doubleTap)
13 | 3. 通过接口+div的渲染,可以打造出一个华丽的移动端图表
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name":"JingleChart",
3 | "version":"0.1.0",
4 | "engines":{
5 | "node":">= 0.8.0"
6 | },
7 | "devDependencies":{
8 | "grunt":"~0.4.0",
9 | "grunt-contrib-concat":"~0.3.0",
10 | "grunt-contrib-copy" : "~0.4.1",
11 | "grunt-contrib-uglify":"~0.2.0",
12 | "connect":""
13 | }
14 | }
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function(grunt){
2 | grunt.initConfig({
3 | pkg : grunt.file.readJSON('package.json'),
4 | concat : {
5 | 'dist/JChart.debug.js' :
6 | ['src/JChart.js','src/*.js']
7 | },
8 | uglify : {
9 | target : {
10 | files : {
11 | 'dist/JChart.min.js': 'dist/JChart.debug.js'
12 | }
13 | }
14 | }
15 |
16 | });
17 | grunt.loadNpmTasks('grunt-contrib-concat');
18 | grunt.loadNpmTasks('grunt-contrib-uglify');
19 |
20 | grunt.registerTask('default', ['concat','uglify']);
21 | }
--------------------------------------------------------------------------------
/JChart.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Jingle Chart",
3 | "title": "Html5 Mobile Chart",
4 | "description": "移动端canvas图表,想做最简单的图表",
5 | "version": "0.1.0",
6 | "homepage": "https://github.com/shixy/JChart",
7 | "author": {
8 | "name": "walker",
9 | "email": "walker.shixy@gmail.com"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "git://github.com/shixy/JChart.git"
14 | },
15 | "bugs": {
16 | "url": "https://github.com/shixy/JChart/issues"
17 | },
18 | "licenses": [
19 | {
20 | "type": "MIT",
21 | "url": "https://github.com/shixy/Jingle/blob/master/LICENSE-MIT"
22 | }
23 | ],
24 | "keywords": []
25 | }
--------------------------------------------------------------------------------
/doc/css/styles.css:
--------------------------------------------------------------------------------
1 |
2 | .chart-title{
3 | text-align: center;
4 | font-weight: bold;
5 | font-size: 16px;
6 | }
7 | .chart-lengend{
8 | border: 1px solid #b9b4b6;
9 | margin:10px auto;
10 | width: 120px;
11 | border-radius:4px;
12 | padding:5px;
13 | display: -webkit-box;
14 | }
15 | .arrow_container{
16 | border-radius: 4px;
17 | border: 1px solid #C0392B;
18 | background: #C0392B;
19 | padding: 5px;
20 | text-align: center;
21 | position: relative;
22 | width:300px;
23 | color: #fff;
24 | display: none;
25 | }
26 | .arrow_container:after{
27 | bottom: 100%;
28 | left: 50%;
29 | margin-left: -20px;
30 | border: solid transparent;
31 | content: " ";
32 | height: 0;
33 | width: 0;
34 | position: absolute;
35 | pointer-events: none;
36 | border-width: 20px;
37 | border-bottom-color: #C0392B;
38 | }
--------------------------------------------------------------------------------
/demo/radar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | radar
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
43 |
44 |
--------------------------------------------------------------------------------
/doc/js/application.js:
--------------------------------------------------------------------------------
1 | !function ($) {
2 |
3 | $(function(){
4 | if (navigator.userAgent.match(/IEMobile\/10\.0/)) {
5 | var msViewportStyle = document.createElement("style");
6 | msViewportStyle.appendChild(
7 | document.createTextNode(
8 | "@-ms-viewport{width:auto!important}"
9 | )
10 | );
11 | document.getElementsByTagName("head")[0].
12 | appendChild(msViewportStyle);
13 | }
14 | var $window = $(window)
15 | var $body = $(document.body)
16 | var navHeight = $('.navbar').outerHeight(true) + 10
17 | $body.scrollspy({
18 | target: '.bs-sidebar',
19 | offset: navHeight
20 | })
21 |
22 | $window.on('load', function () {
23 | $body.scrollspy('refresh')
24 | })
25 |
26 | $('.bs-docs-container [href=#]').click(function (e) {
27 | e.preventDefault()
28 | })
29 |
30 | // back to top
31 | setTimeout(function () {
32 | var $sideBar = $('.bs-sidebar')
33 |
34 | $sideBar.affix({
35 | offset: {
36 | top: function () {
37 | var offsetTop = $sideBar.offset().top
38 | var sideBarMargin = parseInt($sideBar.children(0).css('margin-top'), 10)
39 | var navOuterHeight = $('.bs-docs-nav').height()
40 |
41 | return (this.top = offsetTop - navOuterHeight - sideBarMargin)
42 | }
43 | , bottom: function () {
44 | return (this.bottom = $('.bs-footer').outerHeight(true))
45 | }
46 | }
47 | })
48 | }, 100)
49 |
50 | })
51 |
52 | }(jQuery)
53 |
--------------------------------------------------------------------------------
/demo/bar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Bar
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
45 |
46 |
--------------------------------------------------------------------------------
/demo/polar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Polar
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
55 |
56 |
--------------------------------------------------------------------------------
/demo/line.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Line
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | 折线图
16 |
17 |
18 |
公司产品销售汇总(2012-2014)
19 |
20 |
21 |
22 |
58 |
59 |
--------------------------------------------------------------------------------
/demo/pie.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Pie
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
82 |
83 |
--------------------------------------------------------------------------------
/src/JChart.Bar.js:
--------------------------------------------------------------------------------
1 | ;(function(_){
2 | function Bar(data,cfg){
3 | _.Scale.apply(this);
4 | var barRanges = [];//记录柱状图的占据的位置
5 | this._type_ = 'bar';
6 | var _this = this;
7 | this.data = data;//所有的数据
8 | this.chartData = null;//图表当前展示的数据
9 | //配置项
10 | _.extend(this.config,{
11 | //是否显示bar的边框
12 | showBarBorder : true,
13 | //bar边框宽度
14 | barBorderWidth : 2,
15 | //每两个bar之间的间距
16 | barSpacing : 1,
17 | //每两组bar之间的间距
18 | barSetSpacing : 5,
19 | //是否可以对数据进行拖动
20 | datasetGesture : false,
21 | //每次显示的数据条数
22 | datasetShowNumber : 12
23 | });
24 | /**
25 | * 绑定canvas dom元素上的事件 如:click、touch
26 | */
27 | this.bindEvents = function(){
28 | this.on('_tap',function(x,y){tapHandler(x,y,'tap.bar')});
29 | //this.on('_doubleTap',function(x,y){tapHandler(x,y,'doubleTap.bar')});
30 | this.on('_longTap',function(x,y){tapHandler(x,y,'longTap.bar')});
31 | if(this.config.datasetGesture){
32 | this.bindDataGestureEvent();
33 | }
34 | }
35 | /**
36 | * 初始化部分元素值
37 | */
38 | this.draw = function(noAnim){
39 | if(this.config.datasetGesture && this.data.labels.length > _this.config.datasetShowNumber){
40 | this.chartData = this.sliceData(this.data,0,this.data.labels.length,this.config.datasetShowNumber);
41 | }else{
42 | this.chartData = this.data;
43 | }
44 | this.mergeFont(['scaleFont','textFont']);
45 | this.initScale(true);
46 | if(noAnim){
47 | this.drawScale();
48 | this.drawBars(1);
49 | }else{
50 | this.doAnim(this.drawScale,this.drawBars);
51 | }
52 | }
53 | this.redraw = function(data){
54 | this.chartData = data;
55 | this.clear();
56 | this.initScale(true);
57 | this.drawScale();
58 | this.drawBars(1);
59 | }
60 |
61 | this.drawBars = function(animPc){
62 | if(animPc >= 1)barRanges = [];
63 | var ctx = _this.ctx,cfg = _this.config,scale = _this.scaleData;
64 | _.each(_this.chartData.datasets,function(set,i){
65 | if(!cfg.showBarBorder)borderColor = null;
66 | _.each(set.data,function(d,j){
67 | var x = scale.x + cfg.barSetSpacing + scale.xHop*j + scale.barWidth*i + cfg.barSpacing*i + cfg.barBorderWidth* i,
68 | y = scale.y,width = scale.barWidth,height = animPc*_this.calcOffset(d,scale.yScaleValue,scale.yHop)+(cfg.barBorderWidth/2),
69 | color = set.color,borderColor,bgColor = _.hex2Rgb(color,0.6);
70 | if(cfg.showBarBorder){
71 | //边框颜色默认与设置颜色一致
72 | borderColor = set.borderColor || color;
73 | }
74 | ctx.rect(x,y,width,-height,bgColor,borderColor,cfg.barBorderWidth);
75 | if(animPc >= 1){
76 | barRanges.push([x,x+width,y,y-height,j,i]);
77 | }
78 | cfg.showText && _this.drawText(d,x+width/2,y-height-3,[j,i]);
79 |
80 | });
81 | })
82 | }
83 |
84 | function tapHandler(x,y,event){
85 | var p = isInBarRange(x,y);
86 | if(p){
87 | _this.trigger(event,[_this.chartData.datasets[p[5]].data[p[4]],p[4],p[5]]);
88 | }
89 | }
90 |
91 | function isInBarRange(x,y){
92 | var range;
93 | _.each(barRanges,function(r){
94 | if(x >= r[0] && x <= r[1] && y >= r[3] && y <= r[2]){
95 | range = r;
96 | return false;
97 | }
98 | });
99 | return range;
100 | }
101 | //初始化参数
102 | if(cfg)this.initial(cfg);
103 | }
104 | _.Bar = Bar;
105 | })(JChart)
--------------------------------------------------------------------------------
/doc/js/doc.js:
--------------------------------------------------------------------------------
1 | $(function(){
2 | var data = {
3 | labels : ["2012","二月","三月","四月","五月","六月","七月",'八月','九月','十月','十一月','十二月','2013',"二月","三月","四月","五月","六月","七月",'八月','九月','十月','十一月','十二月','2014','一月','二月'],
4 | datasets : [
5 | {
6 | name : 'A产品',
7 | color : "#72caed",
8 | pointColor : "#95A5A6",
9 | pointBorderColor : "#fff",
10 | data : [65,59,90,81,56,55,40,20,13,20,11,60,65,59,90,81,56,55,40,20,11,20,10,60,11,60,65]
11 | },
12 | {
13 | name : 'B产品',
14 | color : "#a6d854",
15 | pointColor : "#95A5A6",
16 | pointBorderColor : "#fff",
17 | data : [28,48,40,19,96,27,100,40,40,70,11,89,28,48,40,19,96,27,100,40,40,70,10,89,28,48,40]
18 | }
19 | ]
20 | }
21 | new JChart.Line(data,{
22 | id : 'line_canvas',
23 | datasetGesture : true
24 | }).draw();
25 | new JChart.Bar(data,{
26 | id : 'bar_canvas',
27 | datasetGesture : true
28 | }).draw();
29 |
30 | var pie_data = [
31 | {
32 | name : '直接访问',
33 | value: 335,
34 | color:"#F38630"
35 | },{
36 | name : '联盟广告',
37 | value : 234,
38 | color : "#E0E4CC"
39 | },{
40 | name : '视频广告',
41 | value : 135,
42 | color : "#72caed"
43 | },{
44 | name : '搜索引擎',
45 | value : 1400,
46 | color : "#a6d854"
47 | }
48 | ];
49 | new JChart.Pie(pie_data,{
50 | id : 'pie_canvas'
51 | }).draw();
52 | var dount_pie = new JChart.Pie(pie_data,{
53 | id : 'dount_canvas',
54 | clickType : 'rotate',
55 | isDount : true,
56 | dountText : '访问来源'
57 | })
58 | dount_pie.on('rotate',function(d){
59 | var $pop = $(this.ctx.el).next();
60 | $pop.html(d.name + '
' + d.value).show();
61 | })
62 | dount_pie.draw();
63 |
64 | new JChart.Pie(pie_data,{
65 | id : 'sector_canvas_1',
66 | clickType : null,
67 | startAngle : -Math.PI,
68 | totalAngle : Math.PI
69 | }).draw();
70 | new JChart.Pie(pie_data,{
71 | id : 'sector_canvas_2',
72 | clickType : null,
73 | isDount : true,
74 | dountText : '访问来源',
75 | startAngle : Math.PI*3/4,
76 | totalAngle : Math.PI*3/4*2
77 | }).draw();
78 |
79 |
80 | var radar_data = {
81 | labels : ['外观','功能','系统','屏幕','性能'],
82 | datasets : [
83 | {
84 | color : "#72caed",
85 | pointColor : "#95A5A6",
86 | pointStrokeColor : "#fff",
87 | data : [90,50,60,71,54]
88 | },
89 | {
90 | color : "#a6d854",
91 | pointColor : "#95A5A6",
92 | pointStrokeColor : "#fff",
93 | data : [70,64,83,54,93]
94 | }
95 | ]
96 | }
97 | new JChart.Radar(radar_data,'radar_canvas').draw();
98 |
99 | var polar_data = {
100 | labels : [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23],
101 | datasets : [
102 | {value: 3.14,color:"#F38630"},
103 | {value: 1.98,color:"#F38630"},
104 | {value: 1.32,color:"#F38630"},
105 | {value: 0.98,color:"#F38630"},
106 | {value: 0.81,color:"#F38630"},
107 | {value: 0.79,color:"#F38630"},
108 | {value: 1.07,color:"#E0E4CC"},
109 | {value: 1.84,color:"#E0E4CC"},
110 | {value: 3.35,color:"#E0E4CC"},
111 | {value: 4.61,color:"#E0E4CC"},
112 | {value: 5.27,color:"#E0E4CC"},
113 | {value: 5.39,color:"#E0E4CC"},
114 | {value: 5.39,color:"#72caed"},
115 | {value: 5.56,color:"#72caed"},
116 | {value: 5.60,color:"#72caed"},
117 | {value: 5.76,color:"#72caed"},
118 | {value: 5.86,color:"#72caed"},
119 | {value: 5.59,color:"#72caed"},
120 | {value: 5.56,color:"#a6d854"},
121 | {value: 6.19,color:"#a6d854"},
122 | {value: 6.60,color:"#a6d854"},
123 | {value: 6.60,color:"#a6d854"},
124 | {value: 6.05,color:"#a6d854"},
125 | {value: 4.70,color:"#a6d854"}
126 | ]
127 | };
128 |
129 | new JChart.Polar(polar_data,{
130 | id : 'polar_canvas',
131 | borderWidth : 1,
132 | showScaleLabel : false
133 | }).draw();
134 | });
--------------------------------------------------------------------------------
/src/JChart.Line.js:
--------------------------------------------------------------------------------
1 | ;(function(_){
2 | function Line(data,cfg){
3 | _.Scale.apply(this);
4 | var pointRanges = [];//记录线的节点位置 (for click 事件)
5 | this._type_ = 'line';
6 | this.data = data;
7 | this.chartData = null;
8 | var _this = this;
9 | _.extend(this.config,{
10 | //平滑曲线
11 | smooth : true,
12 | //是否显示线的连接点
13 | showPoint : true,
14 | //连接圆点半径
15 | pointRadius : 4,
16 | //连接点的边框宽度
17 | pointBorderWidth : 2,
18 | //连接点的点击范围(方便手指触摸)
19 | pointClickBounds : 20,
20 | //连接线的宽度
21 | lineWidth : 2,
22 | //是否填充为面积图
23 | fill : true,
24 | //是否可以对数据进行拖动
25 | datasetGesture : false,
26 | //每次显示的数据条数
27 | datasetShowNumber : 12
28 | });
29 | /**
30 | * 绑定canvas dom元素上的事件 如:click、touch
31 | */
32 | this.bindEvents = function(){
33 | //this.ctx.canvas.addEventListener('click',tapHandler);
34 | this.on('_tap',tapHandler);
35 | if(this.config.datasetGesture){
36 | this.bindDataGestureEvent();
37 | }
38 | }
39 | /**
40 | * 初始化部分元素值
41 | */
42 | this.draw = function(noAnim){
43 | this.mergeFont(['textFont','scaleFont']);
44 | if(this.config.datasetGesture && this.data.labels.length > _this.config.datasetShowNumber){
45 | this.chartData = this.sliceData(this.data,0,this.data.labels.length,this.config.datasetShowNumber);
46 | }else{
47 | this.chartData = this.data;
48 | }
49 | _this.initScale(true);
50 | if(noAnim){
51 | this.drawScale();
52 | this.drawLines(1);
53 | }else{
54 | this.doAnim(this.drawScale,this.drawLines);
55 | }
56 | }
57 | this.redraw = function(data){
58 | this.chartData = data;
59 | this.clear();
60 | this.initScale(true);
61 | this.drawScale();
62 | this.drawLines(1);
63 | }
64 | this.drawLines = function(animPc){
65 | if(animPc >= 1)pointRanges = [];
66 | var ctx = _this.ctx,cfg = _this.config,dataset = _this.chartData.datasets,scale = _this.scaleData;
67 | _.each(dataset,function(set,i){
68 | //画连接线
69 | ctx.beginPath().moveTo(scale.x, yPos(i,0));
70 | _.each(set.data,function(d,j){
71 | var pointX = xPos(j),pointY = yPos(i,j);
72 | if (cfg.smooth){//贝塞尔曲线
73 | ctx.bezierCurveTo(xPos(j-0.5),yPos(i,j-1),xPos(j-0.5),pointY,pointX,pointY);
74 | }else{
75 | ctx.lineTo(pointX,pointY);
76 | }
77 | if(animPc >= 1){
78 | pointRanges.push([pointX,pointY,j,i]);
79 | }
80 | });
81 | ctx.stroke(set.color,cfg.lineWidth);
82 | //填充区域
83 | cfg.fill ? ctx.lineTo(scale.x + (scale.xHop*(set.data.length-1)),scale.y).lineTo(scale.x,scale.y).closePath()
84 | .fill(set.fillColor?set.fillColor : _.hex2Rgb(set.color,0.6)) : ctx.closePath();
85 |
86 | //画点以及点上文本
87 | _.each(set.data,function(d,k){
88 | var x = xPos(k),y = yPos(i,k);
89 | cfg.showPoint && _this.drawPoint(x,y,set);
90 | cfg.showText && _this.drawText(d,x,y-3,[k,i]);
91 | });
92 | });
93 |
94 | function yPos(i,j){
95 | return scale.y - animPc*(_this.calcOffset(dataset[i].data[j],scale.yScaleValue,scale.yHop));
96 | }
97 | function xPos(i){
98 | return scale.x + (scale.xHop * i);
99 | }
100 | }
101 | function tapHandler(x,y){
102 | var p = isInPointRange(x,y);
103 | if(p){
104 | _this.trigger('tap.point',[_this.chartData.datasets[p[3]].data[p[2]],p[2],p[3]]);
105 | }
106 | }
107 |
108 | function isInPointRange(x,y){
109 | var point,pb = _this.config.pointClickBounds;
110 | _.each(pointRanges,function(p){
111 | if(x >= p[0] - pb && x <= p[0] + pb && y >= p[1]-pb && y <= p[1] + pb){
112 | point = p;
113 | return false;
114 | }
115 | });
116 | return point;
117 | }
118 |
119 | //初始化参数
120 | if(cfg)this.initial(cfg);
121 | }
122 | _.Line = Line;
123 | })(JChart)
--------------------------------------------------------------------------------
/doc/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | JingleChart - 移动端专属。极简的图表控件,简单的图形+合适的接口,打造交互丰富的图表
12 |
13 |
14 |
15 |
16 |
17 |
41 |
42 |
43 | Skip to main content
44 |
45 |
71 |
72 |
73 |
74 |
75 |
JChartV0.1
76 |
77 | Jingle Chart(JChart)是一个极为简单的图表,为移动端而生。
78 |
79 |
80 |
轻量级
81 |
体积轻量:不依赖任何js框架,体积min29k。
82 |
图形轻量:干净的图表,没有标题,没有图例。移动端屏幕太小,放不下这么多东西,这些要放哪里,怎么展示,html可以帮到您。
83 |
84 |
85 |
支持移动端的各种手势
86 |
触摸、长按、拖动等等各种手势(其他手势效果待开发... ^_^)
87 |
88 |
89 |
丰富的接口、事件
90 |
让你的图表动起来,看api吧....
91 |
92 |
93 |
开源免费
94 |
MIT. Enjoy it!!
95 |
96 |
97 |
JChart 才刚开始开发,还非常不完善,正努力开发中...
98 |
感谢您的关注!
99 |
100 |
101 | 下载JChart
102 | Github
103 |
104 |
105 |
117 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
--------------------------------------------------------------------------------
/doc/js/holder.min.js:
--------------------------------------------------------------------------------
1 | var Holder=Holder||{};!function(a,b){function f(a,b){var c="complete",d="readystatechange",e=!1,f=e,g=!0,h=a.document,i=h.documentElement,j=h.addEventListener?"addEventListener":"attachEvent",k=h.addEventListener?"removeEventListener":"detachEvent",l=h.addEventListener?"":"on",m=function(g){(g.type!=d||h.readyState==c)&&(("load"==g.type?a:h)[k](l+g.type,m,e),!f&&(f=!0)&&b.call(a,null))},n=function(){try{i.doScroll("left")}catch(a){return setTimeout(n,50),void 0}m("poll")};if(h.readyState==c)b.call(a,"lazy");else{if(h.createEventObject&&i.doScroll){try{g=!a.frameElement}catch(o){}g&&n()}h[j](l+"DOMContentLoaded",m,e),h[j](l+d,m,e),a[j](l+"load",m,e)}}function g(a){a=a.match(/^(\W)?(.*)/);var b=document["getElement"+(a[1]?"#"==a[1]?"ById":"sByClassName":"sByTagName")](a[2]),c=[];return null!=b&&(c=b.length?b:0==b.length?b:[b]),c}function h(a,b){var c={};for(var d in a)c[d]=a[d];for(var e in b)c[e]=b[e];return c}function k(a,b,c){b=parseInt(b,10),a=parseInt(a,10);var d=Math.max(b,a),e=Math.min(b,a),f=1/12,g=Math.min(.75*e,.75*d*f);return{height:Math.round(Math.max(c.size,g))}}function l(a,b,c,d){var f=k(b.width,b.height,c),g=f.height,h=b.width*d,i=b.height*d,j=c.font?c.font:"sans-serif";e.width=h,e.height=i,a.textAlign="center",a.textBaseline="middle",a.fillStyle=c.background,a.fillRect(0,0,h,i),a.fillStyle=c.foreground,a.font="bold "+g+"px "+j;var l=c.text?c.text:Math.floor(b.width)+"x"+Math.floor(b.height),m=a.measureText(l).width;return m/h>=.75&&(g=Math.floor(.75*g*(h/m))),a.font="bold "+g*d+"px "+j,a.fillText(l,h/2,i/2,h),e.toDataURL("image/png")}function m(a,b,c,e){var f=c.dimensions,g=c.theme,i=c.text?decodeURIComponent(c.text):c.text,j=f.width+"x"+f.height;g=i?h(g,{text:i}):g,g=c.font?h(g,{font:c.font}):g,"image"==a?(b.setAttribute("data-src",e),b.setAttribute("alt",i?i:g.text?g.text+" ["+j+"]":j),(d||!c.auto)&&(b.style.width=f.width+"px",b.style.height=f.height+"px"),d?b.style.backgroundColor=g.background:b.setAttribute("src",l(p,f,g,s))):"background"==a?d||(b.style.backgroundImage="url("+l(p,f,g,s)+")",b.style.backgroundSize=f.width+"px "+f.height+"px"):"fluid"==a&&(b.setAttribute("data-src",e),b.setAttribute("alt",i?i:g.text?g.text+" ["+j+"]":j),b.style.height="%"==f.height.substr(-1)?f.height:f.height+"px",b.style.width="%"==f.width.substr(-1)?f.width:f.width+"px",("inline"==b.style.display||""==b.style.display)&&(b.style.display="block"),d?b.style.backgroundColor=g.background:(b.holderData=c,t.push(b),n(b)))}function n(a){var b;b=null==a.nodeType?t:[a];for(i in b){var c=b[i];if(c.holderData){var d=c.holderData;c.setAttribute("src",l(p,{height:c.clientHeight,width:c.clientWidth},d.theme,s))}}}function o(b,c){var d={theme:u.themes.gray},e=!1;for(sl=b.length,j=0;sl>j;j++){var f=b[j];a.flags.dimensions.match(f)?(e=!0,d.dimensions=a.flags.dimensions.output(f)):a.flags.fluid.match(f)?(e=!0,d.dimensions=a.flags.fluid.output(f),d.fluid=!0):a.flags.colors.match(f)?d.theme=a.flags.colors.output(f):c.themes[f]?d.theme=c.themes[f]:a.flags.text.match(f)?d.text=a.flags.text.output(f):a.flags.font.match(f)?d.font=a.flags.font.output(f):a.flags.auto.match(f)&&(d.auto=!0)}return e?d:!1}var c=!1,d=!1,e=document.createElement("canvas");if(document.getElementsByClassName||(document.getElementsByClassName=function(a){var c,d,e,b=document,f=[];if(b.querySelectorAll)return b.querySelectorAll("."+a);if(b.evaluate)for(d=".//*[contains(concat(' ', @class, ' '), ' "+a+" ')]",c=b.evaluate(d,b,null,0,null);e=c.iterateNext();)f.push(e);else for(c=b.getElementsByTagName("*"),d=new RegExp("(^|\\s)"+a+"(\\s|$)"),e=0;ee;e++){var h=document.createElement("img");h.setAttribute("data-src",b),d[e].appendChild(h)}return a},a.run=function(b){var d=h(u,b),e=[],f=[],i=[];for("string"==typeof d.images?f=g(d.images):window.NodeList&&d.images instanceof window.NodeList?f=d.images:window.Node&&d.images instanceof window.Node&&(f=[d.images]),"string"==typeof d.bgnodes?i=g(d.bgnodes):window.NodeList&&d.elements instanceof window.NodeList?i=d.bgnodes:window.Node&&d.bgnodes instanceof window.Node&&(i=[d.bgnodes]),c=!0,n=0,l=f.length;l>n;n++)e.push(f[n]);var j=document.getElementById("holderjs-style");j||(j=document.createElement("style"),j.setAttribute("id","holderjs-style"),j.type="text/css",document.getElementsByTagName("head")[0].appendChild(j)),d.nocss||(j.styleSheet?j.styleSheet.cssText+=d.stylesheet:j.appendChild(document.createTextNode(d.stylesheet)));for(var k=new RegExp(d.domain+'/(.*?)"?\\)'),l=i.length,n=0;l>n;n++){var p=window.getComputedStyle(i[n],null).getPropertyValue("background-image"),q=p.match(k),r=i[n].getAttribute("data-background-src");if(q){var s=o(q[1].split("/"),d);s&&m("background",i[n],s,p)}else if(null!=r){var s=o(r.substr(r.lastIndexOf(d.domain)+d.domain.length+1).split("/"),d);s&&m("background",i[n],s,p)}}for(l=e.length,n=0;l>n;n++){var t=attr_data_src=p=null;try{t=e[n].getAttribute("src"),attr_datasrc=e[n].getAttribute("data-src")}catch(v){}if(null==attr_datasrc&&t&&t.indexOf(d.domain)>=0?p=t:attr_datasrc&&attr_datasrc.indexOf(d.domain)>=0&&(p=attr_datasrc),p){var s=o(p.substr(p.lastIndexOf(d.domain)+d.domain.length+1).split("/"),d);s&&(s.fluid?m("fluid",e[n],s,p):m("image",e[n],s,p))}}return a},f(b,function(){window.addEventListener?(window.addEventListener("resize",n,!1),window.addEventListener("orientationchange",n,!1)):window.attachEvent("onresize",n),c||a.run()}),"function"==typeof define&&define.amd&&define("Holder",[],function(){return a})}(Holder,window);
--------------------------------------------------------------------------------
/src/JChart.Polar.js:
--------------------------------------------------------------------------------
1 | ;(function(_){
2 | function Polar(data,cfg){
3 | _.Scale.apply(this);
4 | var _this = this;
5 | this.data = this.chartData = data;
6 | //配置项
7 | _.extend(this.config,{
8 | drawScaleFirst : false,
9 | //是否显示刻度文本背景
10 | showScaleLabelBackdrop : true,
11 | //刻度背景颜色
12 | scaleBackdropColor : "rgba(255,255,255,0.75)",
13 | //刻度padding-top bottom
14 | scaleBackdropPaddingY : 2,
15 | //刻度padding-left right
16 | scaleBackdropPaddingX : 2,
17 | //是否显示角度分割线
18 | showAngleLine : true,
19 | //分割线颜色
20 | angleLineColor : "rgba(0,0,0,.1)",
21 | showBorder : true,
22 | borderColor : '#fff',
23 | borderWidth : 1,
24 | textFont : {
25 | size : 16,
26 | color : '#666',
27 | textBaseline : 'middle'
28 | },
29 | //分割线宽度
30 | angleLineWidth : 1,
31 | //是否开启旋转动画
32 | animateRotate : true,
33 | //是否开启缩放动画
34 | animateScale : false
35 | });
36 | /**
37 | * 绑定canvas dom元素上的事件
38 | */
39 | this.bindEvents = function(){
40 | this.on('_tap',tapHandler);
41 | }
42 |
43 | this.draw = function(noAnim){
44 | this.mergeFont(['scaleFont','textFont']);
45 | this.initScale();
46 | if(noAnim){
47 | this.drawAllSegments(1);
48 | this.drawScale();
49 | }
50 | this.doAnim(this.drawScale,this.drawAllSegments);
51 | }
52 | function tapHandler(x,y){
53 | var i = isInSegment(x,y);
54 | if(i>-1){
55 | this.trigger('tap.pie',[this.data[i],i]);
56 | }
57 | }
58 | this.calcDrawingSizes = function(){
59 | var maxSize = Math.min(this.width,this.height)/2,
60 | cfg = this.config,size = cfg.scaleFont.size,lh = size*2;
61 |
62 | maxSize -= Math.max(size*0.5,cfg.scaleLineWidth*0.5);
63 | if (cfg.showScaleLabelBackdrop){
64 | lh += (2 * cfg.scaleBackdropPaddingY);
65 | maxSize -= cfg.scaleBackdropPaddingY*1.5;
66 | }
67 | this.scaleData.yHeight = maxSize - 10;
68 | this.scaleData.yLabelHeight = lh;
69 | }
70 |
71 | this.drawScale = function(){
72 | var cfg = this.config,scale = this.scaleData,x = this.width/2, y = this.height/2
73 | size = cfg.scaleFont.size,px = cfg.scaleBackdropPaddingX,py = cfg.scaleBackdropPaddingY;
74 | this.ctx.save().translate(x,y);
75 |
76 | //画圆圈
77 | for (var i=0; i upperValue) {upperValue = data[i].value;}
137 | if (data[i].value < lowerValue) {lowerValue = data[i].value;}
138 | };
139 | var yh = this.scaleData.yHeight;
140 | var lh = this.scaleData.yLabelHeight;
141 | var maxSteps = Math.floor((yh/(lh*0.66)));
142 | var minSteps = Math.floor((yh/lh*0.5));
143 | return {
144 | maxValue : upperValue,
145 | minValue : lowerValue,
146 | maxSteps : maxSteps,
147 | minSteps : minSteps
148 | };
149 | }
150 |
151 | function isInSegment(x,y){
152 | var startAngle = -Math.PI/2,
153 | angleStep = (Math.PI*2)/this.data.length;
154 | var x = x-_this.width/ 2,y = y-_this.height/2;
155 | //距离圆点的距离
156 | var dfc = Math.sqrt( Math.pow( Math.abs(x), 2 ) + Math.pow( Math.abs(y), 2 ) );
157 | var isInPie = (dfc <= _this.scaleData.yHeight);
158 | if(!isInPie)return -1;
159 | var clickAngle = Math.atan2(y, x)-startAngle;
160 | if ( clickAngle < 0 ) clickAngle = 2 * Math.PI + clickAngle;
161 | if(clickAngle > 2 * Math.PI) clickAngle = clickAngle - 2 * Math.PI;
162 | return Math.floor(clickAngle/angleStep);
163 | }
164 |
165 | //初始化参数
166 | if(cfg)this.initial(cfg);
167 |
168 | }
169 | _.Polar = Polar;
170 | })(JChart);
--------------------------------------------------------------------------------
/src/JChart.Radar.js:
--------------------------------------------------------------------------------
1 | ;(function (_) {
2 | function Radar(data, cfg) {
3 | _.Scale.apply(this);
4 | var pointRanges = [];//记录线的节点位置 (for click 事件)
5 | var _this = this;
6 | this.data = this.chartData = data;
7 | //配置项
8 | _.extend(this.config, {
9 | drawScaleFirst : false,
10 | //是否显示刻度文本背景
11 | scaleShowLabelBackdrop:true,
12 | //刻度背景颜色
13 | scaleBackdropColor:"rgba(255,255,255,0.75)",
14 | //刻度padding-top bottom
15 | scaleBackdropPaddingY:2,
16 | //刻度padding-left right
17 | scaleBackdropPaddingX:2,
18 | //图形形状,菱形 diamond,圆形 circle
19 | graphShape:'circle',
20 | //是否显示角度分割线
21 | showAngleLine:true,
22 | //角度分割线颜色
23 | angleLineColor:"rgba(0,0,0,.1)",
24 | //角度分割线宽度
25 | angleLineWidth:1,
26 | //是否显示线的连接点
27 | showPoint:true,
28 | //连接圆点半径
29 | pointRadius:3,
30 | //连接点的边框宽度
31 | pointBorderWidth:1,
32 | //连接点的点击范围(方便手指触摸)
33 | pointClickBounds:20,
34 | //连接线的宽度
35 | lineWidth:2,
36 | //是否填充为面积图
37 | fill:true,
38 | showScaleLabel:true,
39 | showText : false,
40 | gridLineColor:'rgb(0,0,0,.5)'
41 | });
42 |
43 | /**
44 | * 绑定canvas dom元素上的事件 如:click、touch
45 | */
46 | this.bindEvents = function () {
47 | this.on('_tap', tapHandler);
48 | }
49 |
50 | this.draw = function (noAnim) {
51 | this.mergeFont(['scaleFont','textFont']);
52 | this.initScale();
53 | if (noAnim) {
54 | this.drawAllDataPoints(1);
55 | this.drawScale();
56 | } else {
57 | this.doAnim(this.drawScale, this.drawAllDataPoints);
58 | }
59 | }
60 |
61 | function tapHandler(x, y) {
62 | var p = isInPointRange(x,y);
63 | if(p){
64 | _this.trigger('tap.point',[_this.data.datasets[p[3]].data[p[2]],p[2],p[3]]);
65 | }
66 | }
67 |
68 | this.calcDrawingSizes = function () {
69 | var maxSize = (Math.min(this.width, this.height) / 2),
70 | cfg = this.config,
71 | labelHeight = cfg.scaleFont.size * 2;
72 | var labelLength = 0;
73 | _.each(_this.data.labels, function (label) {
74 | this.ctx.set(cfg.textFont);
75 | var w = this.ctx.measureText(label).width;
76 | if (w > labelLength) labelLength = w;
77 | }, this);
78 | maxSize -= Math.max(labelLength, ((cfg.textFont.size/2) * 1.5));
79 | maxSize -= cfg.textFont.size;
80 | maxSize = _.capValue(maxSize, null, 0);
81 | this.scaleData.yHeight = maxSize;
82 | this.scaleData.yLabelHeight = labelHeight;
83 | }
84 |
85 | this.drawScale = function () {
86 | var ctx = this.ctx, cfg = this.config, scale = this.scaleData,scaleSize = cfg.scaleFont.size,textSize = cfg.textFont.size,
87 | dataLen = this.data.labels.length,px = cfg.scaleBackdropPaddingX,py = cfg.scaleBackdropPaddingY;
88 | //计算每条数据的角度
89 | var rotationDegree = (2 * Math.PI) / dataLen;
90 | ctx.save().translate(this.width / 2, this.height / 2);
91 | //显示角度分割线
92 | if (cfg.showAngleLine) {
93 | var w = scale.yHeight - (scale.yHeight % scale.yHop);
94 | //画每个角度的分割线
95 | for (var h = 0; h < dataLen; h++) {
96 | ctx.rotate(rotationDegree).line(0,0,0,-w,cfg.angleLineColor,cfg.angleLineWidth);
97 | }
98 | }
99 | //画刻度线
100 | for (var i = 0; i < scale.yScaleValue.step; i++) {
101 | var hop = scale.yHop * (i + 1);
102 | ctx.beginPath();
103 | if (cfg.showGridLine) {
104 | ctx.set({strokeStyle : cfg.gridLineColor,lineWidth : cfg.gridLineWidth})
105 | if (cfg.graphShape == 'diamond') {
106 | ctx.moveTo(0, -hop);
107 | for (var j = 0; j < dataLen; j++) {
108 | ctx.rotate(rotationDegree).lineTo(0, -hop);
109 | }
110 | } else {
111 | ctx.circle(0, 0, hop);
112 | }
113 | ctx.closePath().stroke();
114 | }
115 | //画刻度值
116 | if (cfg.showScaleLabel) {
117 | var label = scale.yScaleValue.labels[i];
118 | //显示刻度值的背景
119 | if (cfg.showScaleLabelBackdrop){
120 | var textWidth = this.ctx.measureText(label).width;
121 | this.ctx.rect(
122 | Math.round(-textWidth/2 - px), //X
123 | Math.round(-hop - scaleSize/2 - py),//Y
124 | Math.round(textWidth + px*2), //Width
125 | Math.round(scaleSize + py*2), //Height
126 | cfg.scaleBackdropColor
127 | );
128 | }
129 | this.ctx.fillText(label,0,-hop,cfg.scaleFont);
130 | }
131 |
132 | }
133 |
134 | //设置文本样式
135 | this.ctx.set(cfg.textFont);
136 | //显示数据标签文本
137 | for (var k = 0; k < dataLen; k++) {
138 | var opposite = Math.sin(rotationDegree * k) * (scale.yHeight + textSize);
139 | var adjacent = Math.cos(rotationDegree * k) * (scale.yHeight + textSize);
140 | var align;
141 | if (rotationDegree * k == Math.PI || rotationDegree * k == 0) {
142 | align = 'center';
143 | } else if (rotationDegree * k > Math.PI) {
144 | align = 'right';
145 | }else {
146 | align = 'left';
147 | }
148 | this.ctx.fillText(this.data.labels[k], opposite, -adjacent,{textAlign:align});
149 | }
150 | ctx.restore();
151 | }
152 |
153 | this.drawAllDataPoints = function (animPc) {
154 | if (animPc >= 1)pointRanges = [];
155 | var dataLen = data.datasets[0].data.length,
156 | rotationDegree = (2 * Math.PI) / dataLen,
157 | scale = this.scaleData,
158 | ctx = this.ctx, cfg = this.config;
159 | ctx.save().translate(this.width / 2, this.height / 2);
160 | _.each(this.data.datasets, function (set, i) {
161 | ctx.beginPath().moveTo(0, getY(set.data[0]));
162 | //画连接线
163 | _.each(set.data, function (d, j) {
164 | if (j == 0)return true;
165 | ctx.rotate(rotationDegree).lineTo(0, getY(d));
166 | });
167 | ctx.closePath();
168 |
169 | cfg.fill && ctx.fill(set.fillColor||_.hex2Rgb(set.color,0.6));
170 | ctx.stroke(set.color,cfg.lineWidth);
171 |
172 | //画连接点
173 | _.each(set.data,function(d,j){
174 | var y = getY(d);
175 | if (cfg.showPoint) {
176 | ctx.rotate(rotationDegree).circle(0, y, cfg.pointRadius,set.pointColor,set.pointBorderColor,cfg.pointBorderWidth);
177 | }
178 | if(animPc >= 1){
179 | var p = getPosition(y,j);
180 | pointRanges.push([p[0],p[1],j,i]);
181 | }
182 | });
183 | ctx.rotate(rotationDegree);
184 |
185 | }, this);
186 | ctx.restore();
187 | if(cfg.showText){
188 | drawText();
189 | }
190 | function getY(d){
191 | return -animPc * _this.calcOffset(d, scale.yScaleValue, scale.yHop);
192 | }
193 | function getPosition(radius,i){
194 | radius = Math.abs(radius);
195 | var x,y;
196 | var angel = -Math.PI/2 + i * rotationDegree;
197 | x = Math.cos(angel)*radius + _this.width/2;
198 | y = Math.sin(angel)*radius + _this.height/2;
199 | return [x,y];
200 | }
201 | }
202 |
203 | function drawText(){
204 | _.each(pointRanges,function(p){
205 | var y = p[1];
206 | if(y > _this.height/2){
207 | y += 6;
208 | }
209 | _this.drawText(_this.data.datasets[p[3]].data[p[2]],p[0],y,[p[2],p[3]]);
210 | });
211 |
212 | }
213 |
214 | function isInPointRange(x,y){
215 | var point,pb = _this.config.pointClickBounds;
216 | _.each(pointRanges,function(p){
217 | if(x >= p[0] - pb && x <= p[0] + pb && y >= p[1]-pb && y <= p[1] + pb){
218 | point = p;
219 | return false;
220 | }
221 | });
222 | return point;
223 | }
224 | //初始化参数
225 | if (cfg)this.initial(cfg);
226 | }
227 | _.Radar = Radar;
228 | })(JChart);
--------------------------------------------------------------------------------
/src/JChart.Pie.js:
--------------------------------------------------------------------------------
1 | ;(function(_){
2 | function Pie(data,cfg){
3 | _.Chart.apply(this);
4 | var angleRanges;//记录每个扇形的起始角度(从0开始)
5 | var _this = this;
6 | this.data = data;
7 | var radius,totalData = 0,startAngle = 0,rotateAngle = 0,currentOutIndex = -1,origin = {};
8 | //覆盖配置项
9 | _.extend(this.config,{
10 | //border
11 | showBorder : true,
12 | //border color
13 | borderColor : "#fff",
14 | //border width
15 | borderWidth : 2,
16 | //开始角度,默认为12点钟方向
17 | startAngle : -Math.PI/2,
18 | //旋转扇形,使其中线对应的角度
19 | rotateAngle : Math.PI/2,
20 | //扇形弹出距离
21 | pullOutDistance : 10,
22 | //点击扇形默认触发的事件类型
23 | clickType : 'pullOut',// pullOut||rotate
24 | //环形图
25 | isDount : false,
26 | dountRadiusPercent :0.4,
27 | totalAngle : Math.PI*2,
28 | dountText : '',
29 | dountFont : {
30 | size : 20,
31 | style : 600,
32 | color : '#3498DB'
33 | }
34 | });
35 | /**
36 | * 计算各个扇形的起始角度
37 | * @param data
38 | */
39 | function calcAngel(){
40 | var angle = 0;
41 | angleRanges = [];
42 | _.each(_this.data,function(d,i){
43 | var start = angle;
44 | var percent = d.value/totalData;
45 | angle = angle + percent * _this.config.totalAngle;
46 | var end = angle;
47 | angleRanges.push([start,end,d,i,percent]);
48 | })
49 | }
50 |
51 | function animRotate(percent){
52 | drawPie(percent,'rotate');
53 | }
54 |
55 | /**
56 | * 画饼图
57 | * @param percent 动画比例
58 | */
59 | function drawPie (percent,type){
60 | _this.clear();
61 | percent = _this.config.animation ? percent : 1;
62 | _.each(angleRanges,function(a){
63 | drawSector(a,percent,type);
64 | });
65 | _this.config.isDount && _this.config.dountText && drawDountText();
66 | }
67 |
68 | /**
69 | * 计算扇形真实的角度
70 | */
71 | function calcSectorAngle(r,p,t){
72 | var start = r[0],end = r[1];
73 | if(t == 'rotate'){
74 | //旋转
75 | start = start + startAngle + rotateAngle*p;
76 | end = end + startAngle + rotateAngle*p;
77 | }else{
78 | //默认动画
79 | start = start*p + startAngle;
80 | end = end*p + startAngle
81 | }
82 | return {
83 | start : start,
84 | end : end
85 | }
86 | }
87 |
88 | /**
89 | * 画扇形
90 | * @param i
91 | * @param animPercent
92 | */
93 | function drawSector(r,p,t){
94 | var x = origin.x,y = origin.y,cfg = _this.config,
95 | index = r[3],angle = calcSectorAngle(r,p,t);
96 |
97 | if(index == currentOutIndex){
98 | var midAngle = (r[0] + r[1])/2+startAngle;
99 | x += Math.cos(midAngle) * cfg.pullOutDistance;
100 | y += Math.sin(midAngle) * cfg.pullOutDistance;
101 | }
102 | if(cfg.isDount){
103 | _this.ctx.dountSector(x,y,radius*cfg.dountRadiusPercent,radius,angle.start,angle.end,_this.data[index].color);
104 | }else{
105 | _this.ctx.sector(x,y,radius,angle.start,angle.end,_this.data[index].color);
106 | }
107 | cfg.showBorder && _this.ctx.stroke(cfg.borderColor,cfg.borderWidth);
108 | cfg.showText && drawText(x,y,radius,angle.start,angle.end,r);
109 | }
110 |
111 | function drawText(x,y,r,start,end,data){
112 | //计算文本位置
113 | var middAngle = (start+end)/ 2, dis = r/ 2,
114 | percent = data[4],d = data[2];
115 | if(_this.config.isDount){
116 | dis = r/2 + r*_this.config.dountRadiusPercent/2;
117 | }
118 | percent = (percent * 100).toFixed(1)+'%';
119 | var xaxis = Math.cos(middAngle) * dis + x, yaxis = Math.sin(middAngle) * dis + y;
120 | _this.drawText(percent,xaxis,yaxis,[d,data[3],data[4]]);
121 | }
122 | function drawDountText(){
123 | _this.ctx.fillText(_this.config.dountText,origin.x,origin.y,_this.config.dountFont);
124 | }
125 | /**
126 | * 绑定canvas dom元素上的事件 如:click、touch
127 | */
128 | this.bindEvents = function(){
129 | this.on('_tap',function(x,y){tapHandler(x,y,'tap.pie')});
130 | //暂时关闭doubleTap事件
131 | //this.on('_doubleTap',function(x,y){tapHandler(x,y,'doubleTap.pie')});
132 | this.on('_longTap',function(x,y){tapHandler(x,y,'longTap.pie')});
133 | //添加一个默认点击事件
134 | this.on('tap.pie',function(){return true;})
135 | }
136 |
137 | function tapHandler(x,y,event){
138 | var type = _this.config.clickType;
139 | var angle = isInSegment(x,y);
140 | if(angle){
141 | if(event == 'tap.pie'){//处理一些默认行为
142 | if(!_this.trigger(event,[angle[2],angle[3]]))return;
143 | if(type == 'rotate'){
144 | _this.rotate(angle[3]);
145 | }else if(type == 'pullOut'){
146 | _this.toggleSegment(angle[3]);
147 | }
148 | }else{
149 | _this.trigger(type,[angle[2],angle[3]]);
150 | }
151 | }
152 | }
153 |
154 | function isInSegment(offsetX,offsetY){
155 | var angle;
156 | var x = offsetX - origin.x;
157 | var y = offsetY - origin.y;
158 | //距离圆点的距离
159 | var dfc = Math.sqrt( Math.pow( Math.abs(x), 2 ) + Math.pow( Math.abs(y), 2 ) );
160 | var isInPie = (dfc <= radius);
161 | if(isInPie && _this.config.isDount){//排除dount图中心区
162 | isInPie = (dfc >= radius*_this.config.dountRadiusPercent);
163 | }
164 | if(!isInPie)return;
165 | var clickAngle = Math.atan2(y, x)-startAngle;
166 | if ( clickAngle < 0 ) clickAngle += 2 * Math.PI;
167 | if(clickAngle > 2 * Math.PI) clickAngle -= 2 * Math.PI;
168 |
169 | _.each(angleRanges,function(a){
170 | if(clickAngle >= a[0] && clickAngle < a[1]){
171 | angle = a;
172 | return false;
173 | }
174 | });
175 | return angle;
176 | }
177 |
178 | /**
179 | * 弹出/收起扇形块
180 | * @param i 扇形索引
181 | */
182 | this.toggleSegment = function(i){
183 | if(i == currentOutIndex){
184 | this.pushIn();
185 | }else{
186 | this.pullOut(i);
187 | }
188 | }
189 | /**
190 | * 收起所有弹出的扇形块
191 | */
192 | this.pushIn = function(){
193 | currentOutIndex = -1;
194 | drawPie(1);
195 | this.trigger('pushIn');
196 | }
197 | /**
198 | * 弹出指定的扇形块
199 | * @param i 扇形索引
200 | */
201 | this.pullOut = function(i){
202 | if ( currentOutIndex == i ) return;
203 | currentOutIndex = i;
204 | drawPie(1);
205 | this.trigger('pullOut',[_this.data[i],i,angleRanges[i][4]]);
206 | }
207 | /**
208 | * 旋转扇形块的中线指向6点钟方向
209 | * @param i 扇形索引
210 | */
211 | this.rotate = function(i){
212 | if(_this.isAnimating)return;
213 | var middAngle = (angleRanges[i][0] + angleRanges[i][1]) / 2 + startAngle;
214 | var newRotateAngle = _this.config.rotateAngle-middAngle;
215 | if(_.isEqual(newRotateAngle,0))return;
216 | this.pushIn();
217 | rotateAngle = newRotateAngle;
218 | this.doAnim(null,animRotate,function(){
219 | startAngle += rotateAngle;
220 | _this.trigger('rotate',[_this.data[i],i,angleRanges[i][4]]);
221 | });
222 | }
223 | this.setDountText = function(text){
224 | _this.config.dountText = text;
225 | drawPie(1);
226 | }
227 | /**
228 | * 画图
229 | */
230 | this.draw = function(noAnim){
231 | this.mergeFont(['textFont','dountFont']);
232 | calcOrigin();
233 | totalData = 0;
234 | currentOutIndex = -1;
235 | _.each(_this.data,function(d){
236 | totalData += d.value;
237 | });
238 | calcAngel();
239 | startAngle = _this.config.startAngle;
240 | if(noAnim){
241 | drawPie(1);
242 | }else{
243 | this.doAnim(null,drawPie);
244 | }
245 | }
246 | //计算原点位置及半径
247 | function calcOrigin(){
248 | if(_this.config.totalAngle == Math.PI){
249 | origin = {
250 | x : _this.width/2,
251 | y : _this.height - 20
252 | }
253 | radius = Math.min(origin.x,origin.y) - 10;
254 | }else{
255 | origin = {x:_this.width/2,y:_this.height/2};
256 | radius = Math.min(origin.x,origin.y) - 10;
257 | }
258 | }
259 | //初始化参数
260 | if(cfg)this.initial(cfg);
261 | }
262 | _.Pie = Pie;
263 | }(JChart));
264 |
--------------------------------------------------------------------------------
/src/JChart.Canvas.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 简单的Canvas帮助类,使canvas支持类似于jquery的链式操作,支持CanvasRenderingContext2D所有的方法,并提供一些常用的工具方法
3 | */
4 | ;(function(_){
5 | function Chain(el){
6 | //需要返回结果的方法,这些方法将不能进行后续的链式调用
7 | var needReturnValueFn = ['isPointInPath','measureText','getImageData','createLinearGradient','createPattern','createRadialGradient','isPointInPath'];
8 | function Canvas(){
9 | this.el = el = (typeof el === 'string') ? document.getElementById(el) : el;
10 | this.ctx = el.getContext('2d');
11 | this.width = el.width;
12 | this.height = el.height;
13 | addProtoFunc(this.ctx);
14 | }
15 |
16 | //添加canvas原生方法到prototype中
17 | function addProtoFunc(ctx){
18 | for(var fn in CanvasRenderingContext2D.prototype){
19 | if(Canvas.prototype[fn])continue;
20 | Canvas.prototype[fn] = function(fn){
21 | return function(){
22 | var args = Array.prototype.slice.call(arguments);
23 | var result = ctx[fn].apply(ctx,args);
24 | if(needReturnValueFn.indexOf(fn)>-1){
25 | return result;
26 | }
27 | return this;
28 | }
29 | }(fn);
30 | }
31 | }
32 |
33 | Canvas.prototype = {
34 | /**
35 | * 设置context的属性值
36 | * @param name 属性名
37 | * @param value 属性值
38 | * @return this
39 | */
40 | set : function(name,value){
41 | if(typeof name == 'object'){
42 | for(var p in name){
43 | this.ctx[p] && (this.ctx[p] = name[p]);
44 | }
45 | }else{
46 | this.ctx[name] && (this.ctx[name] = value);
47 | }
48 | return this;
49 | },
50 | /**
51 | * 获取context的属性值
52 | * @param name 属性名
53 | * @return value 属性值
54 | */
55 | get : function(name){
56 | return this.ctx[name];
57 | },
58 | /**
59 | * context填充
60 | * @param color 填充颜色
61 | * @return this
62 | */
63 | fill : function (color) {
64 | color && this.set('fillStyle', color);
65 | this.ctx.fill();
66 | return this;
67 | },
68 | /**
69 | * context描边
70 | * @param color 描边颜色
71 | * @return this
72 | */
73 | stroke : function (color,width) {
74 | if (color) {
75 | this.set('strokeStyle', color);
76 | width && this.set('lineWidth',width);
77 | }
78 | this.ctx.stroke();
79 | return this;
80 | },
81 | /**
82 | * 填充文本,在文本中加入\n可实现换行
83 | * @param text
84 | * @param x
85 | * @param y
86 | * @param style
87 | * @return {*}
88 | */
89 | fillText : function(text,x,y,style){
90 | this.ctx.save();
91 | if(style && typeof style == 'object'){
92 | for(var p in style){
93 | this.set(p,style[p]);
94 | }
95 | }
96 | var texts = (text+'').split('\n');
97 | if(texts.length > 1){
98 | var fontsize = this.getFontSize();
99 | for(var i=0;i 0 && delta <= 250) touch.isDoubleTap = true;
208 | touch.last = now;
209 | longTapTimeout = setTimeout(longTap, longTapDelay);
210 | }
211 | function touchmove(e){
212 | if(!touch.last)return;
213 | var ev = e.touches ? e.touches[0] : e;
214 | touch.x2 = ev.pageX - offset.left;
215 | touch.y2 = ev.pageY - offset.top;
216 | if (Math.abs(touch.x1 - touch.x2) > 15){
217 | e.preventDefault();
218 | cancelAll();
219 | }
220 | }
221 | function touchend(e){
222 | cancelLongTap();
223 | if ('last' in touch){
224 | //tap事件,单击/双击都会触发,0延迟,建议在不使用doubleTap的环境中使用,如果要同时使用tap和doubleTap,请使用singleTap
225 | _this.trigger('_tap',[touch.x1,touch.y1]);
226 | _this.trigger('tap',[touch.x1,touch.y1]);
227 | if (touch.isDoubleTap) {
228 | cancelAll();
229 | _this.trigger('_doubleTap',[touch.x1,touch.y1]);
230 | _this.trigger('doubleTap',[touch.x1,touch.y1]);
231 | }else {
232 | touchTimeout = setTimeout(function(){
233 | touchTimeout = null;
234 | _this.trigger('_singleTap',[touch.x1,touch.y1]);
235 | _this.trigger('singleTap',[touch.x1,touch.y1]);
236 | touch = {};
237 | }, 250)
238 |
239 | }
240 | };
241 | }
242 |
243 | function longTap() {
244 | longTapTimeout = null;
245 | if (touch.last) {
246 | _this.trigger('_longTap',[touch.x1,touch.y1]);
247 | _this.trigger('longTap',[touch.x1,touch.y1]);
248 | touch = {};
249 | }
250 | }
251 |
252 | function cancelLongTap() {
253 | if (longTapTimeout) clearTimeout(longTapTimeout);
254 | longTapTimeout = null;
255 | }
256 |
257 | function cancelAll() {
258 | if (touchTimeout) clearTimeout(touchTimeout);
259 | if (longTapTimeout) clearTimeout(longTapTimeout);
260 | touchTimeout = longTapTimeout = null;
261 | touch = {};
262 | }
263 | }
264 | }
265 | _.Chart = Chart;
266 | })(JChart);
--------------------------------------------------------------------------------
/src/JChart.js:
--------------------------------------------------------------------------------
1 | window.JingleChart = JChart = {
2 | version : '0.1',
3 | animationOptions : {
4 | linear : function (t){
5 | return t;
6 | },
7 | easeInQuad: function (t) {
8 | return t*t;
9 | },
10 | easeOutQuad: function (t) {
11 | return -1 *t*(t-2);
12 | },
13 | easeInOutQuad: function (t) {
14 | if ((t/=1/2) < 1) return 1/2*t*t;
15 | return -1/2 * ((--t)*(t-2) - 1);
16 | },
17 | easeInCubic: function (t) {
18 | return t*t*t;
19 | },
20 | easeOutCubic: function (t) {
21 | return 1*((t=t/1-1)*t*t + 1);
22 | },
23 | easeInOutCubic: function (t) {
24 | if ((t/=1/2) < 1) return 1/2*t*t*t;
25 | return 1/2*((t-=2)*t*t + 2);
26 | },
27 | easeInQuart: function (t) {
28 | return t*t*t*t;
29 | },
30 | easeOutQuart: function (t) {
31 | return -1 * ((t=t/1-1)*t*t*t - 1);
32 | },
33 | easeInOutQuart: function (t) {
34 | if ((t/=1/2) < 1) return 1/2*t*t*t*t;
35 | return -1/2 * ((t-=2)*t*t*t - 2);
36 | },
37 | easeInQuint: function (t) {
38 | return 1*(t/=1)*t*t*t*t;
39 | },
40 | easeOutQuint: function (t) {
41 | return 1*((t=t/1-1)*t*t*t*t + 1);
42 | },
43 | easeInOutQuint: function (t) {
44 | if ((t/=1/2) < 1) return 1/2*t*t*t*t*t;
45 | return 1/2*((t-=2)*t*t*t*t + 2);
46 | },
47 | easeInSine: function (t) {
48 | return -1 * Math.cos(t/1 * (Math.PI/2)) + 1;
49 | },
50 | easeOutSine: function (t) {
51 | return 1 * Math.sin(t/1 * (Math.PI/2));
52 | },
53 | easeInOutSine: function (t) {
54 | return -1/2 * (Math.cos(Math.PI*t/1) - 1);
55 | },
56 | easeInExpo: function (t) {
57 | return (t==0) ? 1 : 1 * Math.pow(2, 10 * (t/1 - 1));
58 | },
59 | easeOutExpo: function (t) {
60 | return (t==1) ? 1 : 1 * (-Math.pow(2, -10 * t/1) + 1);
61 | },
62 | easeInOutExpo: function (t) {
63 | if (t==0) return 0;
64 | if (t==1) return 1;
65 | if ((t/=1/2) < 1) return 1/2 * Math.pow(2, 10 * (t - 1));
66 | return 1/2 * (-Math.pow(2, -10 * --t) + 2);
67 | },
68 | easeInCirc: function (t) {
69 | if (t>=1) return t;
70 | return -1 * (Math.sqrt(1 - (t/=1)*t) - 1);
71 | },
72 | easeOutCirc: function (t) {
73 | return 1 * Math.sqrt(1 - (t=t/1-1)*t);
74 | },
75 | easeInOutCirc: function (t) {
76 | if ((t/=1/2) < 1) return -1/2 * (Math.sqrt(1 - t*t) - 1);
77 | return 1/2 * (Math.sqrt(1 - (t-=2)*t) + 1);
78 | },
79 | easeInElastic: function (t) {
80 | var s=1.70158;var p=0;var a=1;
81 | if (t==0) return 0; if ((t/=1)==1) return 1; if (!p) p=1*.3;
82 | if (a < Math.abs(1)) { a=1; var s=p/4; }
83 | else var s = p/(2*Math.PI) * Math.asin (1/a);
84 | return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*1-s)*(2*Math.PI)/p ));
85 | },
86 | easeOutElastic: function (t) {
87 | var s=1.70158;var p=0;var a=1;
88 | if (t==0) return 0; if ((t/=1)==1) return 1; if (!p) p=1*.3;
89 | if (a < Math.abs(1)) { a=1; var s=p/4; }
90 | else var s = p/(2*Math.PI) * Math.asin (1/a);
91 | return a*Math.pow(2,-10*t) * Math.sin( (t*1-s)*(2*Math.PI)/p ) + 1;
92 | },
93 | easeInOutElastic: function (t) {
94 | var s=1.70158;var p=0;var a=1;
95 | if (t==0) return 0; if ((t/=1/2)==2) return 1; if (!p) p=1*(.3*1.5);
96 | if (a < Math.abs(1)) { a=1; var s=p/4; }
97 | else var s = p/(2*Math.PI) * Math.asin (1/a);
98 | if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*1-s)*(2*Math.PI)/p ));
99 | return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*1-s)*(2*Math.PI)/p )*.5 + 1;
100 | },
101 | easeInBack: function (t) {
102 | var s = 1.70158;
103 | return 1*(t/=1)*t*((s+1)*t - s);
104 | },
105 | easeOutBack: function (t) {
106 | var s = 1.70158;
107 | return 1*((t=t/1-1)*t*((s+1)*t + s) + 1);
108 | },
109 | easeInOutBack: function (t) {
110 | var s = 1.70158;
111 | if ((t/=1/2) < 1) return 1/2*(t*t*(((s*=(1.525))+1)*t - s));
112 | return 1/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2);
113 | },
114 | easeInBounce: function (t) {
115 | return 1 - JChart.animationOptions.easeOutBounce (1-t);
116 | },
117 | easeOutBounce: function (t) {
118 | if ((t/=1) < (1/2.75)) {
119 | return 1*(7.5625*t*t);
120 | } else if (t < (2/2.75)) {
121 | return 1*(7.5625*(t-=(1.5/2.75))*t + .75);
122 | } else if (t < (2.5/2.75)) {
123 | return 1*(7.5625*(t-=(2.25/2.75))*t + .9375);
124 | } else {
125 | return 1*(7.5625*(t-=(2.625/2.75))*t + .984375);
126 | }
127 | },
128 | easeInOutBounce: function (t) {
129 | if (t < 1/2) return JChart.animationOptions.easeInBounce (t*2) * .5;
130 | return JChart.animationOptions.easeOutBounce (t*2-1) * .5 + 1*.5;
131 | }
132 | },
133 | /**
134 | * 通用的计时控制器
135 | */
136 | requestAnimFrame : (function(){
137 | return window.requestAnimationFrame ||
138 | window.webkitRequestAnimationFrame ||
139 | window.mozRequestAnimationFrame ||
140 | window.oRequestAnimationFrame ||
141 | window.msRequestAnimationFrame ||
142 | function(callback) {
143 | window.setTimeout(callback, 1000 / 60);
144 | };
145 | })(),
146 | isNumber : function(n){
147 | return !isNaN(parseFloat(n)) && isFinite(n);
148 | },
149 | isEqual : function(number1, number2, digits){
150 | digits = digits == undefined? 10: digits; // 默认精度为10
151 | return number1.toFixed(digits) === number2.toFixed(digits);
152 | },
153 | /**
154 | * 取有效区域内的值
155 | * @param valueToCap
156 | * @param maxValue
157 | * @param minValue
158 | * @return {*}
159 | */
160 | capValue : function(valueToCap, maxValue, minValue){
161 | var value;
162 | if(this.isNumber(maxValue) && valueToCap > maxValue) {
163 | return maxValue;
164 | }
165 | if(this.isNumber(minValue) && valueToCap < minValue ){
166 | return minValue;
167 | }
168 | return valueToCap;
169 | },
170 | getDecimalPlaces : function(num){
171 | if (num%1!=0){
172 | return num.toString().split(".")[1].length
173 | }
174 | else{
175 | return 0;
176 | }
177 | },
178 | extend : function(target){
179 | var args = Array.prototype.slice.call(arguments,1);
180 | this.each(args,function(v,i){
181 | extend(target,v);
182 | });
183 | function extend(target,source){
184 | for(var key in source){
185 | var o = source[key];
186 | if(o instanceof Array){
187 | target[key] = extend([], o);
188 | }else if(o instanceof Object){
189 | target[key] = extend({},o);
190 | }else{
191 | target[key] = o;
192 | }
193 | }
194 | return target;
195 | }
196 | return target;
197 |
198 | },
199 | clone : function(obj){
200 | var o;
201 | if (typeof obj == "object") {
202 | if (obj === null) {
203 | o = null;
204 | } else {
205 | if (obj instanceof Array) {
206 | o = [];
207 | for (var i = 0, len = obj.length; i < len; i++) {
208 | o.push(this.clone(obj[i]));
209 | }
210 | } else {
211 | o = {};
212 | for (var j in obj) {
213 | o[j] = this.clone(obj[j]);
214 | }
215 | }
216 | }
217 | } else {
218 | o = obj;
219 | }
220 | return o;
221 | },
222 | //只对array有效
223 | each : function(array,fn,context){
224 | for(var i = 0,len=array.length;i)[^\t]*)'/g, "$1\r")
297 | .replace(/\t=(.*?)%>/g, "',$1,'")
298 | .split("\t").join("');")
299 | .split("%>").join("p.push('")
300 | .split("\r").join("\\'")
301 | + "');}return p.join('');");
302 |
303 | // Provide some basic currying to the user
304 | return data ? fn( data ) : fn;
305 | };
306 | return tmpl;
307 | })()
308 | };
309 |
310 |
--------------------------------------------------------------------------------
/doc/css/bootstrap-theme.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v3.0.3 (http://getbootstrap.com)
3 | * Copyright 2013 Twitter, Inc.
4 | * Licensed under http://www.apache.org/licenses/LICENSE-2.0
5 | */
6 |
7 | .btn-default,.btn-primary,.btn-success,.btn-info,.btn-warning,.btn-danger{text-shadow:0 -1px 0 rgba(0,0,0,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075)}.btn-default:active,.btn-primary:active,.btn-success:active,.btn-info:active,.btn-warning:active,.btn-danger:active,.btn-default.active,.btn-primary.active,.btn-success.active,.btn-info.active,.btn-warning.active,.btn-danger.active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn:active,.btn.active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe0e0e0',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-default:hover,.btn-default:focus{background-color:#e0e0e0;background-position:0 -15px}.btn-default:active,.btn-default.active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-primary{background-image:-webkit-linear-gradient(top,#428bca 0,#2d6ca2 100%);background-image:linear-gradient(to bottom,#428bca 0,#2d6ca2 100%);background-repeat:repeat-x;border-color:#2b669a;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff2d6ca2',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary:hover,.btn-primary:focus{background-color:#2d6ca2;background-position:0 -15px}.btn-primary:active,.btn-primary.active{background-color:#2d6ca2;border-color:#2b669a}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);background-repeat:repeat-x;border-color:#3e8f3e;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c',endColorstr='#ff419641',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:focus{background-color:#419641;background-position:0 -15px}.btn-success:active,.btn-success.active{background-color:#419641;border-color:#3e8f3e}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);background-repeat:repeat-x;border-color:#e38d13;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e',endColorstr='#ffeb9316',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:focus{background-color:#eb9316;background-position:0 -15px}.btn-warning:active,.btn-warning.active{background-color:#eb9316;border-color:#e38d13}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);background-repeat:repeat-x;border-color:#b92c28;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f',endColorstr='#ffc12e2a',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:focus{background-color:#c12e2a;background-position:0 -15px}.btn-danger:active,.btn-danger.active{background-color:#c12e2a;border-color:#b92c28}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);background-repeat:repeat-x;border-color:#28a4c9;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff2aabd2',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info:hover,.btn-info:focus{background-color:#2aabd2;background-position:0 -15px}.btn-info:active,.btn-info.active{background-color:#2aabd2;border-color:#28a4c9}.thumbnail,.img-thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#ffe8e8e8',GradientType=0)}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{background-color:#357ebd;background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff357ebd',GradientType=0)}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);background-repeat:repeat-x;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#fff8f8f8',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075)}.navbar-default .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f3f3f3 100%);background-image:linear-gradient(to bottom,#ebebeb 0,#f3f3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb',endColorstr='#fff3f3f3',GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,0.075);box-shadow:inset 0 3px 9px rgba(0,0,0,0.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,0.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c',endColorstr='#ff222222',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.navbar-inverse .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#222 0,#282828 100%);background-image:linear-gradient(to bottom,#222 0,#282828 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222',endColorstr='#ff282828',GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,0.25);box-shadow:inset 0 3px 9px rgba(0,0,0,0.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-static-top,.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}.alert{text-shadow:0 1px 0 rgba(255,255,255,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);background-repeat:repeat-x;border-color:#b2dba1;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8',endColorstr='#ffc8e5bc',GradientType=0)}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);background-repeat:repeat-x;border-color:#9acfea;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7',endColorstr='#ffb9def0',GradientType=0)}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);background-repeat:repeat-x;border-color:#f5e79e;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3',endColorstr='#fff8efc0',GradientType=0)}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);background-repeat:repeat-x;border-color:#dca7a7;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede',endColorstr='#ffe7c3c3',GradientType=0)}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb',endColorstr='#fff5f5f5',GradientType=0)}.progress-bar{background-image:-webkit-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:linear-gradient(to bottom,#428bca 0,#3071a9 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff3071a9',GradientType=0)}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c',endColorstr='#ff449d44',GradientType=0)}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff31b0d5',GradientType=0)}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e',endColorstr='#ffec971f',GradientType=0)}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f',endColorstr='#ffc9302c',GradientType=0)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{text-shadow:0 -1px 0 #3071a9;background-image:-webkit-linear-gradient(top,#428bca 0,#3278b3 100%);background-image:linear-gradient(to bottom,#428bca 0,#3278b3 100%);background-repeat:repeat-x;border-color:#3278b3;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff3278b3',GradientType=0)}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#ffe8e8e8',GradientType=0)}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff357ebd',GradientType=0)}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8',endColorstr='#ffd0e9c6',GradientType=0)}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7',endColorstr='#ffc4e3f3',GradientType=0)}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3',endColorstr='#fffaf2cc',GradientType=0)}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede',endColorstr='#ffebcccc',GradientType=0)}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);background-repeat:repeat-x;border-color:#dcdcdc;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8',endColorstr='#fff5f5f5',GradientType=0);-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1)}
--------------------------------------------------------------------------------
/doc/css/bootstrap-theme.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v3.0.3 (http://getbootstrap.com)
3 | * Copyright 2013 Twitter, Inc.
4 | * Licensed under http://www.apache.org/licenses/LICENSE-2.0
5 | */
6 |
7 | .btn-default,
8 | .btn-primary,
9 | .btn-success,
10 | .btn-info,
11 | .btn-warning,
12 | .btn-danger {
13 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
14 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
15 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
16 | }
17 |
18 | .btn-default:active,
19 | .btn-primary:active,
20 | .btn-success:active,
21 | .btn-info:active,
22 | .btn-warning:active,
23 | .btn-danger:active,
24 | .btn-default.active,
25 | .btn-primary.active,
26 | .btn-success.active,
27 | .btn-info.active,
28 | .btn-warning.active,
29 | .btn-danger.active {
30 | -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
31 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
32 | }
33 |
34 | .btn:active,
35 | .btn.active {
36 | background-image: none;
37 | }
38 |
39 | .btn-default {
40 | text-shadow: 0 1px 0 #fff;
41 | background-image: -webkit-linear-gradient(top, #ffffff 0%, #e0e0e0 100%);
42 | background-image: linear-gradient(to bottom, #ffffff 0%, #e0e0e0 100%);
43 | background-repeat: repeat-x;
44 | border-color: #dbdbdb;
45 | border-color: #ccc;
46 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
47 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
48 | }
49 |
50 | .btn-default:hover,
51 | .btn-default:focus {
52 | background-color: #e0e0e0;
53 | background-position: 0 -15px;
54 | }
55 |
56 | .btn-default:active,
57 | .btn-default.active {
58 | background-color: #e0e0e0;
59 | border-color: #dbdbdb;
60 | }
61 |
62 | .btn-primary {
63 | background-image: -webkit-linear-gradient(top, #428bca 0%, #2d6ca2 100%);
64 | background-image: linear-gradient(to bottom, #428bca 0%, #2d6ca2 100%);
65 | background-repeat: repeat-x;
66 | border-color: #2b669a;
67 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0);
68 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
69 | }
70 |
71 | .btn-primary:hover,
72 | .btn-primary:focus {
73 | background-color: #2d6ca2;
74 | background-position: 0 -15px;
75 | }
76 |
77 | .btn-primary:active,
78 | .btn-primary.active {
79 | background-color: #2d6ca2;
80 | border-color: #2b669a;
81 | }
82 |
83 | .btn-success {
84 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
85 | background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
86 | background-repeat: repeat-x;
87 | border-color: #3e8f3e;
88 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
89 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
90 | }
91 |
92 | .btn-success:hover,
93 | .btn-success:focus {
94 | background-color: #419641;
95 | background-position: 0 -15px;
96 | }
97 |
98 | .btn-success:active,
99 | .btn-success.active {
100 | background-color: #419641;
101 | border-color: #3e8f3e;
102 | }
103 |
104 | .btn-warning {
105 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
106 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
107 | background-repeat: repeat-x;
108 | border-color: #e38d13;
109 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
110 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
111 | }
112 |
113 | .btn-warning:hover,
114 | .btn-warning:focus {
115 | background-color: #eb9316;
116 | background-position: 0 -15px;
117 | }
118 |
119 | .btn-warning:active,
120 | .btn-warning.active {
121 | background-color: #eb9316;
122 | border-color: #e38d13;
123 | }
124 |
125 | .btn-danger {
126 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
127 | background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
128 | background-repeat: repeat-x;
129 | border-color: #b92c28;
130 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
131 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
132 | }
133 |
134 | .btn-danger:hover,
135 | .btn-danger:focus {
136 | background-color: #c12e2a;
137 | background-position: 0 -15px;
138 | }
139 |
140 | .btn-danger:active,
141 | .btn-danger.active {
142 | background-color: #c12e2a;
143 | border-color: #b92c28;
144 | }
145 |
146 | .btn-info {
147 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
148 | background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
149 | background-repeat: repeat-x;
150 | border-color: #28a4c9;
151 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
152 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
153 | }
154 |
155 | .btn-info:hover,
156 | .btn-info:focus {
157 | background-color: #2aabd2;
158 | background-position: 0 -15px;
159 | }
160 |
161 | .btn-info:active,
162 | .btn-info.active {
163 | background-color: #2aabd2;
164 | border-color: #28a4c9;
165 | }
166 |
167 | .thumbnail,
168 | .img-thumbnail {
169 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
170 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
171 | }
172 |
173 | .dropdown-menu > li > a:hover,
174 | .dropdown-menu > li > a:focus {
175 | background-color: #e8e8e8;
176 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
177 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
178 | background-repeat: repeat-x;
179 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
180 | }
181 |
182 | .dropdown-menu > .active > a,
183 | .dropdown-menu > .active > a:hover,
184 | .dropdown-menu > .active > a:focus {
185 | background-color: #357ebd;
186 | background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%);
187 | background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%);
188 | background-repeat: repeat-x;
189 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);
190 | }
191 |
192 | .navbar-default {
193 | background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);
194 | background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%);
195 | background-repeat: repeat-x;
196 | border-radius: 4px;
197 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
198 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
199 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);
200 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);
201 | }
202 |
203 | .navbar-default .navbar-nav > .active > a {
204 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%);
205 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f3f3f3 100%);
206 | background-repeat: repeat-x;
207 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0);
208 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);
209 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);
210 | }
211 |
212 | .navbar-brand,
213 | .navbar-nav > li > a {
214 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);
215 | }
216 |
217 | .navbar-inverse {
218 | background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222222 100%);
219 | background-image: linear-gradient(to bottom, #3c3c3c 0%, #222222 100%);
220 | background-repeat: repeat-x;
221 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
222 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
223 | }
224 |
225 | .navbar-inverse .navbar-nav > .active > a {
226 | background-image: -webkit-linear-gradient(top, #222222 0%, #282828 100%);
227 | background-image: linear-gradient(to bottom, #222222 0%, #282828 100%);
228 | background-repeat: repeat-x;
229 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0);
230 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);
231 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);
232 | }
233 |
234 | .navbar-inverse .navbar-brand,
235 | .navbar-inverse .navbar-nav > li > a {
236 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
237 | }
238 |
239 | .navbar-static-top,
240 | .navbar-fixed-top,
241 | .navbar-fixed-bottom {
242 | border-radius: 0;
243 | }
244 |
245 | .alert {
246 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);
247 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
248 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
249 | }
250 |
251 | .alert-success {
252 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
253 | background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
254 | background-repeat: repeat-x;
255 | border-color: #b2dba1;
256 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
257 | }
258 |
259 | .alert-info {
260 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
261 | background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
262 | background-repeat: repeat-x;
263 | border-color: #9acfea;
264 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
265 | }
266 |
267 | .alert-warning {
268 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
269 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
270 | background-repeat: repeat-x;
271 | border-color: #f5e79e;
272 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
273 | }
274 |
275 | .alert-danger {
276 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
277 | background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
278 | background-repeat: repeat-x;
279 | border-color: #dca7a7;
280 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
281 | }
282 |
283 | .progress {
284 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
285 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
286 | background-repeat: repeat-x;
287 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
288 | }
289 |
290 | .progress-bar {
291 | background-image: -webkit-linear-gradient(top, #428bca 0%, #3071a9 100%);
292 | background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%);
293 | background-repeat: repeat-x;
294 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0);
295 | }
296 |
297 | .progress-bar-success {
298 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
299 | background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
300 | background-repeat: repeat-x;
301 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
302 | }
303 |
304 | .progress-bar-info {
305 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
306 | background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
307 | background-repeat: repeat-x;
308 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
309 | }
310 |
311 | .progress-bar-warning {
312 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
313 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
314 | background-repeat: repeat-x;
315 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
316 | }
317 |
318 | .progress-bar-danger {
319 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
320 | background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
321 | background-repeat: repeat-x;
322 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
323 | }
324 |
325 | .list-group {
326 | border-radius: 4px;
327 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
328 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
329 | }
330 |
331 | .list-group-item.active,
332 | .list-group-item.active:hover,
333 | .list-group-item.active:focus {
334 | text-shadow: 0 -1px 0 #3071a9;
335 | background-image: -webkit-linear-gradient(top, #428bca 0%, #3278b3 100%);
336 | background-image: linear-gradient(to bottom, #428bca 0%, #3278b3 100%);
337 | background-repeat: repeat-x;
338 | border-color: #3278b3;
339 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0);
340 | }
341 |
342 | .panel {
343 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
344 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
345 | }
346 |
347 | .panel-default > .panel-heading {
348 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
349 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
350 | background-repeat: repeat-x;
351 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
352 | }
353 |
354 | .panel-primary > .panel-heading {
355 | background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%);
356 | background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%);
357 | background-repeat: repeat-x;
358 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);
359 | }
360 |
361 | .panel-success > .panel-heading {
362 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
363 | background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
364 | background-repeat: repeat-x;
365 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
366 | }
367 |
368 | .panel-info > .panel-heading {
369 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
370 | background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
371 | background-repeat: repeat-x;
372 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
373 | }
374 |
375 | .panel-warning > .panel-heading {
376 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
377 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
378 | background-repeat: repeat-x;
379 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
380 | }
381 |
382 | .panel-danger > .panel-heading {
383 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
384 | background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
385 | background-repeat: repeat-x;
386 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
387 | }
388 |
389 | .well {
390 | background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
391 | background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
392 | background-repeat: repeat-x;
393 | border-color: #dcdcdc;
394 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
395 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);
396 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);
397 | }
--------------------------------------------------------------------------------
/src/JChart.Scale.js:
--------------------------------------------------------------------------------
1 | ;(function(_){
2 | /**
3 | * 抽象类-刻度值
4 | * 用来初始化XY轴各项数据
5 | * @constructor
6 | */
7 | function Scale(){
8 | var P_T = 5,//图表顶部空白
9 | P_R = 5,//图表右侧空白
10 | P_Y = 20,//y轴左侧空白
11 | P_X = 10;//x轴文本与x之间的间距
12 | _.Chart.apply(this);
13 | _.extend(this.config,{
14 | /**
15 | * @Object
16 | * Y轴刻度值,默认为null,会自动生成,也可以自己指定
17 | * {
18 | * step : 10,//刻度个数,必选项
19 | * stepValue : 10//每两个刻度线之间的差值,必选项
20 | * start : 0//起始刻度值,默认为0
21 | * }
22 | */
23 | scale : null,
24 | //xy轴刻度线的颜色
25 | scaleLineColor : "rgba(0,0,0,.3)",
26 | //刻度线宽度
27 | scaleLineWidth:1,
28 | //是否显示Y轴刻度值
29 | showScaleLabel : true,
30 | //是否显示X轴刻度值
31 | showLabel : true,
32 | //刻度值字体属性
33 | scaleFont : {
34 | size:12,
35 | color : '#666'
36 | },
37 | textFont : {
38 | size : 14,
39 | textBaseline : 'bottom'
40 | },
41 | //是否显示网格线
42 | showGridLine : true,
43 | //网格线颜色
44 | gridLineColor : "rgba(0,0,0,.1)",
45 | //网格线宽度
46 | gridLineWidth : 1,
47 | //水平线{value : 50,color : #fff,width : 1}
48 | horizonLine : null
49 |
50 | });
51 | //数据偏移量-已经偏移
52 | this.dataOffset = 0;
53 | this.scaleData = {
54 | x : 0,//圆点坐标
55 | y : 0,
56 | xHop : 0,//x轴数据项宽度
57 | yHop : 0,//y轴每个刻度的高度
58 | xLength : 0,//x轴长度
59 | yHeight : 0,//y轴高度
60 | yLabelHeight : 0,//y轴刻度文本高度
61 | yScaleValue : null,//y轴刻度指标
62 | labelRotate : 0,//x轴label旋转角度
63 | xLabelWidth : 0,//x轴label宽度
64 | xLabelHeight : 0,//x轴label宽度
65 | barWidth : 0//柱形图柱子宽度
66 | }
67 | /**
68 | * 计算X轴文本宽度、旋转角度及Y轴高度
69 | */
70 | this.calcDrawingSizes = function(){
71 | var maxSize = this.height,widestX = 0,scaleFontSize = this.config.scaleFont.size, xLabelWidth = 0,xLabelHeight = scaleFontSize,
72 | labelRotate = 0,dataLen = this.chartData.labels.length;
73 | //计算X轴,如果发现数据宽度超过总宽度,需要将label进行旋转
74 | this.ctx.set(this.config.scaleFont);
75 | //找出最宽的label
76 | _.each(this.chartData.labels,function(o){
77 | var w = this.ctx.measureText(o).width;
78 | widestX = (w > widestX)? w : widestX;
79 | },this);
80 | xLabelWidth = widestX;
81 | if (this.width/dataLen < widestX){
82 | labelRotate = 45;
83 | xLabelWidth = Math.cos(labelRotate*Math.PI/180) * widestX;
84 | xLabelHeight = Math.sin(labelRotate*Math.PI/180) * widestX ;
85 | if (this.width/dataLen < xLabelHeight){
86 | labelRotate = 90;
87 | xLabelWidth = scaleFontSize;
88 | xLabelHeight = widestX;
89 | }
90 | }
91 | //减去x轴label的高度
92 | maxSize -= xLabelHeight;
93 | //减去x轴文本与x轴之间的间距
94 | maxSize -= P_X;
95 | //给Y轴顶部留一点空白
96 | maxSize -= P_T;
97 | maxSize -= this.config.showText?scaleFontSize:0;
98 | //y轴高度
99 | this.scaleData.yHeight = maxSize;
100 | //y轴刻度高度
101 | this.scaleData.yLabelHeight = scaleFontSize;
102 | //x轴文本旋转角度
103 | this.scaleData.labelRotate = labelRotate;
104 | //x轴文本的宽度
105 | this.scaleData.xLabelWidth = xLabelWidth;
106 | //x轴文本的高度
107 | this.scaleData.xLabelHeight = xLabelHeight;
108 | }
109 |
110 | /**
111 | * 计算Y轴刻度的边界及刻度步数
112 | * @return {Object}
113 | */
114 | this.getValueBounds = function(dataset) {
115 | var upperValue = Number.MIN_VALUE;
116 | var lowerValue = Number.MAX_VALUE;
117 | _.each(dataset,function(o){
118 | _.each(o.data,function(obj){
119 | if(obj > upperValue){upperValue = obj};
120 | if (obj < lowerValue) { lowerValue = obj};
121 | });
122 | })
123 | var yh = this.scaleData.yHeight;
124 | var lh = this.scaleData.yLabelHeight;
125 | var maxSteps = Math.floor((yh/(lh*0.66)));
126 | var minSteps = Math.floor((yh/lh*0.5));
127 |
128 | return {
129 | maxValue : upperValue,
130 | minValue : lowerValue,
131 | maxSteps : maxSteps,
132 | minSteps : minSteps
133 | };
134 | }
135 |
136 | /**
137 | * 计算Y轴刻度的各项数据
138 | */
139 | this.calcYAxis = function(){
140 | var scale = this.config.scale;
141 | if (scale){
142 | scale.start = scale.start || 0;
143 | scale.labels = this.populateLabels(scale.step,scale.start,scale.stepValue);
144 | }else {
145 | var bounds = this.getValueBounds(this.chartData.datasets);
146 | scale = this.calcScale(this.scaleData.yHeight,bounds.maxSteps,bounds.minSteps,bounds.maxValue,bounds.minValue);
147 | }
148 | this.scaleData.yScaleValue = scale;
149 | this.scaleData.yHop = Math.floor(this.scaleData.yHeight/scale.step);
150 | }
151 |
152 | /**
153 | * 计算X轴宽度,每个数据项宽度大小及坐标原点
154 | */
155 | this.calcXAxis = function(){
156 | var config = this.config,scale = this.scaleData,yLabelWidth = 0,xAxisLength,valueHop, x,y;
157 | if (config.showScaleLabel){
158 | //找出Y轴刻度的最宽值
159 | _.each(scale.yScaleValue.labels,function(o){
160 | var w = this.ctx.measureText(o).width;
161 | yLabelWidth = (w > yLabelWidth)? w : yLabelWidth;
162 | },this);
163 | yLabelWidth += P_Y;
164 | }
165 | //x轴的宽度
166 | xAxisLength = this.width - yLabelWidth-P_R-(this.config.showText?this.config.textFont.size:0);
167 |
168 | if(this._type_ == 'bar'){//计算柱形图柱子宽度,柱形图x轴文本居中显示,需要重新计算数据项宽度
169 | valueHop = Math.floor(xAxisLength/this.chartData.labels.length);
170 | var len = this.chartData.datasets.length;
171 | scale.barWidth = (valueHop - config.gridLineWidth*2 - (config.barSetSpacing*2) - (config.barSpacing*len-1) - ((config.barBorderWidth/2)*len-1))/len;
172 | }else{
173 | valueHop = Math.floor(xAxisLength/(this.chartData.labels.length-1));
174 | }
175 | scale.x = yLabelWidth;
176 | scale.y = this.height - scale.xLabelHeight - P_X;
177 | scale.xWidth = xAxisLength;
178 | scale.xHop = valueHop;
179 | }
180 |
181 | this.drawScale = function(){
182 | var ctx = this.ctx,cfg = this.config,scale = this.scaleData,align;
183 | ctx.set({
184 | strokeStyle : cfg.scaleLineColor,
185 | lineWidth : cfg.scaleLineWidth
186 | })
187 | //画X轴
188 | ctx.line(scale.x-3, scale.y, scale.x+scale.xWidth, scale.y, true);
189 | //画Y轴
190 | ctx.line(scale.x,scale.y+3, scale.x,scale.y-scale.yHeight, true);
191 |
192 | //todo 设置主体部分背景颜色 渐变???
193 |
194 | //画X轴刻度文本
195 | if (scale.labelRotate > 0){
196 | ctx.save();
197 | align = 'right';
198 | }else{
199 | align = 'center';
200 | }
201 | ctx.set({
202 | fillStyle : cfg.scaleFont.color,
203 | textAlign : align,
204 | textBaseline : 'hanging',
205 | strokeStyle : cfg.gridLineColor,
206 | lineWidth : cfg.gridLineWidth
207 | });
208 | _.each(this.chartData.labels,function(label,i){
209 | ctx.save();
210 | var cx = scale.x + i*scale.xHop,labelY = scale.y + P_X/ 2,
211 | labelX = this._type_ == 'bar'?cx + scale.xHop/2 : cx;
212 | if (scale.labelRotate > 0){
213 | ctx.translate(labelX,labelY).rotate(-(scale.labelRotate * (Math.PI/180))).fillText(label,0,0).restore();
214 | }else{
215 | ctx.fillText(label, labelX,labelY);
216 | }
217 | //画纵向的网格线
218 | if(cfg.showGridLine){
219 | var x = (this._type_ == 'bar')?cx + scale.xHop : cx;
220 | ctx.line(x, scale.y, x, scale.y-scale.yHeight,true);
221 | }
222 | },this);
223 |
224 | //画横向网格线
225 | ctx.set({textAlign:'right',textBaseline:'middle'});
226 | for (var j=0; j maxSteps) {
277 | if (step < minSteps){
278 | stepValue /= 2;
279 | step = Math.round(range/stepValue);
280 | }
281 | else{
282 | stepValue *=2;
283 | step = Math.round(range/stepValue);
284 | }
285 | };
286 | var labels = this.populateLabels(step, min, stepValue);;
287 | return {
288 | step : step,
289 | stepValue : stepValue,
290 | start : min,
291 | labels : labels
292 | }
293 | function calculateOrderOfMagnitude(val){
294 | return Math.floor(Math.log(val) / Math.LN10);
295 | }
296 | }
297 |
298 | /**
299 | * 构造刻度值
300 | * @param labels
301 | * @param numberOfSteps
302 | * @param graphMin
303 | * @param stepValue
304 | */
305 | this.populateLabels = function (step, start, stepValue) {
306 | var labels = [];
307 | for (var i = 1; i < step + 1; i++) {
308 | if(!this.config.showScaleLabel){
309 | labels.push('');
310 | continue;
311 | }
312 | //小数点位数与stepValue后的小数点一致
313 | var value = (start + (stepValue * i)).toFixed(_.getDecimalPlaces(stepValue));
314 | var text = this.trigger('renderYLabel',[value]);
315 | text = text ? text : value;
316 | labels.push(text);
317 | }
318 | return labels;
319 | },
320 | this.calcOffset = function(val,scale,scaleHop){
321 | var outerValue = scale.step * scale.stepValue;
322 | var adjustedValue = val - scale.start;
323 | var scalingFactor = _.capValue(adjustedValue/outerValue,1,0);
324 | return (scaleHop*scale.step) * scalingFactor;
325 | },
326 |
327 | this.sliceData = function(data,offset,len,num){
328 | var newdata = _.clone(data);
329 | var min = offset,max = offset + num;
330 | if(max > len){
331 | min = len - num;
332 | max = len;
333 | }
334 | newdata.labels = newdata.labels.slice(min,max);
335 | _.each(newdata.datasets,function(d){
336 | d.data = d.data.slice(min,max)
337 | });
338 | return newdata;
339 | }
340 | this.bindDataGestureEvent = function(){
341 | var _this = this,
342 | touchDistanceX,//手指滑动偏移量
343 | startPosition,//触摸初始位置记录
344 | currentOffset = 0,//当前一次滑动的偏移量
345 | dataNum = this.config.datasetShowNumber,//每屏显示的数据条数
346 | gestureStarted,
347 | hasTouch = 'ontouchstart' in window,
348 | START_EV = hasTouch ? 'touchstart' : 'mousedown',
349 | MOVE_EV = hasTouch ? 'touchmove' : 'mousemove',
350 | END_EV = hasTouch ? 'touchend' : 'mouseup';
351 |
352 | this.ctx.el.addEventListener(START_EV,touchstart);
353 | this.ctx.el.addEventListener(MOVE_EV,touchmove);
354 | this.ctx.el.addEventListener(END_EV,touchend);
355 |
356 | function touchstart(e){
357 | e = e.touches ? e.touches[0] : e;
358 | startPosition = {
359 | x : e.pageX,
360 | y : e.pageY
361 | }
362 | touchDistanceX = 0;
363 | gestureStarted = true;
364 | }
365 | function touchmove(e){
366 | if(!gestureStarted || !_this.config.datasetGesture)return;
367 | e = e.touches ? e.touches[0] : e;
368 | var x = e.pageX;
369 | var y = e.pageY;
370 | touchDistanceX = x - startPosition.x;
371 | //每滑动xHop加载下一组数据
372 | var totalLen = _this.data.labels.length;//数据总长度
373 | var offset = _this.dataOffset - Math.floor(touchDistanceX/_this.scaleData.xHop);
374 | if(offset < 0 || offset == currentOffset||(offset+dataNum > totalLen))return;
375 | currentOffset = offset;
376 | console.log(offset);
377 | //将操作加入系统队列,解决android系统下touchmove的bug
378 | setTimeout(function(){
379 | _this.redraw(_this.sliceData(_this.data,offset,totalLen,dataNum));
380 | },0)
381 | }
382 | function touchend(event){
383 | gestureStarted = false;
384 | _this.dataOffset = currentOffset;
385 | }
386 | }
387 | }
388 | _.Scale = Scale;
389 | })(JChart);
--------------------------------------------------------------------------------
/doc/js/highlight.min.js:
--------------------------------------------------------------------------------
1 | var hljs=new function(){function l(o){return o.replace(/&/gm,"&").replace(//gm,">")}function b(p){for(var o=p.firstChild;o;o=o.nextSibling){if(o.nodeName=="CODE"){return o}if(!(o.nodeType==3&&o.nodeValue.match(/\s+/))){break}}}function h(p,o){return Array.prototype.map.call(p.childNodes,function(q){if(q.nodeType==3){return o?q.nodeValue.replace(/\n/g,""):q.nodeValue}if(q.nodeName=="BR"){return"\n"}return h(q,o)}).join("")}function a(q){var p=(q.className+" "+q.parentNode.className).split(/\s+/);p=p.map(function(r){return r.replace(/^language-/,"")});for(var o=0;o"}while(x.length||v.length){var u=t().splice(0,1)[0];y+=l(w.substr(p,u.offset-p));p=u.offset;if(u.event=="start"){y+=s(u.node);r.push(u.node)}else{if(u.event=="stop"){var o,q=r.length;do{q--;o=r[q];y+=(""+o.nodeName.toLowerCase()+">")}while(o!=u.node);r.splice(q,1);while(q'+L[0]+""}else{r+=L[0]}N=A.lR.lastIndex;L=A.lR.exec(K)}return r+K.substr(N)}function z(){if(A.sL&&!e[A.sL]){return l(w)}var r=A.sL?d(A.sL,w):g(w);if(A.r>0){v+=r.keyword_count;B+=r.r}return''+r.value+""}function J(){return A.sL!==undefined?z():G()}function I(L,r){var K=L.cN?'':"";if(L.rB){x+=K;w=""}else{if(L.eB){x+=l(r)+K;w=""}else{x+=K;w=r}}A=Object.create(L,{parent:{value:A}});B+=L.r}function C(K,r){w+=K;if(r===undefined){x+=J();return 0}var L=o(r,A);if(L){x+=J();I(L,r);return L.rB?0:r.length}var M=s(A,r);if(M){if(!(M.rE||M.eE)){w+=r}x+=J();do{if(A.cN){x+=""}A=A.parent}while(A!=M.parent);if(M.eE){x+=l(r)}w="";if(M.starts){I(M.starts,"")}return M.rE?0:r.length}if(t(r,A)){throw"Illegal"}w+=r;return r.length||1}var F=e[D];f(F);var A=F;var w="";var B=0;var v=0;var x="";try{var u,q,p=0;while(true){A.t.lastIndex=p;u=A.t.exec(E);if(!u){break}q=C(E.substr(p,u.index-p),u[0]);p=u.index+q}C(E.substr(p));return{r:B,keyword_count:v,value:x,language:D}}catch(H){if(H=="Illegal"){return{r:0,keyword_count:0,value:l(E)}}else{throw H}}}function g(s){var o={keyword_count:0,r:0,value:l(s)};var q=o;for(var p in e){if(!e.hasOwnProperty(p)){continue}var r=d(p,s);r.language=p;if(r.keyword_count+r.r>q.keyword_count+q.r){q=r}if(r.keyword_count+r.r>o.keyword_count+o.r){q=o;o=r}}if(q.language){o.second_best=q}return o}function i(q,p,o){if(p){q=q.replace(/^((<[^>]+>|\t)+)/gm,function(r,v,u,t){return v.replace(/\t/g,p)})}if(o){q=q.replace(/\n/g,"
")}return q}function m(r,u,p){var v=h(r,p);var t=a(r);if(t=="no-highlight"){return}var w=t?d(t,v):g(v);t=w.language;var o=c(r);if(o.length){var q=document.createElement("pre");q.innerHTML=w.value;w.value=j(o,c(q),v)}w.value=i(w.value,u,p);var s=r.className;if(!s.match("(\\s|^)(language-)?"+t+"(\\s|$)")){s=s?(s+" "+t):t}r.innerHTML=w.value;r.className=s;r.result={language:t,kw:w.keyword_count,re:w.r};if(w.second_best){r.second_best={language:w.second_best.language,kw:w.second_best.keyword_count,re:w.second_best.r}}}function n(){if(n.called){return}n.called=true;Array.prototype.map.call(document.getElementsByTagName("pre"),b).filter(Boolean).forEach(function(o){m(o,hljs.tabReplace)})}function k(){window.addEventListener("DOMContentLoaded",n,false);window.addEventListener("load",n,false)}var e={};this.LANGUAGES=e;this.highlight=d;this.highlightAuto=g;this.fixMarkup=i;this.highlightBlock=m;this.initHighlighting=n;this.initHighlightingOnLoad=k;this.IR="[a-zA-Z][a-zA-Z0-9_]*";this.UIR="[a-zA-Z_][a-zA-Z0-9_]*";this.NR="\\b\\d+(\\.\\d+)?";this.CNR="(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)";this.BNR="\\b(0b[01]+)";this.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|\\.|-|-=|/|/=|:|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.BE={b:"\\\\[\\s\\S]",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE],r:0};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE],r:0};this.CLCM={cN:"comment",b:"//",e:"$"};this.CBLCLM={cN:"comment",b:"/\\*",e:"\\*/"};this.HCM={cN:"comment",b:"#",e:"$"};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.BNM={cN:"number",b:this.BNR,r:0};this.inherit=function(q,r){var o={};for(var p in q){o[p]=q[p]}if(r){for(var p in r){o[p]=r[p]}}return o}}();hljs.LANGUAGES.bash=function(a){var g="true false";var e="if then else elif fi for break continue while in do done echo exit return set declare";var c={cN:"variable",b:"\\$[a-zA-Z0-9_#]+"};var b={cN:"variable",b:"\\${([^}]|\\\\})+}"};var h={cN:"string",b:'"',e:'"',i:"\\n",c:[a.BE,c,b],r:0};var d={cN:"string",b:"'",e:"'",c:[{b:"''"}],r:0};var f={cN:"test_condition",b:"",e:"",c:[h,d,c,b],k:{literal:g},r:0};return{k:{keyword:e,literal:g},c:[{cN:"shebang",b:"(#!\\/bin\\/bash)|(#!\\/bin\\/sh)",r:10},c,b,a.HCM,h,d,a.inherit(f,{b:"\\[ ",e:" \\]",r:0}),a.inherit(f,{b:"\\[\\[ ",e:" \\]\\]"})]}}(hljs);hljs.LANGUAGES.cs=function(a){return{k:"abstract as base bool break byte case catch char checked class const continue decimal default delegate do double else enum event explicit extern false finally fixed float for foreach goto if implicit in int interface internal is lock long namespace new null object operator out override params private protected public readonly ref return sbyte sealed short sizeof stackalloc static string struct switch this throw true try typeof uint ulong unchecked unsafe ushort using virtual volatile void while ascending descending from get group into join let orderby partial select set value var where yield",c:[{cN:"comment",b:"///",e:"$",rB:true,c:[{cN:"xmlDocTag",b:"///|"},{cN:"xmlDocTag",b:"?",e:">"}]},a.CLCM,a.CBLCLM,{cN:"preprocessor",b:"#",e:"$",k:"if else elif endif define undef warning error line region endregion pragma checksum"},{cN:"string",b:'@"',e:'"',c:[{b:'""'}]},a.ASM,a.QSM,a.CNM]}}(hljs);hljs.LANGUAGES.ruby=function(e){var a="[a-zA-Z_][a-zA-Z0-9_]*(\\!|\\?)?";var j="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?";var g={keyword:"and false then defined module in return redo if BEGIN retry end for true self when next until do begin unless END rescue nil else break undef not super class case require yield alias while ensure elsif or include"};var c={cN:"yardoctag",b:"@[A-Za-z]+"};var k=[{cN:"comment",b:"#",e:"$",c:[c]},{cN:"comment",b:"^\\=begin",e:"^\\=end",c:[c],r:10},{cN:"comment",b:"^__END__",e:"\\n$"}];var d={cN:"subst",b:"#\\{",e:"}",l:a,k:g};var i=[e.BE,d];var b=[{cN:"string",b:"'",e:"'",c:i,r:0},{cN:"string",b:'"',e:'"',c:i,r:0},{cN:"string",b:"%[qw]?\\(",e:"\\)",c:i},{cN:"string",b:"%[qw]?\\[",e:"\\]",c:i},{cN:"string",b:"%[qw]?{",e:"}",c:i},{cN:"string",b:"%[qw]?<",e:">",c:i,r:10},{cN:"string",b:"%[qw]?/",e:"/",c:i,r:10},{cN:"string",b:"%[qw]?%",e:"%",c:i,r:10},{cN:"string",b:"%[qw]?-",e:"-",c:i,r:10},{cN:"string",b:"%[qw]?\\|",e:"\\|",c:i,r:10}];var h={cN:"function",bWK:true,e:" |$|;",k:"def",c:[{cN:"title",b:j,l:a,k:g},{cN:"params",b:"\\(",e:"\\)",l:a,k:g}].concat(k)};var f=k.concat(b.concat([{cN:"class",bWK:true,e:"$|;",k:"class module",c:[{cN:"title",b:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?",r:0},{cN:"inheritance",b:"<\\s*",c:[{cN:"parent",b:"("+e.IR+"::)?"+e.IR}]}].concat(k)},h,{cN:"constant",b:"(::)?(\\b[A-Z]\\w*(::)?)+",r:0},{cN:"symbol",b:":",c:b.concat([{b:j}]),r:0},{cN:"symbol",b:a+":",r:0},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{cN:"number",b:"\\?\\w"},{cN:"variable",b:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},{b:"("+e.RSR+")\\s*",c:k.concat([{cN:"regexp",b:"/",e:"/[a-z]*",i:"\\n",c:[e.BE,d]}]),r:0}]));d.c=f;h.c[1].c=f;return{l:a,k:g,c:f}}(hljs);hljs.LANGUAGES.diff=function(a){return{c:[{cN:"chunk",b:"^\\@\\@ +\\-\\d+,\\d+ +\\+\\d+,\\d+ +\\@\\@$",r:10},{cN:"chunk",b:"^\\*\\*\\* +\\d+,\\d+ +\\*\\*\\*\\*$",r:10},{cN:"chunk",b:"^\\-\\-\\- +\\d+,\\d+ +\\-\\-\\-\\-$",r:10},{cN:"header",b:"Index: ",e:"$"},{cN:"header",b:"=====",e:"=====$"},{cN:"header",b:"^\\-\\-\\-",e:"$"},{cN:"header",b:"^\\*{3} ",e:"$"},{cN:"header",b:"^\\+\\+\\+",e:"$"},{cN:"header",b:"\\*{5}",e:"\\*{5}$"},{cN:"addition",b:"^\\+",e:"$"},{cN:"deletion",b:"^\\-",e:"$"},{cN:"change",b:"^\\!",e:"$"}]}}(hljs);hljs.LANGUAGES.javascript=function(a){return{k:{keyword:"in if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const",literal:"true false null undefined NaN Infinity"},c:[a.ASM,a.QSM,a.CLCM,a.CBLCLM,a.CNM,{b:"("+a.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[a.CLCM,a.CBLCLM,{cN:"regexp",b:"/",e:"/[gim]*",i:"\\n",c:[{b:"\\\\/"}]},{b:"<",e:">;",sL:"xml"}],r:0},{cN:"function",bWK:true,e:"{",k:"function",c:[{cN:"title",b:"[A-Za-z$_][0-9A-Za-z$_]*"},{cN:"params",b:"\\(",e:"\\)",c:[a.CLCM,a.CBLCLM],i:"[\"'\\(]"}],i:"\\[|%"}]}}(hljs);hljs.LANGUAGES.css=function(a){var b={cN:"function",b:a.IR+"\\(",e:"\\)",c:[a.NM,a.ASM,a.QSM]};return{cI:true,i:"[=/|']",c:[a.CBLCLM,{cN:"id",b:"\\#[A-Za-z0-9_-]+"},{cN:"class",b:"\\.[A-Za-z0-9_-]+",r:0},{cN:"attr_selector",b:"\\[",e:"\\]",i:"$"},{cN:"pseudo",b:":(:)?[a-zA-Z0-9\\_\\-\\+\\(\\)\\\"\\']+"},{cN:"at_rule",b:"@(font-face|page)",l:"[a-z-]+",k:"font-face page"},{cN:"at_rule",b:"@",e:"[{;]",eE:true,k:"import page media charset",c:[b,a.ASM,a.QSM,a.NM]},{cN:"tag",b:a.IR,r:0},{cN:"rules",b:"{",e:"}",i:"[^\\s]",r:0,c:[a.CBLCLM,{cN:"rule",b:"[^\\s]",rB:true,e:";",eW:true,c:[{cN:"attribute",b:"[A-Z\\_\\.\\-]+",e:":",eE:true,i:"[^\\s]",starts:{cN:"value",eW:true,eE:true,c:[b,a.NM,a.QSM,a.ASM,a.CBLCLM,{cN:"hexcolor",b:"\\#[0-9A-F]+"},{cN:"important",b:"!important"}]}}]}]}]}}(hljs);hljs.LANGUAGES.xml=function(a){var c="[A-Za-z0-9\\._:-]+";var b={eW:true,c:[{cN:"attribute",b:c,r:0},{b:'="',rB:true,e:'"',c:[{cN:"value",b:'"',eW:true}]},{b:"='",rB:true,e:"'",c:[{cN:"value",b:"'",eW:true}]},{b:"=",c:[{cN:"value",b:"[^\\s/>]+"}]}]};return{cI:true,c:[{cN:"pi",b:"<\\?",e:"\\?>",r:10},{cN:"doctype",b:"",r:10,c:[{b:"\\[",e:"\\]"}]},{cN:"comment",b:"",r:10},{cN:"cdata",b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"",rE:true,sL:"css"}},{cN:"tag",b:"