refactor: add examples of chart
This commit is contained in:
parent
e558634dda
commit
4262b148ca
10 changed files with 796 additions and 100 deletions
|
|
@ -37,7 +37,6 @@ func barChartRender(opt ChartOption, result *basicRenderResult) ([]*markPointRen
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// yRange := result.yRange
|
|
||||||
xRange := result.xRange
|
xRange := result.xRange
|
||||||
x0, x1 := xRange.GetRange(0)
|
x0, x1 := xRange.GetRange(0)
|
||||||
width := int(x1 - x0)
|
width := int(x1 - x0)
|
||||||
|
|
@ -45,6 +44,10 @@ func barChartRender(opt ChartOption, result *basicRenderResult) ([]*markPointRen
|
||||||
margin := 10
|
margin := 10
|
||||||
// 每一个bar之间的margin
|
// 每一个bar之间的margin
|
||||||
barMargin := 5
|
barMargin := 5
|
||||||
|
if width < 50 {
|
||||||
|
margin = 5
|
||||||
|
barMargin = 3
|
||||||
|
}
|
||||||
|
|
||||||
seriesCount := len(opt.SeriesList)
|
seriesCount := len(opt.SeriesList)
|
||||||
// 总的宽度-两个margin-(总数-1)的barMargin
|
// 总的宽度-两个margin-(总数-1)的barMargin
|
||||||
|
|
@ -80,9 +83,9 @@ func barChartRender(opt ChartOption, result *basicRenderResult) ([]*markPointRen
|
||||||
Series: &series,
|
Series: &series,
|
||||||
Range: yRange,
|
Range: yRange,
|
||||||
})
|
})
|
||||||
|
divideValues := xRange.AutoDivide()
|
||||||
for j, item := range series.Data {
|
for j, item := range series.Data {
|
||||||
x0, _ := xRange.GetRange(j)
|
x := divideValues[j]
|
||||||
x := int(x0)
|
|
||||||
x += margin
|
x += margin
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
x += i * (barWidth + barMargin)
|
x += i * (barWidth + barMargin)
|
||||||
|
|
|
||||||
21
chart.go
21
chart.go
|
|
@ -144,17 +144,24 @@ func (o *ChartOption) FillDefault(theme string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *ChartOption) getWidth() int {
|
func (o *ChartOption) getWidth() int {
|
||||||
if o.Width == 0 {
|
if o.Width != 0 {
|
||||||
return 600
|
|
||||||
}
|
|
||||||
return o.Width
|
return o.Width
|
||||||
|
}
|
||||||
|
if o.Parent != nil {
|
||||||
|
return o.Parent.Box.Width()
|
||||||
|
}
|
||||||
|
return 600
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *ChartOption) getHeight() int {
|
func (o *ChartOption) getHeight() int {
|
||||||
if o.Height == 0 {
|
|
||||||
return 400
|
if o.Height != 0 {
|
||||||
}
|
|
||||||
return o.Height
|
return o.Height
|
||||||
|
}
|
||||||
|
if o.Parent != nil {
|
||||||
|
return o.Parent.Box.Height()
|
||||||
|
}
|
||||||
|
return 400
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *ChartOption) newYRange(axisIndex int) Range {
|
func (o *ChartOption) newYRange(axisIndex int) Range {
|
||||||
|
|
@ -219,6 +226,7 @@ func Render(opt ChartOption) (*Draw, error) {
|
||||||
if len(opt.SeriesList) == 0 {
|
if len(opt.SeriesList) == 0 {
|
||||||
return nil, errors.New("series can not be nil")
|
return nil, errors.New("series can not be nil")
|
||||||
}
|
}
|
||||||
|
opt.FillDefault(opt.Theme)
|
||||||
|
|
||||||
lineSeries := make([]Series, 0)
|
lineSeries := make([]Series, 0)
|
||||||
barSeries := make([]Series, 0)
|
barSeries := make([]Series, 0)
|
||||||
|
|
@ -321,7 +329,6 @@ func Render(opt ChartOption) (*Draw, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func chartBasicRender(opt *ChartOption) (*basicRenderResult, error) {
|
func chartBasicRender(opt *ChartOption) (*basicRenderResult, error) {
|
||||||
opt.FillDefault(opt.Theme)
|
|
||||||
d, err := NewDraw(
|
d, err := NewDraw(
|
||||||
DrawOption{
|
DrawOption{
|
||||||
Type: opt.Type,
|
Type: opt.Type,
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
|
|
||||||
charts "github.com/vicanso/go-charts"
|
charts "github.com/vicanso/go-charts"
|
||||||
"github.com/wcharczuk/go-chart/v2"
|
"github.com/wcharczuk/go-chart/v2"
|
||||||
|
"github.com/wcharczuk/go-chart/v2/drawing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var html = `<!DOCTYPE html>
|
var html = `<!DOCTYPE html>
|
||||||
|
|
@ -13,7 +14,6 @@ var html = `<!DOCTYPE html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
<link type="text/css" rel="styleSheet" href="https://unpkg.com/normalize.css@8.0.1/normalize.css" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
|
|
@ -49,7 +49,7 @@ var html = `<!DOCTYPE html>
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
svg{
|
svg{
|
||||||
margin: auto auto 50px auto;
|
margin: auto auto 10px auto;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
@ -61,20 +61,565 @@ var html = `<!DOCTYPE html>
|
||||||
</html>
|
</html>
|
||||||
`
|
`
|
||||||
|
|
||||||
func indexHandler(w http.ResponseWriter, r *http.Request) {
|
func indexHandler(w http.ResponseWriter, req *http.Request) {
|
||||||
|
if req.URL.Path != "/" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
theme := req.URL.Query().Get("theme")
|
||||||
|
chartOptions := []charts.ChartOption{
|
||||||
|
// 普通折线图
|
||||||
|
{
|
||||||
|
Theme: theme,
|
||||||
|
Title: charts.TitleOption{
|
||||||
|
Text: "Line",
|
||||||
|
},
|
||||||
|
Legend: charts.NewLegendOption([]string{
|
||||||
|
"Email",
|
||||||
|
"Union Ads",
|
||||||
|
"Video Ads",
|
||||||
|
"Direct",
|
||||||
|
"Search Engine",
|
||||||
|
}),
|
||||||
|
XAxis: charts.NewXAxisOption([]string{
|
||||||
|
"Mon",
|
||||||
|
"Tue",
|
||||||
|
"Wed",
|
||||||
|
"Thu",
|
||||||
|
"Fri",
|
||||||
|
"Sat",
|
||||||
|
"Sun",
|
||||||
|
}),
|
||||||
|
SeriesList: []charts.Series{
|
||||||
|
charts.NewSeriesFromValues([]float64{
|
||||||
|
120,
|
||||||
|
132,
|
||||||
|
101,
|
||||||
|
134,
|
||||||
|
90,
|
||||||
|
230,
|
||||||
|
210,
|
||||||
|
}),
|
||||||
|
charts.NewSeriesFromValues([]float64{
|
||||||
|
220,
|
||||||
|
182,
|
||||||
|
191,
|
||||||
|
234,
|
||||||
|
290,
|
||||||
|
330,
|
||||||
|
310,
|
||||||
|
}),
|
||||||
|
charts.NewSeriesFromValues([]float64{
|
||||||
|
150,
|
||||||
|
232,
|
||||||
|
201,
|
||||||
|
154,
|
||||||
|
190,
|
||||||
|
330,
|
||||||
|
410,
|
||||||
|
}),
|
||||||
|
charts.NewSeriesFromValues([]float64{
|
||||||
|
320,
|
||||||
|
332,
|
||||||
|
301,
|
||||||
|
334,
|
||||||
|
390,
|
||||||
|
330,
|
||||||
|
320,
|
||||||
|
}),
|
||||||
|
charts.NewSeriesFromValues([]float64{
|
||||||
|
820,
|
||||||
|
932,
|
||||||
|
901,
|
||||||
|
934,
|
||||||
|
1290,
|
||||||
|
1330,
|
||||||
|
1320,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// 温度折线图
|
||||||
|
{
|
||||||
|
Theme: theme,
|
||||||
|
Title: charts.TitleOption{
|
||||||
|
Text: "Temperature Change in the Coming Week",
|
||||||
|
},
|
||||||
|
Padding: chart.Box{
|
||||||
|
Top: 20,
|
||||||
|
Left: 20,
|
||||||
|
Right: 30,
|
||||||
|
Bottom: 20,
|
||||||
|
},
|
||||||
|
Legend: charts.NewLegendOption([]string{
|
||||||
|
"Highest",
|
||||||
|
"Lowest",
|
||||||
|
}, charts.PositionRight),
|
||||||
|
XAxis: charts.NewXAxisOption([]string{
|
||||||
|
"Mon",
|
||||||
|
"Tue",
|
||||||
|
"Wed",
|
||||||
|
"Thu",
|
||||||
|
"Fri",
|
||||||
|
"Sat",
|
||||||
|
"Sun",
|
||||||
|
}, charts.FalseFlag()),
|
||||||
|
SeriesList: []charts.Series{
|
||||||
|
{
|
||||||
|
Data: charts.NewSeriesDataFromValues([]float64{
|
||||||
|
14,
|
||||||
|
11,
|
||||||
|
13,
|
||||||
|
11,
|
||||||
|
12,
|
||||||
|
12,
|
||||||
|
7,
|
||||||
|
}),
|
||||||
|
MarkPoint: charts.NewMarkPoint(charts.SeriesMarkDataTypeMax, charts.SeriesMarkDataTypeMin),
|
||||||
|
MarkLine: charts.NewMarkLine(charts.SeriesMarkDataTypeAverage),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Data: charts.NewSeriesDataFromValues([]float64{
|
||||||
|
1,
|
||||||
|
-2,
|
||||||
|
2,
|
||||||
|
5,
|
||||||
|
3,
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
}),
|
||||||
|
MarkLine: charts.NewMarkLine(charts.SeriesMarkDataTypeAverage),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// 柱状图
|
||||||
|
{
|
||||||
|
Theme: theme,
|
||||||
|
Title: charts.TitleOption{
|
||||||
|
Text: "Bar",
|
||||||
|
},
|
||||||
|
XAxis: charts.NewXAxisOption([]string{
|
||||||
|
"Mon",
|
||||||
|
"Tue",
|
||||||
|
"Wed",
|
||||||
|
"Thu",
|
||||||
|
"Fri",
|
||||||
|
"Sat",
|
||||||
|
"Sun",
|
||||||
|
}),
|
||||||
|
Legend: charts.NewLegendOption([]string{
|
||||||
|
"Rainfall",
|
||||||
|
"Evaporation",
|
||||||
|
}),
|
||||||
|
SeriesList: []charts.Series{
|
||||||
|
charts.NewSeriesFromValues([]float64{
|
||||||
|
120,
|
||||||
|
200,
|
||||||
|
150,
|
||||||
|
80,
|
||||||
|
70,
|
||||||
|
110,
|
||||||
|
130,
|
||||||
|
}, charts.ChartTypeBar),
|
||||||
|
{
|
||||||
|
Type: charts.ChartTypeBar,
|
||||||
|
Data: []charts.SeriesData{
|
||||||
|
{
|
||||||
|
Value: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: 190,
|
||||||
|
Style: chart.Style{
|
||||||
|
FillColor: drawing.Color{
|
||||||
|
R: 169,
|
||||||
|
G: 0,
|
||||||
|
B: 0,
|
||||||
|
A: 255,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: 230,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: 140,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: 180,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// 柱状图+mark
|
||||||
|
{
|
||||||
|
Theme: theme,
|
||||||
|
Title: charts.TitleOption{
|
||||||
|
Text: "Rainfall vs Evaporation",
|
||||||
|
Subtext: "Fake Data",
|
||||||
|
},
|
||||||
|
Padding: chart.Box{
|
||||||
|
Top: 20,
|
||||||
|
Right: 20,
|
||||||
|
Bottom: 20,
|
||||||
|
Left: 20,
|
||||||
|
},
|
||||||
|
XAxis: charts.NewXAxisOption([]string{
|
||||||
|
"Jan",
|
||||||
|
"Feb",
|
||||||
|
"Mar",
|
||||||
|
"Apr",
|
||||||
|
"May",
|
||||||
|
"Jun",
|
||||||
|
"Jul",
|
||||||
|
"Aug",
|
||||||
|
"Sep",
|
||||||
|
"Oct",
|
||||||
|
"Nov",
|
||||||
|
"Dec",
|
||||||
|
}),
|
||||||
|
Legend: charts.NewLegendOption([]string{
|
||||||
|
"Rainfall",
|
||||||
|
"Evaporation",
|
||||||
|
}, charts.PositionRight),
|
||||||
|
SeriesList: []charts.Series{
|
||||||
|
{
|
||||||
|
Type: charts.ChartTypeBar,
|
||||||
|
Data: charts.NewSeriesDataFromValues([]float64{
|
||||||
|
2.0,
|
||||||
|
4.9,
|
||||||
|
7.0,
|
||||||
|
23.2,
|
||||||
|
25.6,
|
||||||
|
76.7,
|
||||||
|
135.6,
|
||||||
|
162.2,
|
||||||
|
32.6,
|
||||||
|
20.0,
|
||||||
|
6.4,
|
||||||
|
3.3,
|
||||||
|
}),
|
||||||
|
MarkPoint: charts.NewMarkPoint(
|
||||||
|
charts.SeriesMarkDataTypeMax,
|
||||||
|
charts.SeriesMarkDataTypeMin,
|
||||||
|
),
|
||||||
|
MarkLine: charts.NewMarkLine(
|
||||||
|
charts.SeriesMarkDataTypeAverage,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: charts.ChartTypeBar,
|
||||||
|
Data: charts.NewSeriesDataFromValues([]float64{
|
||||||
|
2.6,
|
||||||
|
5.9,
|
||||||
|
9.0,
|
||||||
|
26.4,
|
||||||
|
28.7,
|
||||||
|
70.7,
|
||||||
|
175.6,
|
||||||
|
182.2,
|
||||||
|
48.7,
|
||||||
|
18.8,
|
||||||
|
6.0,
|
||||||
|
2.3,
|
||||||
|
}),
|
||||||
|
MarkPoint: charts.NewMarkPoint(
|
||||||
|
charts.SeriesMarkDataTypeMax,
|
||||||
|
charts.SeriesMarkDataTypeMin,
|
||||||
|
),
|
||||||
|
MarkLine: charts.NewMarkLine(
|
||||||
|
charts.SeriesMarkDataTypeAverage,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// 双Y轴示例
|
||||||
|
{
|
||||||
|
Theme: theme,
|
||||||
|
XAxis: charts.NewXAxisOption([]string{
|
||||||
|
"Jan",
|
||||||
|
"Feb",
|
||||||
|
"Mar",
|
||||||
|
"Apr",
|
||||||
|
"May",
|
||||||
|
"Jun",
|
||||||
|
"Jul",
|
||||||
|
"Aug",
|
||||||
|
"Sep",
|
||||||
|
"Oct",
|
||||||
|
"Nov",
|
||||||
|
"Dec",
|
||||||
|
}),
|
||||||
|
Legend: charts.NewLegendOption([]string{
|
||||||
|
"Evaporation",
|
||||||
|
"Precipitation",
|
||||||
|
"Temperature",
|
||||||
|
}),
|
||||||
|
YAxisList: []charts.YAxisOption{
|
||||||
|
{
|
||||||
|
Formatter: "{value}°C",
|
||||||
|
Color: drawing.Color{
|
||||||
|
R: 250,
|
||||||
|
G: 200,
|
||||||
|
B: 88,
|
||||||
|
A: 255,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Formatter: "{value}ml",
|
||||||
|
Color: drawing.Color{
|
||||||
|
R: 84,
|
||||||
|
G: 112,
|
||||||
|
B: 198,
|
||||||
|
A: 255,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SeriesList: []charts.Series{
|
||||||
|
{
|
||||||
|
Type: charts.ChartTypeBar,
|
||||||
|
Data: charts.NewSeriesDataFromValues([]float64{
|
||||||
|
2.0,
|
||||||
|
4.9,
|
||||||
|
7.0,
|
||||||
|
23.2,
|
||||||
|
25.6,
|
||||||
|
76.7,
|
||||||
|
135.6,
|
||||||
|
162.2,
|
||||||
|
32.6,
|
||||||
|
20.0,
|
||||||
|
6.4,
|
||||||
|
3.3,
|
||||||
|
}),
|
||||||
|
YAxisIndex: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: charts.ChartTypeBar,
|
||||||
|
Data: charts.NewSeriesDataFromValues([]float64{
|
||||||
|
2.6,
|
||||||
|
5.9,
|
||||||
|
9.0,
|
||||||
|
26.4,
|
||||||
|
28.7,
|
||||||
|
70.7,
|
||||||
|
175.6,
|
||||||
|
182.2,
|
||||||
|
48.7,
|
||||||
|
18.8,
|
||||||
|
6.0,
|
||||||
|
2.3,
|
||||||
|
}),
|
||||||
|
YAxisIndex: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Data: charts.NewSeriesDataFromValues([]float64{
|
||||||
|
2.0,
|
||||||
|
2.2,
|
||||||
|
3.3,
|
||||||
|
4.5,
|
||||||
|
6.3,
|
||||||
|
10.2,
|
||||||
|
20.3,
|
||||||
|
23.4,
|
||||||
|
23.0,
|
||||||
|
16.5,
|
||||||
|
12.0,
|
||||||
|
6.2,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// 饼图
|
||||||
|
{
|
||||||
|
Theme: theme,
|
||||||
|
Title: charts.TitleOption{
|
||||||
|
Text: "Referer of a Website",
|
||||||
|
Subtext: "Fake Data",
|
||||||
|
Left: charts.PositionCenter,
|
||||||
|
},
|
||||||
|
Legend: charts.LegendOption{
|
||||||
|
Orient: charts.OrientVertical,
|
||||||
|
Data: []string{
|
||||||
|
"Search Engine",
|
||||||
|
"Direct",
|
||||||
|
"Email",
|
||||||
|
"Union Ads",
|
||||||
|
"Video Ads",
|
||||||
|
},
|
||||||
|
Left: charts.PositionLeft,
|
||||||
|
},
|
||||||
|
SeriesList: charts.NewPieSeriesList([]float64{
|
||||||
|
1048,
|
||||||
|
735,
|
||||||
|
580,
|
||||||
|
484,
|
||||||
|
300,
|
||||||
|
}, charts.PieSeriesOption{
|
||||||
|
LabelShow: true,
|
||||||
|
Radius: "35%",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
// 多图展示
|
||||||
|
{
|
||||||
|
Theme: theme,
|
||||||
|
Legend: charts.LegendOption{
|
||||||
|
Top: "-90",
|
||||||
|
Data: []string{
|
||||||
|
"Milk Tea",
|
||||||
|
"Matcha Latte",
|
||||||
|
"Cheese Cocoa",
|
||||||
|
"Walnut Brownie",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Padding: chart.Box{
|
||||||
|
Top: 100,
|
||||||
|
},
|
||||||
|
XAxis: charts.NewXAxisOption([]string{
|
||||||
|
"2012",
|
||||||
|
"2013",
|
||||||
|
"2014",
|
||||||
|
"2015",
|
||||||
|
"2016",
|
||||||
|
"2017",
|
||||||
|
}),
|
||||||
|
YAxisList: []charts.YAxisOption{
|
||||||
|
{
|
||||||
|
|
||||||
|
Min: charts.NewFloatPoint(0),
|
||||||
|
Max: charts.NewFloatPoint(90),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SeriesList: []charts.Series{
|
||||||
|
charts.NewSeriesFromValues([]float64{
|
||||||
|
56.5,
|
||||||
|
82.1,
|
||||||
|
88.7,
|
||||||
|
70.1,
|
||||||
|
53.4,
|
||||||
|
85.1,
|
||||||
|
}),
|
||||||
|
charts.NewSeriesFromValues([]float64{
|
||||||
|
51.1,
|
||||||
|
51.4,
|
||||||
|
55.1,
|
||||||
|
53.3,
|
||||||
|
73.8,
|
||||||
|
68.7,
|
||||||
|
}),
|
||||||
|
charts.NewSeriesFromValues([]float64{
|
||||||
|
40.1,
|
||||||
|
62.2,
|
||||||
|
69.5,
|
||||||
|
36.4,
|
||||||
|
45.2,
|
||||||
|
32.5,
|
||||||
|
}, charts.ChartTypeBar),
|
||||||
|
charts.NewSeriesFromValues([]float64{
|
||||||
|
25.2,
|
||||||
|
37.1,
|
||||||
|
41.2,
|
||||||
|
18,
|
||||||
|
33.9,
|
||||||
|
49.1,
|
||||||
|
}, charts.ChartTypeBar),
|
||||||
|
},
|
||||||
|
Children: []charts.ChartOption{
|
||||||
|
{
|
||||||
|
Legend: charts.LegendOption{
|
||||||
|
Show: charts.FalseFlag(),
|
||||||
|
Data: []string{
|
||||||
|
"Milk Tea",
|
||||||
|
"Matcha Latte",
|
||||||
|
"Cheese Cocoa",
|
||||||
|
"Walnut Brownie",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Height: 20,
|
||||||
|
Padding: chart.Box{
|
||||||
|
Left: 250,
|
||||||
|
Top: -80,
|
||||||
|
},
|
||||||
|
SeriesList: charts.NewPieSeriesList([]float64{
|
||||||
|
435.9,
|
||||||
|
354.3,
|
||||||
|
285.9,
|
||||||
|
204.5,
|
||||||
|
}, charts.PieSeriesOption{
|
||||||
|
LabelShow: true,
|
||||||
|
Radius: "35%",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
bytesList := make([][]byte, 0)
|
||||||
|
for _, opt := range chartOptions {
|
||||||
|
d, err := charts.Render(opt)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
buf, err := d.Bytes()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
bytesList = append(bytesList, buf)
|
||||||
|
}
|
||||||
|
data := bytes.ReplaceAll([]byte(html), []byte("{{body}}"), bytes.Join(bytesList, []byte("")))
|
||||||
|
w.Header().Set("Content-Type", "text/html")
|
||||||
|
w.Write(data)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func indexHandlerBak(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.URL.Path != "/" {
|
if r.URL.Path != "/" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
d, err := charts.NewLineChart(charts.LineChartOption{
|
zero := float64(0)
|
||||||
ChartOption: charts.ChartOption{
|
outputType := r.URL.Query().Get("type")
|
||||||
Theme: "dark",
|
chartOption := charts.ChartOption{
|
||||||
|
// Theme: "dark",
|
||||||
|
Type: outputType,
|
||||||
|
Title: charts.TitleOption{
|
||||||
|
Left: charts.PositionCenter,
|
||||||
|
Style: chart.Style{
|
||||||
|
FontColor: chart.ColorAlternateBlue,
|
||||||
|
},
|
||||||
|
SubtextStyle: chart.Style{
|
||||||
|
FontColor: chart.ColorRed,
|
||||||
|
},
|
||||||
|
Text: "Stacked Line",
|
||||||
|
Subtext: "Hello World!",
|
||||||
|
},
|
||||||
Padding: chart.Box{
|
Padding: chart.Box{
|
||||||
Left: 5,
|
Left: 5,
|
||||||
Top: 15,
|
Top: 15,
|
||||||
Bottom: 5,
|
Bottom: 5,
|
||||||
Right: 10,
|
Right: 10,
|
||||||
},
|
},
|
||||||
|
YAxisList: []charts.YAxisOption{
|
||||||
|
{
|
||||||
|
Min: &zero,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Formatter: "{value} °C",
|
||||||
|
// Max: charts.NewFloatPoint(24),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Legend: charts.LegendOption{
|
||||||
|
Data: []string{
|
||||||
|
"Email",
|
||||||
|
"Union Ads",
|
||||||
|
// "Video Ads",
|
||||||
|
},
|
||||||
|
Left: charts.PositionLeft,
|
||||||
|
// Orient: charts.OrientVertical,
|
||||||
|
},
|
||||||
XAxis: charts.XAxisOption{
|
XAxis: charts.XAxisOption{
|
||||||
Data: []string{
|
Data: []string{
|
||||||
"Mon",
|
"Mon",
|
||||||
|
|
@ -85,96 +630,164 @@ func indexHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
"Sat",
|
"Sat",
|
||||||
"Sun",
|
"Sun",
|
||||||
},
|
},
|
||||||
|
// SplitNumber: 4,
|
||||||
// BoundaryGap: charts.FalseFlag(),
|
// BoundaryGap: charts.FalseFlag(),
|
||||||
},
|
},
|
||||||
SeriesList: []charts.Series{
|
SeriesList: []charts.Series{
|
||||||
{
|
{
|
||||||
Data: []charts.SeriesData{
|
// Type: charts.ChartTypeBar,
|
||||||
|
MarkPoint: charts.SeriesMarkPoint{
|
||||||
|
Data: []charts.SeriesMarkPointData{
|
||||||
{
|
{
|
||||||
Value: 150,
|
Type: "max",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Value: 230,
|
Type: "min",
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MarkLine: charts.SeriesMarkLine{
|
||||||
|
Data: []charts.SeriesMarkLineData{
|
||||||
|
// {
|
||||||
|
// Type: "max",
|
||||||
|
// },
|
||||||
{
|
{
|
||||||
Value: 224,
|
Type: "average",
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Data: charts.NewSeriesDataFromValues([]float64{
|
||||||
|
2.0,
|
||||||
|
4.9,
|
||||||
|
7.0,
|
||||||
|
23.2,
|
||||||
|
25.6,
|
||||||
|
76.7,
|
||||||
|
135.6,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// // Type: charts.ChartTypeBar,
|
||||||
|
// Data: charts.NewSeriesDataFromValues([]float64{
|
||||||
|
// 2.6,
|
||||||
|
// 5.9,
|
||||||
|
// 9.0,
|
||||||
|
// 26.4,
|
||||||
|
// 28.7,
|
||||||
|
// 70.7,
|
||||||
|
// 175.6,
|
||||||
|
// }),
|
||||||
|
// },
|
||||||
{
|
{
|
||||||
Value: 218,
|
Data: charts.NewSeriesDataFromValues([]float64{
|
||||||
|
2.0,
|
||||||
|
2.2,
|
||||||
|
3.3,
|
||||||
|
4.5,
|
||||||
|
6.3,
|
||||||
|
10.2,
|
||||||
|
20.3,
|
||||||
|
}),
|
||||||
|
YAxisIndex: 1,
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
Value: 135,
|
// Data: []charts.SeriesData{
|
||||||
|
// {
|
||||||
|
// Value: 220,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// Value: 182,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// Value: 191,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// Value: 234,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// Value: 290,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// Value: 330,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// Value: 310,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// Data: []charts.SeriesData{
|
||||||
|
// {
|
||||||
|
// Value: 150,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// Value: 232,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// Value: 201,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// Value: 154,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// Value: 190,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// Value: 330,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// Value: 410,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
},
|
},
|
||||||
{
|
// Children: []charts.ChartOption{
|
||||||
Value: 147,
|
// {
|
||||||
},
|
// Padding: chart.Box{
|
||||||
{
|
// Left: 350,
|
||||||
Value: 260,
|
// Top: 0,
|
||||||
},
|
// },
|
||||||
},
|
// Legend: charts.LegendOption{
|
||||||
},
|
// Show: charts.FalseFlag(),
|
||||||
{
|
// },
|
||||||
Data: []charts.SeriesData{
|
// Width: 150,
|
||||||
{
|
// Height: 150,
|
||||||
Value: 220,
|
// SeriesList: []charts.Series{
|
||||||
},
|
// charts.NewSeriesFromValues([]float64{
|
||||||
{
|
// 1048,
|
||||||
Value: 182,
|
// }, charts.ChartTypePie),
|
||||||
},
|
// {
|
||||||
{
|
// Data: charts.NewSeriesDataFromValues([]float64{
|
||||||
Value: 191,
|
// 735,
|
||||||
},
|
// }),
|
||||||
{
|
// Radius: "50%",
|
||||||
Value: 234,
|
// Name: "test",
|
||||||
},
|
// },
|
||||||
{
|
// charts.NewSeriesFromValues([]float64{
|
||||||
Value: 290,
|
// 580,
|
||||||
},
|
// }),
|
||||||
{
|
// charts.NewSeriesFromValues([]float64{
|
||||||
Value: 330,
|
// 484,
|
||||||
},
|
// }),
|
||||||
{
|
// },
|
||||||
Value: 310,
|
// },
|
||||||
},
|
// },
|
||||||
},
|
}
|
||||||
},
|
d, err := charts.Render(chartOption)
|
||||||
{
|
|
||||||
Data: []charts.SeriesData{
|
|
||||||
{
|
|
||||||
Value: 150,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: 232,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: 201,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: 154,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: 190,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: 330,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: 410,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
buf, _ := d.Bytes()
|
buf, _ := d.Bytes()
|
||||||
|
|
||||||
|
if outputType == "png" {
|
||||||
|
w.Header().Set("Content-Type", "image/png")
|
||||||
|
w.Write(buf)
|
||||||
|
} else {
|
||||||
data := bytes.ReplaceAll([]byte(html), []byte("{{body}}"), buf)
|
data := bytes.ReplaceAll([]byte(html), []byte("{{body}}"), buf)
|
||||||
w.Header().Set("Content-Type", "text/html")
|
w.Header().Set("Content-Type", "text/html")
|
||||||
w.Write(data)
|
w.Write(data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
|
||||||
11
legend.go
11
legend.go
|
|
@ -49,6 +49,17 @@ type LegendOption struct {
|
||||||
// The layout orientation of legend, it can be horizontal or vertical, default is horizontal.
|
// The layout orientation of legend, it can be horizontal or vertical, default is horizontal.
|
||||||
Orient string
|
Orient string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewLegendOption(data []string, position ...string) LegendOption {
|
||||||
|
opt := LegendOption{
|
||||||
|
Data: data,
|
||||||
|
}
|
||||||
|
if len(position) != 0 {
|
||||||
|
opt.Left = position[0]
|
||||||
|
}
|
||||||
|
return opt
|
||||||
|
}
|
||||||
|
|
||||||
type legend struct {
|
type legend struct {
|
||||||
d *Draw
|
d *Draw
|
||||||
opt *LegendOption
|
opt *LegendOption
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,6 @@ func getPieStyle(theme *Theme, index int) chart.Style {
|
||||||
StrokeWidth: 1,
|
StrokeWidth: 1,
|
||||||
FillColor: seriesColor,
|
FillColor: seriesColor,
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func pieChartRender(opt ChartOption, result *basicRenderResult) error {
|
func pieChartRender(opt ChartOption, result *basicRenderResult) error {
|
||||||
|
|
@ -86,7 +85,11 @@ func pieChartRender(opt ChartOption, result *basicRenderResult) error {
|
||||||
if radius <= 0 {
|
if radius <= 0 {
|
||||||
radius = float64(diameter) * defaultRadiusPercent
|
radius = float64(diameter) * defaultRadiusPercent
|
||||||
}
|
}
|
||||||
labelRadius := radius + 20
|
labelLineWidth := 15
|
||||||
|
if radius < 50 {
|
||||||
|
labelLineWidth = 10
|
||||||
|
}
|
||||||
|
labelRadius := radius + float64(labelLineWidth)
|
||||||
|
|
||||||
seriesNames := opt.Legend.Data
|
seriesNames := opt.Legend.Data
|
||||||
|
|
||||||
|
|
@ -126,7 +129,7 @@ func pieChartRender(opt ChartOption, result *basicRenderResult) error {
|
||||||
endy := cy + int(labelRadius*math.Sin(angle))
|
endy := cy + int(labelRadius*math.Sin(angle))
|
||||||
d.moveTo(startx, starty)
|
d.moveTo(startx, starty)
|
||||||
d.lineTo(endx, endy)
|
d.lineTo(endx, endy)
|
||||||
offset := 30
|
offset := labelLineWidth
|
||||||
if endx < cx {
|
if endx < cx {
|
||||||
offset *= -1
|
offset *= -1
|
||||||
}
|
}
|
||||||
|
|
|
||||||
3
range.go
3
range.go
|
|
@ -94,6 +94,9 @@ func (r *Range) GetRange(index int) (float64, float64) {
|
||||||
unit := float64(r.Size) / float64(r.divideCount)
|
unit := float64(r.Size) / float64(r.divideCount)
|
||||||
return unit * float64(index), unit * float64(index+1)
|
return unit * float64(index), unit * float64(index+1)
|
||||||
}
|
}
|
||||||
|
func (r *Range) AutoDivide() []int {
|
||||||
|
return autoDivide(r.Size, r.divideCount)
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Range) getWidth(value float64) int {
|
func (r *Range) getWidth(value float64) int {
|
||||||
v := value / (r.Max - r.Min)
|
v := value / (r.Max - r.Min)
|
||||||
|
|
|
||||||
29
series.go
29
series.go
|
|
@ -95,6 +95,35 @@ type Series struct {
|
||||||
MarkLine SeriesMarkLine
|
MarkLine SeriesMarkLine
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PieSeriesOption struct {
|
||||||
|
Radius string
|
||||||
|
LabelShow bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPieSeriesList(values []float64, opts ...PieSeriesOption) []Series {
|
||||||
|
result := make([]Series, len(values))
|
||||||
|
var opt PieSeriesOption
|
||||||
|
if len(opts) != 0 {
|
||||||
|
opt = opts[0]
|
||||||
|
}
|
||||||
|
for index, v := range values {
|
||||||
|
s := Series{
|
||||||
|
Type: ChartTypePie,
|
||||||
|
Data: []SeriesData{
|
||||||
|
{
|
||||||
|
Value: v,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Radius: opt.Radius,
|
||||||
|
Label: SeriesLabel{
|
||||||
|
Show: opt.LabelShow,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
result[index] = s
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
type seriesSummary struct {
|
type seriesSummary struct {
|
||||||
MaxIndex int
|
MaxIndex int
|
||||||
MaxValue float64
|
MaxValue float64
|
||||||
|
|
|
||||||
8
util.go
8
util.go
|
|
@ -40,6 +40,14 @@ func FalseFlag() *bool {
|
||||||
return &f
|
return &f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ceilFloatToInt(value float64) int {
|
||||||
|
i := int(value)
|
||||||
|
if value == float64(i) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
return i + 1
|
||||||
|
}
|
||||||
|
|
||||||
func getDefaultInt(value, defaultValue int) int {
|
func getDefaultInt(value, defaultValue int) int {
|
||||||
if value == 0 {
|
if value == 0 {
|
||||||
return defaultValue
|
return defaultValue
|
||||||
|
|
|
||||||
10
xaxis.go
10
xaxis.go
|
|
@ -40,6 +40,16 @@ type XAxisOption struct {
|
||||||
SplitNumber int
|
SplitNumber int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewXAxisOption(data []string, boundaryGap ...*bool) XAxisOption {
|
||||||
|
opt := XAxisOption{
|
||||||
|
Data: data,
|
||||||
|
}
|
||||||
|
if len(boundaryGap) != 0 {
|
||||||
|
opt.BoundaryGap = boundaryGap[0]
|
||||||
|
}
|
||||||
|
return opt
|
||||||
|
}
|
||||||
|
|
||||||
// drawXAxis draws x axis, and returns the height, range of if.
|
// drawXAxis draws x axis, and returns the height, range of if.
|
||||||
func drawXAxis(p *Draw, opt *XAxisOption, yAxisCount int) (int, *Range, error) {
|
func drawXAxis(p *Draw, opt *XAxisOption, yAxisCount int) (int, *Range, error) {
|
||||||
if opt.Hidden {
|
if opt.Hidden {
|
||||||
|
|
|
||||||
11
yaxis.go
11
yaxis.go
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/wcharczuk/go-chart/v2"
|
"github.com/wcharczuk/go-chart/v2"
|
||||||
|
"github.com/wcharczuk/go-chart/v2/drawing"
|
||||||
)
|
)
|
||||||
|
|
||||||
type YAxisOption struct {
|
type YAxisOption struct {
|
||||||
|
|
@ -37,15 +38,19 @@ type YAxisOption struct {
|
||||||
Hidden bool
|
Hidden bool
|
||||||
// Formatter for y axis text value
|
// Formatter for y axis text value
|
||||||
Formatter string
|
Formatter string
|
||||||
|
// Color for y axis
|
||||||
|
Color drawing.Color
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO 长度是否可以变化
|
||||||
const YAxisWidth = 40
|
const YAxisWidth = 40
|
||||||
|
|
||||||
func drawYAxis(p *Draw, opt *ChartOption, axisIndex, xAxisHeight int, padding chart.Box) (*Range, error) {
|
func drawYAxis(p *Draw, opt *ChartOption, axisIndex, xAxisHeight int, padding chart.Box) (*Range, error) {
|
||||||
theme := NewTheme(opt.Theme)
|
theme := NewTheme(opt.Theme)
|
||||||
yRange := opt.newYRange(axisIndex)
|
yRange := opt.newYRange(axisIndex)
|
||||||
values := yRange.Values()
|
values := yRange.Values()
|
||||||
formatter := opt.YAxisList[axisIndex].Formatter
|
yAxis := opt.YAxisList[axisIndex]
|
||||||
|
formatter := yAxis.Formatter
|
||||||
if len(formatter) != 0 {
|
if len(formatter) != 0 {
|
||||||
for index, text := range values {
|
for index, text := range values {
|
||||||
values[index] = strings.ReplaceAll(formatter, "{value}", text)
|
values[index] = strings.ReplaceAll(formatter, "{value}", text)
|
||||||
|
|
@ -62,6 +67,10 @@ func drawYAxis(p *Draw, opt *ChartOption, axisIndex, xAxisHeight int, padding ch
|
||||||
SplitLineColor: theme.GetAxisSplitLineColor(),
|
SplitLineColor: theme.GetAxisSplitLineColor(),
|
||||||
SplitLineShow: true,
|
SplitLineShow: true,
|
||||||
}
|
}
|
||||||
|
if !yAxis.Color.IsZero() {
|
||||||
|
style.FontColor = yAxis.Color
|
||||||
|
style.StrokeColor = yAxis.Color
|
||||||
|
}
|
||||||
width := NewAxis(p, data, style).measure().Width
|
width := NewAxis(p, data, style).measure().Width
|
||||||
|
|
||||||
yAxisCount := len(opt.YAxisList)
|
yAxisCount := len(opt.YAxisList)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue