refactor: enhance chart render function

This commit is contained in:
vicanso 2022-06-17 23:37:21 +08:00
parent 65a1cb11ad
commit 38c4978e44
20 changed files with 665 additions and 462 deletions

View file

@ -49,25 +49,21 @@ func writeFile(file string, buf []byte) error {
} }
func chartsRender() ([]byte, error) { func chartsRender() ([]byte, error) {
d, err := charts.LineRender([][]float64{ values := [][]float64{
{ {
150, 120,
132,
101,
134,
90,
230, 230,
224, 210,
218,
135,
147,
260,
}, },
}, }
// output type p, err := charts.LineRender(
charts.PNGTypeOption(), values,
// title charts.TitleTextOptionFunc("Line"),
charts.TitleOptionFunc(charts.TitleOption{ charts.XAxisDataOptionFunc([]string{
Text: "Line",
}),
// x axis
charts.XAxisOptionFunc(charts.NewXAxisOption([]string{
"Mon", "Mon",
"Tue", "Tue",
"Wed", "Wed",
@ -75,12 +71,12 @@ func chartsRender() ([]byte, error) {
"Fri", "Fri",
"Sat", "Sat",
"Sun", "Sun",
})), }),
) )
if err != nil { if err != nil {
return nil, err return nil, err
} }
return d.Bytes() return p.Bytes()
} }
func echartsRender() ([]byte, error) { func echartsRender() ([]byte, error) {

View file

@ -34,7 +34,7 @@ type barChart struct {
func NewBarChart(p *Painter, opt BarChartOption) *barChart { func NewBarChart(p *Painter, opt BarChartOption) *barChart {
if opt.Theme == nil { if opt.Theme == nil {
opt.Theme = NewTheme("") opt.Theme = defaultTheme
} }
return &barChart{ return &barChart{
p: p, p: p,

View file

@ -66,6 +66,182 @@ type ChartOption struct {
Children []ChartOption Children []ChartOption
} }
// OptionFunc option function
type OptionFunc func(opt *ChartOption)
// PNGTypeOption set png type of chart's output
func PNGTypeOption() OptionFunc {
return TypeOptionFunc(ChartOutputPNG)
}
// TypeOptionFunc set type of chart's output
func TypeOptionFunc(t string) OptionFunc {
return func(opt *ChartOption) {
opt.Type = t
}
}
// FontFamilyOptionFunc set font family of chart
func FontFamilyOptionFunc(fontFamily string) OptionFunc {
return func(opt *ChartOption) {
opt.FontFamily = fontFamily
}
}
// ThemeOptionFunc set them of chart
func ThemeOptionFunc(theme string) OptionFunc {
return func(opt *ChartOption) {
opt.Theme = theme
}
}
// TitleOptionFunc set title of chart
func TitleOptionFunc(title TitleOption) OptionFunc {
return func(opt *ChartOption) {
opt.Title = title
}
}
// TitleTextOptionFunc set title text of chart
func TitleTextOptionFunc(text string) OptionFunc {
return func(opt *ChartOption) {
opt.Title.Text = text
}
}
// LegendOptionFunc set legend of chart
func LegendOptionFunc(legend LegendOption) OptionFunc {
return func(opt *ChartOption) {
opt.Legend = legend
}
}
// LegendLabelsOptionFunc set legend labels of chart
func LegendLabelsOptionFunc(labels []string, left ...string) OptionFunc {
return func(opt *ChartOption) {
opt.Legend = NewLegendOption(labels, left...)
}
}
// XAxisOptionFunc set x axis of chart
func XAxisOptionFunc(xAxisOption XAxisOption) OptionFunc {
return func(opt *ChartOption) {
opt.XAxis = xAxisOption
}
}
// XAxisDataOptionFunc set x axis data of chart
func XAxisDataOptionFunc(data []string, boundaryGap ...*bool) OptionFunc {
return func(opt *ChartOption) {
opt.XAxis = NewXAxisOption(data, boundaryGap...)
}
}
// YAxisOptionFunc set y axis of chart, support two y axis
func YAxisOptionFunc(yAxisOption ...YAxisOption) OptionFunc {
return func(opt *ChartOption) {
opt.YAxisOptions = yAxisOption
}
}
// YAxisDataOptionFunc set y axis data of chart
func YAxisDataOptionFunc(data []string) OptionFunc {
return func(opt *ChartOption) {
opt.YAxisOptions = NewYAxisOptions(data)
}
}
// WidthOptionFunc set width of chart
func WidthOptionFunc(width int) OptionFunc {
return func(opt *ChartOption) {
opt.Width = width
}
}
// HeightOptionFunc set height of chart
func HeightOptionFunc(height int) OptionFunc {
return func(opt *ChartOption) {
opt.Height = height
}
}
// PaddingOptionFunc set padding of chart
func PaddingOptionFunc(padding Box) OptionFunc {
return func(opt *ChartOption) {
opt.Padding = padding
}
}
// BoxOptionFunc set box of chart
func BoxOptionFunc(box Box) OptionFunc {
return func(opt *ChartOption) {
opt.Box = box
}
}
// PieSeriesShowLabel set pie series show label
func PieSeriesShowLabel() OptionFunc {
return func(opt *ChartOption) {
for index := range opt.SeriesList {
opt.SeriesList[index].Label.Show = true
}
}
}
// ChildOptionFunc add child chart
func ChildOptionFunc(child ...ChartOption) OptionFunc {
return func(opt *ChartOption) {
if opt.Children == nil {
opt.Children = make([]ChartOption, 0)
}
opt.Children = append(opt.Children, child...)
}
}
// RadarIndicatorOptionFunc set radar indicator of chart
func RadarIndicatorOptionFunc(names []string, values []float64) OptionFunc {
return func(opt *ChartOption) {
if len(names) != len(values) {
return
}
indicators := make([]RadarIndicator, len(names))
for index, name := range names {
indicators[index] = RadarIndicator{
Name: name,
Max: values[index],
}
}
opt.RadarIndicators = indicators
}
}
// BackgroundColorOptionFunc set background color of chart
func BackgroundColorOptionFunc(color Color) OptionFunc {
return func(opt *ChartOption) {
opt.BackgroundColor = color
}
}
// MarkLineOptionFunc set mark line for series of chart
func MarkLineOptionFunc(seriesIndex int, markLineTypes ...string) OptionFunc {
return func(opt *ChartOption) {
if len(opt.SeriesList) <= seriesIndex {
return
}
opt.SeriesList[seriesIndex].MarkLine = NewMarkLine(markLineTypes...)
}
}
// MarkPointOptionFunc set mark point for series of chart
func MarkPointOptionFunc(seriesIndex int, markPointTypes ...string) OptionFunc {
return func(opt *ChartOption) {
if len(opt.SeriesList) <= seriesIndex {
return
}
opt.SeriesList[seriesIndex].MarkPoint = NewMarkPoint(markPointTypes...)
}
}
func (o *ChartOption) fillDefault() { func (o *ChartOption) fillDefault() {
t := NewTheme(o.Theme) t := NewTheme(o.Theme)
o.theme = t o.theme = t
@ -90,11 +266,11 @@ func (o *ChartOption) fillDefault() {
o.BackgroundColor = t.GetBackgroundColor() o.BackgroundColor = t.GetBackgroundColor()
} }
if o.Padding.IsZero() { if o.Padding.IsZero() {
o.Padding = chart.Box{ o.Padding = Box{
Top: 10, Top: 20,
Right: 10, Right: 20,
Bottom: 10, Bottom: 20,
Left: 10, Left: 20,
} }
} }
// legend与series name的关联 // legend与series name的关联
@ -118,3 +294,55 @@ func (o *ChartOption) fillDefault() {
}) })
} }
} }
// LineRender line chart render
func LineRender(values [][]float64, opts ...OptionFunc) (*Painter, error) {
seriesList := NewSeriesListDataFromValues(values, ChartTypeLine)
return Render(ChartOption{
SeriesList: seriesList,
}, opts...)
}
// BarRender bar chart render
func BarRender(values [][]float64, opts ...OptionFunc) (*Painter, error) {
seriesList := NewSeriesListDataFromValues(values, ChartTypeBar)
return Render(ChartOption{
SeriesList: seriesList,
}, opts...)
}
// HorizontalBarRender horizontal bar chart render
func HorizontalBarRender(values [][]float64, opts ...OptionFunc) (*Painter, error) {
seriesList := NewSeriesListDataFromValues(values, ChartTypeHorizontalBar)
return Render(ChartOption{
SeriesList: seriesList,
}, opts...)
}
// PieRender pie chart render
func PieRender(values []float64, opts ...OptionFunc) (*Painter, error) {
return Render(ChartOption{
SeriesList: NewPieSeriesList(values),
}, opts...)
}
// RadarRender radar chart render
func RadarRender(values [][]float64, opts ...OptionFunc) (*Painter, error) {
seriesList := NewSeriesListDataFromValues(values, ChartTypeRadar)
return Render(ChartOption{
SeriesList: seriesList,
}, opts...)
}
// FunnelRender funnel chart render
func FunnelRender(values []float64, opts ...OptionFunc) (*Painter, error) {
seriesList := make(SeriesList, len(values))
for index, value := range values {
seriesList[index] = NewSeriesFromValues([]float64{
value,
}, ChartTypeFunnel)
}
return Render(ChartOption{
SeriesList: seriesList,
}, opts...)
}

View file

@ -239,7 +239,10 @@ func doRender(renderers ...Renderer) error {
return nil return nil
} }
func Render(opt ChartOption) (*Painter, error) { func Render(opt ChartOption, opts ...OptionFunc) (*Painter, error) {
for _, fn := range opts {
fn(&opt)
}
opt.fillDefault() opt.fillDefault()
isChild := true isChild := true
@ -398,6 +401,12 @@ func Render(opt ChartOption) (*Painter, error) {
} }
for _, item := range opt.Children { for _, item := range opt.Children {
item.Parent = p item.Parent = p
if item.Theme == "" {
item.Theme = opt.Theme
}
if item.FontFamily == "" {
item.FontFamily = opt.FontFamily
}
_, err = Render(item) _, err = Render(item)
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -130,6 +130,7 @@ type EChartsXAxisData struct {
BoundaryGap *bool `json:"boundaryGap"` BoundaryGap *bool `json:"boundaryGap"`
SplitNumber int `json:"splitNumber"` SplitNumber int `json:"splitNumber"`
Data []string `json:"data"` Data []string `json:"data"`
Type string `json:"type"`
} }
type EChartsXAxis struct { type EChartsXAxis struct {
Data []EChartsXAxisData Data []EChartsXAxisData
@ -155,6 +156,7 @@ type EChartsYAxisData struct {
Color string `json:"color"` Color string `json:"color"`
} `json:"lineStyle"` } `json:"lineStyle"`
} `json:"axisLine"` } `json:"axisLine"`
Data []string `json:"data"`
} }
type EChartsYAxis struct { type EChartsYAxis struct {
Data []EChartsYAxisData `json:"data"` Data []EChartsYAxisData `json:"data"`
@ -453,6 +455,21 @@ func (eo *EChartsOption) ToOption() ChartOption {
Box: eo.Box, Box: eo.Box,
SeriesList: eo.Series.ToSeriesList(), SeriesList: eo.Series.ToSeriesList(),
} }
isHorizontalChart := false
for _, item := range eo.XAxis.Data {
if item.Type == "value" {
isHorizontalChart = true
}
}
if isHorizontalChart {
for index := range o.SeriesList {
series := o.SeriesList[index]
if series.Type == ChartTypeBar {
o.SeriesList[index].Type = ChartTypeHorizontalBar
}
}
}
if len(eo.XAxis.Data) != 0 { if len(eo.XAxis.Data) != 0 {
xAxisData := eo.XAxis.Data[0] xAxisData := eo.XAxis.Data[0]
o.XAxis = XAxisOption{ o.XAxis = XAxisOption{
@ -468,6 +485,7 @@ func (eo *EChartsOption) ToOption() ChartOption {
Max: item.Max, Max: item.Max,
Formatter: item.AxisLabel.Formatter, Formatter: item.AxisLabel.Formatter,
Color: parseColor(item.AxisLine.LineStyle.Color), Color: parseColor(item.AxisLine.LineStyle.Color),
Data: item.Data,
} }
} }
o.YAxisOptions = yAxisOptions o.YAxisOptions = yAxisOptions

View file

@ -24,26 +24,39 @@ func writeFile(buf []byte) error {
} }
func main() { func main() {
p, err := charts.NewPainter(charts.PainterOptions{ values := [][]float64{
Width: 800, {
Height: 600, 2.0,
Type: charts.ChartOutputPNG, 4.9,
}) 7.0,
if err != nil { 23.2,
panic(err) 25.6,
76.7,
135.6,
162.2,
32.6,
20.0,
6.4,
3.3,
},
{
2.6,
5.9,
9.0,
26.4,
28.7,
70.7,
175.6,
182.2,
48.7,
18.8,
6.0,
2.3,
},
} }
_, err = charts.NewBarChart(p, charts.BarChartOption{ p, err := charts.BarRender(
Title: charts.TitleOption{ values,
Text: "Rainfall vs Evaporation", charts.XAxisDataOptionFunc([]string{
Subtext: "Fake Data",
},
Padding: charts.Box{
Top: 20,
Right: 20,
Bottom: 20,
Left: 20,
},
XAxis: charts.NewXAxisOption([]string{
"Jan", "Jan",
"Feb", "Feb",
"Mar", "Mar",
@ -57,61 +70,24 @@ func main() {
"Nov", "Nov",
"Dec", "Dec",
}), }),
Legend: charts.NewLegendOption([]string{ charts.LegendLabelsOptionFunc([]string{
"Rainfall", "Rainfall",
"Evaporation", "Evaporation",
}, charts.PositionRight), }, charts.PositionRight),
SeriesList: []charts.Series{ charts.MarkLineOptionFunc(0, charts.SeriesMarkDataTypeAverage),
{ charts.MarkPointOptionFunc(0, charts.SeriesMarkDataTypeMax,
Type: charts.ChartTypeBar, charts.SeriesMarkDataTypeMin),
Data: charts.NewSeriesDataFromValues([]float64{ // custom option func
2.0, func(opt *charts.ChartOption) {
4.9, opt.SeriesList[1].MarkPoint = charts.NewMarkPoint(
7.0, charts.SeriesMarkDataTypeMax,
23.2, charts.SeriesMarkDataTypeMin,
25.6, )
76.7, opt.SeriesList[1].MarkLine = charts.NewMarkLine(
135.6, charts.SeriesMarkDataTypeAverage,
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,
),
},
}, },
}).Render() )
if err != nil { if err != nil {
panic(err) panic(err)
} }

View file

@ -73,6 +73,7 @@ func handler(w http.ResponseWriter, req *http.Request, chartOptions []charts.Cha
bytesList := make([][]byte, 0) bytesList := make([][]byte, 0)
for _, opt := range chartOptions { for _, opt := range chartOptions {
opt.Theme = theme opt.Theme = theme
opt.Type = charts.ChartOutputSVG
d, err := charts.Render(opt) d, err := charts.Render(opt)
if err != nil { if err != nil {
panic(err) panic(err)
@ -1055,6 +1056,64 @@ func echartsHandler(w http.ResponseWriter, req *http.Request) {
} }
] ]
}`, }`,
`{
"title": {
"text": "World Population"
},
"tooltip": {
"trigger": "axis",
"axisPointer": {
"type": "shadow"
}
},
"legend": {},
"grid": {
"left": "3%",
"right": "4%",
"bottom": "3%",
"containLabel": true
},
"xAxis": {
"type": "value"
},
"yAxis": {
"type": "category",
"data": [
"Brazil",
"Indonesia",
"USA",
"India",
"China",
"World"
]
},
"series": [
{
"name": "2011",
"type": "bar",
"data": [
18203,
23489,
29034,
104970,
131744,
630230
]
},
{
"name": "2012",
"type": "bar",
"data": [
19325,
23438,
31000,
121594,
134141,
681807
]
}
]
}`,
`{ `{
"title": { "title": {
"text": "Rainfall vs Evaporation", "text": "Rainfall vs Evaporation",

View file

@ -24,64 +24,24 @@ func writeFile(buf []byte) error {
} }
func main() { func main() {
p, err := charts.NewPainter(charts.PainterOptions{ values := []float64{
Width: 800, 100,
Height: 600, 80,
Type: charts.ChartOutputPNG, 60,
}) 40,
if err != nil { 20,
panic(err)
} }
_, err = charts.NewFunnelChart(p, charts.FunnelChartOption{ p, err := charts.FunnelRender(
Title: charts.TitleOption{ values,
Text: "Funnel", charts.TitleTextOptionFunc("Funnel"),
}, charts.LegendLabelsOptionFunc([]string{
Legend: charts.NewLegendOption([]string{
"Show", "Show",
"Click", "Click",
"Visit", "Visit",
"Inquiry", "Inquiry",
"Order", "Order",
}), }),
SeriesList: []charts.Series{ )
{
Type: charts.ChartTypeFunnel,
Name: "Show",
Data: charts.NewSeriesDataFromValues([]float64{
100,
}),
},
{
Type: charts.ChartTypeFunnel,
Name: "Click",
Data: charts.NewSeriesDataFromValues([]float64{
80,
}),
},
{
Type: charts.ChartTypeFunnel,
Name: "Visit",
Data: charts.NewSeriesDataFromValues([]float64{
60,
}),
},
{
Type: charts.ChartTypeFunnel,
Name: "Inquiry",
Data: charts.NewSeriesDataFromValues([]float64{
40,
}),
},
{
Type: charts.ChartTypeFunnel,
Name: "Order",
Data: charts.NewSeriesDataFromValues([]float64{
20,
}),
},
},
}).Render()
if err != nil { if err != nil {
panic(err) panic(err)
} }

View file

@ -24,29 +24,38 @@ func writeFile(buf []byte) error {
} }
func main() { func main() {
p, err := charts.NewPainter(charts.PainterOptions{ values := [][]float64{
Width: 800, {
Height: 600, 18203,
Type: charts.ChartOutputPNG, 23489,
}) 29034,
if err != nil { 104970,
panic(err) 131744,
} 630230,
_, err = charts.NewHorizontalBarChart(p, charts.HorizontalBarChartOption{
Title: charts.TitleOption{
Text: "World Population",
}, },
Padding: charts.Box{ {
19325,
23438,
31000,
121594,
134141,
681807,
},
}
p, err := charts.HorizontalBarRender(
values,
charts.TitleTextOptionFunc("World Population"),
charts.PaddingOptionFunc(charts.Box{
Top: 20, Top: 20,
Right: 40, Right: 40,
Bottom: 20, Bottom: 20,
Left: 20, Left: 20,
}, }),
Legend: charts.NewLegendOption([]string{ charts.LegendLabelsOptionFunc([]string{
"2011", "2011",
"2012", "2012",
}), }),
YAxisOptions: charts.NewYAxisOptions([]string{ charts.YAxisDataOptionFunc([]string{
"Brazil", "Brazil",
"Indonesia", "Indonesia",
"USA", "USA",
@ -54,31 +63,7 @@ func main() {
"China", "China",
"World", "World",
}), }),
SeriesList: []charts.Series{ )
{
Type: charts.ChartTypeHorizontalBar,
Data: charts.NewSeriesDataFromValues([]float64{
18203,
23489,
29034,
104970,
131744,
630230,
}),
},
{
Type: charts.ChartTypeHorizontalBar,
Data: charts.NewSeriesDataFromValues([]float64{
19325,
23438,
31000,
121594,
134141,
681807,
}),
},
},
}).Render()
if err != nil { if err != nil {
panic(err) panic(err)
} }

View file

@ -24,35 +24,57 @@ func writeFile(buf []byte) error {
} }
func main() { func main() {
p, err := charts.NewPainter(charts.PainterOptions{ values := [][]float64{
Width: 800, {
Height: 600, 120,
Type: charts.ChartOutputPNG, 132,
}) 101,
if err != nil { 134,
panic(err) 90,
230,
210,
},
{
220,
182,
191,
234,
290,
330,
310,
},
{
150,
232,
201,
154,
190,
330,
410,
},
{
320,
332,
301,
334,
390,
330,
320,
},
{
820,
932,
901,
934,
1290,
1330,
1320,
},
} }
_, err = charts.NewLineChart(p, charts.LineChartOption{ p, err := charts.LineRender(
Padding: charts.Box{ values,
Left: 10, charts.TitleTextOptionFunc("Line"),
Top: 10, charts.XAxisDataOptionFunc([]string{
Right: 10,
Bottom: 10,
},
Title: charts.TitleOption{
Text: "Line",
},
Legend: charts.LegendOption{
Data: []string{
"Email",
"Union Ads",
"Video Ads",
"Direct",
"Search Engine",
},
Left: charts.PositionCenter,
},
XAxis: charts.NewXAxisOption([]string{
"Mon", "Mon",
"Tue", "Tue",
"Wed", "Wed",
@ -61,54 +83,15 @@ func main() {
"Sat", "Sat",
"Sun", "Sun",
}), }),
SeriesList: charts.SeriesList{ charts.LegendLabelsOptionFunc([]string{
charts.NewSeriesFromValues([]float64{ "Email",
120, "Union Ads",
132, "Video Ads",
101, "Direct",
134, "Search Engine",
90, }, charts.PositionCenter),
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,
}),
},
}).Render()
if err != nil { if err != nil {
panic(err) panic(err)
} }

View file

@ -24,27 +24,27 @@ func writeFile(buf []byte) error {
} }
func main() { func main() {
p, err := charts.NewPainter(charts.PainterOptions{ values := []float64{
Width: 800, 1048,
Height: 600, 735,
Type: charts.ChartOutputPNG, 580,
}) 484,
if err != nil { 300,
panic(err)
} }
_, err = charts.NewPieChart(p, charts.PieChartOption{ p, err := charts.PieRender(
Title: charts.TitleOption{ values,
charts.TitleOptionFunc(charts.TitleOption{
Text: "Rainfall vs Evaporation", Text: "Rainfall vs Evaporation",
Subtext: "Fake Data", Subtext: "Fake Data",
Left: charts.PositionCenter, Left: charts.PositionCenter,
}, }),
Padding: charts.Box{ charts.PaddingOptionFunc(charts.Box{
Top: 20, Top: 20,
Right: 20, Right: 20,
Bottom: 20, Bottom: 20,
Left: 20, Left: 20,
}, }),
Legend: charts.LegendOption{ charts.LegendOptionFunc(charts.LegendOption{
Orient: charts.OrientVertical, Orient: charts.OrientVertical,
Data: []string{ Data: []string{
"Search Engine", "Search Engine",
@ -54,20 +54,9 @@ func main() {
"Video Ads", "Video Ads",
}, },
Left: charts.PositionLeft, Left: charts.PositionLeft,
},
SeriesList: charts.NewPieSeriesList([]float64{
1048,
735,
580,
484,
300,
}, charts.PieSeriesOption{
Label: charts.SeriesLabel{
Show: true,
},
Radius: "35%",
}), }),
}).Render() charts.PieSeriesShowLabel(),
)
if err != nil { if err != nil {
panic(err) panic(err)
} }

View file

@ -24,79 +24,47 @@ func writeFile(buf []byte) error {
} }
func main() { func main() {
p, err := charts.NewPainter(charts.PainterOptions{ values := [][]float64{
Width: 800, {
Height: 600, 4200,
Type: charts.ChartOutputPNG, 3000,
}) 20000,
if err != nil { 35000,
panic(err) 50000,
18000,
},
{
5000,
14000,
28000,
26000,
42000,
21000,
},
} }
_, err = charts.NewRadarChart(p, charts.RadarChartOption{ p, err := charts.RadarRender(
Padding: charts.Box{ values,
Left: 10, charts.TitleTextOptionFunc("Basic Radar Chart"),
Top: 10, charts.LegendLabelsOptionFunc([]string{
Right: 10,
Bottom: 10,
},
Title: charts.TitleOption{
Text: "Basic Radar Chart",
},
Legend: charts.NewLegendOption([]string{
"Allocated Budget", "Allocated Budget",
"Actual Spending", "Actual Spending",
}), }),
RadarIndicators: []charts.RadarIndicator{ charts.RadarIndicatorOptionFunc([]string{
{ "Sales",
Name: "Sales", "Administration",
Max: 6500, "Information Technology",
}, "Customer Support",
{ "Development",
Name: "Administration", "Marketing",
Max: 16000, }, []float64{
}, 6500,
{ 16000,
Name: "Information Technology", 30000,
Max: 30000, 38000,
}, 52000,
{ 25000,
Name: "Customer Support", }),
Max: 38000, )
},
{
Name: "Development",
Max: 52000,
},
{
Name: "Marketing",
Max: 25000,
},
},
SeriesList: charts.SeriesList{
{
Type: charts.ChartTypeRadar,
Data: charts.NewSeriesDataFromValues([]float64{
4200,
3000,
20000,
35000,
50000,
18000,
}),
},
{
Type: charts.ChartTypeRadar,
Data: charts.NewSeriesDataFromValues([]float64{
5000,
14000,
28000,
26000,
42000,
21000,
}),
},
},
}).Render()
if err != nil { if err != nil {
panic(err) panic(err)
} }

View file

@ -36,7 +36,7 @@ type funnelChart struct {
func NewFunnelChart(p *Painter, opt FunnelChartOption) *funnelChart { func NewFunnelChart(p *Painter, opt FunnelChartOption) *funnelChart {
if opt.Theme == nil { if opt.Theme == nil {
opt.Theme = NewTheme("") opt.Theme = defaultTheme
} }
return &funnelChart{ return &funnelChart{
p: p, p: p,

View file

@ -52,7 +52,7 @@ type HorizontalBarChartOption struct {
func NewHorizontalBarChart(p *Painter, opt HorizontalBarChartOption) *horizontalBarChart { func NewHorizontalBarChart(p *Painter, opt HorizontalBarChartOption) *horizontalBarChart {
if opt.Theme == nil { if opt.Theme == nil {
opt.Theme = NewTheme("") opt.Theme = defaultTheme
} }
return &horizontalBarChart{ return &horizontalBarChart{
p: p, p: p,

View file

@ -34,7 +34,7 @@ type lineChart struct {
func NewLineChart(p *Painter, opt LineChartOption) *lineChart { func NewLineChart(p *Painter, opt LineChartOption) *lineChart {
if opt.Theme == nil { if opt.Theme == nil {
opt.Theme = NewTheme("") opt.Theme = defaultTheme
} }
return &lineChart{ return &lineChart{
p: p, p: p,

View file

@ -149,9 +149,9 @@ func NewPainter(opts PainterOptions, opt ...PainterOption) (*Painter, error) {
} }
font = f font = f
} }
fn := chart.SVG fn := chart.PNG
if opts.Type == ChartOutputPNG { if opts.Type == ChartOutputSVG {
fn = chart.PNG fn = chart.SVG
} }
width := opts.Width width := opts.Width
height := opts.Height height := opts.Height

View file

@ -53,7 +53,7 @@ type PieChartOption struct {
func NewPieChart(p *Painter, opt PieChartOption) *pieChart { func NewPieChart(p *Painter, opt PieChartOption) *pieChart {
if opt.Theme == nil { if opt.Theme == nil {
opt.Theme = NewTheme("") opt.Theme = defaultTheme
} }
return &pieChart{ return &pieChart{
p: p, p: p,

View file

@ -64,7 +64,7 @@ type RadarChartOption struct {
func NewRadarChart(p *Painter, opt RadarChartOption) *radarChart { func NewRadarChart(p *Painter, opt RadarChartOption) *radarChart {
if opt.Theme == nil { if opt.Theme == nil {
opt.Theme = NewTheme("") opt.Theme = defaultTheme
} }
return &radarChart{ return &radarChart{
p: p, p: p,

View file

@ -36,6 +36,14 @@ type SeriesData struct {
Style Style Style Style
} }
func NewSeriesListDataFromValues(values [][]float64, chartType ...string) SeriesList {
seriesList := make(SeriesList, len(values))
for index, value := range values {
seriesList[index] = NewSeriesFromValues(value, chartType...)
}
return seriesList
}
func NewSeriesFromValues(values []float64, chartType ...string) Series { func NewSeriesFromValues(values []float64, chartType ...string) Series {
s := Series{ s := Series{
Data: NewSeriesDataFromValues(values), Data: NewSeriesDataFromValues(values),

218
theme.go
View file

@ -55,10 +55,21 @@ type themeColorPalette struct {
font *truetype.Font font *truetype.Font
} }
type ThemeOption struct {
IsDarkMode bool
AxisStrokeColor Color
AxisSplitLineColor Color
BackgroundColor Color
TextColor Color
SeriesColors []Color
}
var palettes = map[string]ColorPalette{} var palettes = map[string]ColorPalette{}
const defaultFontSize = 12.0 const defaultFontSize = 12.0
var defaultTheme ColorPalette
func init() { func init() {
echartSeriesColors := []Color{ echartSeriesColors := []Color{
parseColor("#5470c6"), parseColor("#5470c6"),
@ -93,121 +104,134 @@ func init() {
} }
AddTheme( AddTheme(
ThemeDark, ThemeDark,
true, ThemeOption{
Color{ IsDarkMode: true,
R: 185, AxisStrokeColor: Color{
G: 184, R: 185,
B: 206, G: 184,
A: 255, B: 206,
A: 255,
},
AxisSplitLineColor: Color{
R: 72,
G: 71,
B: 83,
A: 255,
},
BackgroundColor: Color{
R: 16,
G: 12,
B: 42,
A: 255,
},
TextColor: Color{
R: 238,
G: 238,
B: 238,
A: 255,
},
SeriesColors: echartSeriesColors,
}, },
Color{
R: 72,
G: 71,
B: 83,
A: 255,
},
Color{
R: 16,
G: 12,
B: 42,
A: 255,
},
Color{
R: 238,
G: 238,
B: 238,
A: 255,
},
echartSeriesColors,
) )
AddTheme( AddTheme(
ThemeLight, ThemeLight,
false, ThemeOption{
Color{ IsDarkMode: false,
R: 110, AxisStrokeColor: Color{
G: 112, R: 110,
B: 121, G: 112,
A: 255, B: 121,
A: 255,
},
AxisSplitLineColor: Color{
R: 224,
G: 230,
B: 242,
A: 255,
},
BackgroundColor: drawing.ColorWhite,
TextColor: Color{
R: 70,
G: 70,
B: 70,
A: 255,
},
SeriesColors: echartSeriesColors,
}, },
Color{
R: 224,
G: 230,
B: 242,
A: 255,
},
drawing.ColorWhite,
drawing.Color{
R: 70,
G: 70,
B: 70,
A: 255,
},
echartSeriesColors,
) )
AddTheme( AddTheme(
ThemeAnt, ThemeAnt,
false, ThemeOption{
Color{ IsDarkMode: false,
R: 110, AxisStrokeColor: Color{
G: 112, R: 110,
B: 121, G: 112,
A: 255, B: 121,
A: 255,
},
AxisSplitLineColor: Color{
R: 224,
G: 230,
B: 242,
A: 255,
},
BackgroundColor: drawing.ColorWhite,
TextColor: drawing.Color{
R: 70,
G: 70,
B: 70,
A: 255,
},
SeriesColors: antSeriesColors,
}, },
Color{
R: 224,
G: 230,
B: 242,
A: 255,
},
drawing.ColorWhite,
drawing.Color{
R: 70,
G: 70,
B: 70,
A: 255,
},
antSeriesColors,
) )
AddTheme( AddTheme(
ThemeGrafana, ThemeGrafana,
true, ThemeOption{
drawing.Color{ IsDarkMode: true,
R: 185, AxisStrokeColor: Color{
G: 184, R: 185,
B: 206, G: 184,
A: 255, B: 206,
A: 255,
},
AxisSplitLineColor: Color{
R: 68,
G: 67,
B: 67,
A: 255,
},
BackgroundColor: drawing.Color{
R: 31,
G: 29,
B: 29,
A: 255,
},
TextColor: Color{
R: 216,
G: 217,
B: 218,
A: 255,
},
SeriesColors: grafanaSeriesColors,
}, },
drawing.Color{
R: 68,
G: 67,
B: 67,
A: 255,
},
drawing.Color{
R: 31,
G: 29,
B: 29,
A: 255,
},
drawing.Color{
R: 216,
G: 217,
B: 218,
A: 255,
},
grafanaSeriesColors,
) )
SetDefaultTheme(ThemeLight)
} }
func AddTheme(name string, isDarkMode bool, axisStrokeColor, axisSplitLineColor, backgroundColor, textColor drawing.Color, seriesColors []drawing.Color) { func SetDefaultTheme(name string) {
defaultTheme = NewTheme(name)
}
func AddTheme(name string, opt ThemeOption) {
palettes[name] = &themeColorPalette{ palettes[name] = &themeColorPalette{
isDarkMode: isDarkMode, isDarkMode: opt.IsDarkMode,
axisStrokeColor: axisStrokeColor, axisStrokeColor: opt.AxisStrokeColor,
axisSplitLineColor: axisSplitLineColor, axisSplitLineColor: opt.AxisSplitLineColor,
backgroundColor: backgroundColor, backgroundColor: opt.BackgroundColor,
textColor: textColor, textColor: opt.TextColor,
seriesColors: seriesColors, seriesColors: opt.SeriesColors,
} }
} }