refactor: support more echarts options
This commit is contained in:
parent
23e2eca0c6
commit
c0170bf250
5 changed files with 176 additions and 43 deletions
39
axis.go
39
axis.go
|
|
@ -43,6 +43,8 @@ type (
|
||||||
type YAxisOption struct {
|
type YAxisOption struct {
|
||||||
Formater chart.ValueFormatter
|
Formater chart.ValueFormatter
|
||||||
Disabled bool
|
Disabled bool
|
||||||
|
Min *float64
|
||||||
|
Max *float64
|
||||||
}
|
}
|
||||||
|
|
||||||
const axisStrokeWidth = 1
|
const axisStrokeWidth = 1
|
||||||
|
|
@ -113,14 +115,9 @@ func defaultFloatFormater(v interface{}) string {
|
||||||
|
|
||||||
func GetSecondaryYAxis(theme string, option *YAxisOption) chart.YAxis {
|
func GetSecondaryYAxis(theme string, option *YAxisOption) chart.YAxis {
|
||||||
strokeColor := getGridColor(theme)
|
strokeColor := getGridColor(theme)
|
||||||
formater := defaultFloatFormater
|
yAxis := chart.YAxis{
|
||||||
if option != nil {
|
Range: &chart.ContinuousRange{},
|
||||||
if option.Formater != nil {
|
ValueFormatter: defaultFloatFormater,
|
||||||
formater = option.Formater
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return chart.YAxis{
|
|
||||||
ValueFormatter: formater,
|
|
||||||
AxisType: chart.YAxisSecondary,
|
AxisType: chart.YAxisSecondary,
|
||||||
GridMajorStyle: chart.Style{
|
GridMajorStyle: chart.Style{
|
||||||
StrokeColor: strokeColor,
|
StrokeColor: strokeColor,
|
||||||
|
|
@ -137,22 +134,35 @@ func GetSecondaryYAxis(theme string, option *YAxisOption) chart.YAxis {
|
||||||
StrokeWidth: axisStrokeWidth,
|
StrokeWidth: axisStrokeWidth,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
setYAxis(&yAxis, option)
|
||||||
|
return yAxis
|
||||||
|
}
|
||||||
|
|
||||||
|
func setYAxis(yAxis *chart.YAxis, option *YAxisOption) {
|
||||||
|
if option == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if option.Formater != nil {
|
||||||
|
yAxis.ValueFormatter = option.Formater
|
||||||
|
}
|
||||||
|
if option.Max != nil {
|
||||||
|
yAxis.Range.SetMax(*option.Max)
|
||||||
|
}
|
||||||
|
if option.Min != nil {
|
||||||
|
yAxis.Range.SetMin(*option.Min)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetYAxis(theme string, option *YAxisOption) chart.YAxis {
|
func GetYAxis(theme string, option *YAxisOption) chart.YAxis {
|
||||||
// strokeColor := getGridColor(theme)
|
|
||||||
formater := defaultFloatFormater
|
|
||||||
disabled := false
|
disabled := false
|
||||||
if option != nil {
|
if option != nil {
|
||||||
if option.Formater != nil {
|
|
||||||
formater = option.Formater
|
|
||||||
}
|
|
||||||
disabled = option.Disabled
|
disabled = option.Disabled
|
||||||
}
|
}
|
||||||
hidden := chart.Hidden()
|
hidden := chart.Hidden()
|
||||||
|
|
||||||
yAxis := chart.YAxis{
|
yAxis := chart.YAxis{
|
||||||
ValueFormatter: formater,
|
Range: &chart.ContinuousRange{},
|
||||||
|
ValueFormatter: defaultFloatFormater,
|
||||||
AxisType: chart.YAxisPrimary,
|
AxisType: chart.YAxisPrimary,
|
||||||
GridMajorStyle: hidden,
|
GridMajorStyle: hidden,
|
||||||
GridMinorStyle: hidden,
|
GridMinorStyle: hidden,
|
||||||
|
|
@ -167,5 +177,6 @@ func GetYAxis(theme string, option *YAxisOption) chart.YAxis {
|
||||||
yAxis.Range = &HiddenRange{}
|
yAxis.Range = &HiddenRange{}
|
||||||
yAxis.Style.Hidden = true
|
yAxis.Style.Hidden = true
|
||||||
}
|
}
|
||||||
|
setYAxis(&yAxis, option)
|
||||||
return yAxis
|
return yAxis
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,9 @@ type (
|
||||||
Style chart.Style
|
Style chart.Style
|
||||||
}
|
}
|
||||||
Legend struct {
|
Legend struct {
|
||||||
Data []string
|
Data []string
|
||||||
|
Align string
|
||||||
|
Padding chart.Box
|
||||||
}
|
}
|
||||||
Options struct {
|
Options struct {
|
||||||
Width int
|
Width int
|
||||||
|
|
@ -177,6 +179,8 @@ func New(opt Options) (Graph, error) {
|
||||||
Theme: opt.Theme,
|
Theme: opt.Theme,
|
||||||
TextPosition: LegendTextPositionRight,
|
TextPosition: LegendTextPositionRight,
|
||||||
IconDraw: DefaultLegendIconDraw,
|
IconDraw: DefaultLegendIconDraw,
|
||||||
|
Align: opt.Legend.Align,
|
||||||
|
Padding: opt.Legend.Padding,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
144
echarts.go
144
echarts.go
|
|
@ -42,6 +42,17 @@ type ECharsSeriesData struct {
|
||||||
}
|
}
|
||||||
type _ECharsSeriesData ECharsSeriesData
|
type _ECharsSeriesData ECharsSeriesData
|
||||||
|
|
||||||
|
func convertToArray(data []byte) []byte {
|
||||||
|
data = bytes.TrimSpace(data)
|
||||||
|
if len(data) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if data[0] != '[' {
|
||||||
|
data = []byte("[" + string(data) + "]")
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
func (es *ECharsSeriesData) UnmarshalJSON(data []byte) error {
|
func (es *ECharsSeriesData) UnmarshalJSON(data []byte) error {
|
||||||
data = bytes.TrimSpace(data)
|
data = bytes.TrimSpace(data)
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
|
|
@ -66,11 +77,57 @@ func (es *ECharsSeriesData) UnmarshalJSON(data []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type EChartsPadding struct {
|
||||||
|
box chart.Box
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ep *EChartsPadding) UnmarshalJSON(data []byte) error {
|
||||||
|
data = convertToArray(data)
|
||||||
|
if len(data) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
arr := make([]int, 0)
|
||||||
|
err := json.Unmarshal(data, &arr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(arr) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
switch len(arr) {
|
||||||
|
case 1:
|
||||||
|
ep.box = chart.Box{
|
||||||
|
Left: arr[0],
|
||||||
|
Top: arr[0],
|
||||||
|
Bottom: arr[0],
|
||||||
|
Right: arr[0],
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
ep.box = chart.Box{
|
||||||
|
Top: arr[0],
|
||||||
|
Bottom: arr[0],
|
||||||
|
Left: arr[1],
|
||||||
|
Right: arr[1],
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
result := make([]int, 4)
|
||||||
|
copy(result, arr)
|
||||||
|
// 上右下左
|
||||||
|
ep.box = chart.Box{
|
||||||
|
Top: arr[0],
|
||||||
|
Right: arr[1],
|
||||||
|
Bottom: arr[2],
|
||||||
|
Left: arr[3],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type EChartsYAxis struct {
|
type EChartsYAxis struct {
|
||||||
Data []struct {
|
Data []struct {
|
||||||
Min int `json:"min"`
|
Min *float64 `json:"min"`
|
||||||
Max int `json:"max"`
|
Max *float64 `json:"max"`
|
||||||
Interval int `json:"interval"`
|
Interval int `json:"interval"`
|
||||||
AxisLabel struct {
|
AxisLabel struct {
|
||||||
Formatter string `json:"formatter"`
|
Formatter string `json:"formatter"`
|
||||||
} `json:"axisLabel"`
|
} `json:"axisLabel"`
|
||||||
|
|
@ -78,34 +135,50 @@ type EChartsYAxis struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ey *EChartsYAxis) UnmarshalJSON(data []byte) error {
|
func (ey *EChartsYAxis) UnmarshalJSON(data []byte) error {
|
||||||
data = bytes.TrimSpace(data)
|
data = convertToArray(data)
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if data[0] != '[' {
|
|
||||||
data = []byte("[" + string(data) + "]")
|
|
||||||
}
|
|
||||||
return json.Unmarshal(data, &ey.Data)
|
return json.Unmarshal(data, &ey.Data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type EChartsXAxis struct {
|
||||||
|
Data []struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
BoundaryGap *bool `json:"boundaryGap"`
|
||||||
|
SplitNumber int `json:"splitNumber"`
|
||||||
|
Data []string `json:"data"`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ex *EChartsXAxis) UnmarshalJSON(data []byte) error {
|
||||||
|
data = convertToArray(data)
|
||||||
|
if len(data) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return json.Unmarshal(data, &ex.Data)
|
||||||
|
}
|
||||||
|
|
||||||
type ECharsOptions struct {
|
type ECharsOptions struct {
|
||||||
Theme string `json:"theme"`
|
Theme string `json:"theme"`
|
||||||
Title struct {
|
Title struct {
|
||||||
Text string `json:"text"`
|
Text string `json:"text"`
|
||||||
|
// 暂不支持(go-chart默认title只能居中)
|
||||||
|
TextAlign string `json:"textAlign"`
|
||||||
TextStyle struct {
|
TextStyle struct {
|
||||||
Color string `json:"color"`
|
Color string `json:"color"`
|
||||||
FontFamily string `json:"fontFamily"`
|
// TODO 字体支持
|
||||||
|
FontFamily string `json:"fontFamily"`
|
||||||
|
FontSize float64 `json:"fontSize"`
|
||||||
|
Height float64 `json:"height"`
|
||||||
} `json:"textStyle"`
|
} `json:"textStyle"`
|
||||||
} `json:"title"`
|
} `json:"title"`
|
||||||
XAxis struct {
|
XAxis EChartsXAxis `json:"xAxis"`
|
||||||
Type string `json:"type"`
|
|
||||||
BoundaryGap *bool `json:"boundaryGap"`
|
|
||||||
SplitNumber int `json:"splitNumber"`
|
|
||||||
Data []string `json:"data"`
|
|
||||||
} `json:"xAxis"`
|
|
||||||
YAxis EChartsYAxis `json:"yAxis"`
|
YAxis EChartsYAxis `json:"yAxis"`
|
||||||
Legend struct {
|
Legend struct {
|
||||||
Data []string `json:"data"`
|
Data []string `json:"data"`
|
||||||
|
Align string `json:"align"`
|
||||||
|
Padding EChartsPadding `json:"padding"`
|
||||||
} `json:"legend"`
|
} `json:"legend"`
|
||||||
Series []struct {
|
Series []struct {
|
||||||
Data []ECharsSeriesData `json:"data"`
|
Data []ECharsSeriesData `json:"data"`
|
||||||
|
|
@ -167,23 +240,46 @@ func (e *ECharsOptions) ToOptions() Options {
|
||||||
o := Options{
|
o := Options{
|
||||||
Theme: e.Theme,
|
Theme: e.Theme,
|
||||||
}
|
}
|
||||||
|
titleTextStyle := e.Title.TextStyle
|
||||||
o.Title = Title{
|
o.Title = Title{
|
||||||
Text: e.Title.Text,
|
Text: e.Title.Text,
|
||||||
|
Style: chart.Style{
|
||||||
|
FontColor: parseColor(titleTextStyle.Color),
|
||||||
|
FontSize: titleTextStyle.FontSize,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
o.XAxis = XAxis{
|
if titleTextStyle.FontSize != 0 && titleTextStyle.Height > titleTextStyle.FontSize {
|
||||||
Type: e.XAxis.Type,
|
padding := int(titleTextStyle.Height-titleTextStyle.FontSize) / 2
|
||||||
Data: e.XAxis.Data,
|
o.Title.Style.Padding.Top = padding
|
||||||
SplitNumber: e.XAxis.SplitNumber,
|
o.Title.Style.Padding.Bottom = padding
|
||||||
|
}
|
||||||
|
|
||||||
|
boundaryGap := false
|
||||||
|
if len(e.XAxis.Data) != 0 {
|
||||||
|
xAxis := e.XAxis.Data[0]
|
||||||
|
o.XAxis = XAxis{
|
||||||
|
Type: xAxis.Type,
|
||||||
|
Data: xAxis.Data,
|
||||||
|
SplitNumber: xAxis.SplitNumber,
|
||||||
|
}
|
||||||
|
if xAxis.BoundaryGap == nil || *xAxis.BoundaryGap {
|
||||||
|
boundaryGap = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
o.Legend = Legend{
|
o.Legend = Legend{
|
||||||
Data: e.Legend.Data,
|
Data: e.Legend.Data,
|
||||||
|
Align: e.Legend.Align,
|
||||||
|
Padding: e.Legend.Padding.box,
|
||||||
}
|
}
|
||||||
if len(e.YAxis.Data) != 0 {
|
if len(e.YAxis.Data) != 0 {
|
||||||
yAxisOptions := make([]*YAxisOption, len(e.YAxis.Data))
|
yAxisOptions := make([]*YAxisOption, len(e.YAxis.Data))
|
||||||
for index, item := range e.YAxis.Data {
|
for index, item := range e.YAxis.Data {
|
||||||
opt := &YAxisOption{}
|
opt := &YAxisOption{
|
||||||
|
Max: item.Max,
|
||||||
|
Min: item.Min,
|
||||||
|
}
|
||||||
template := item.AxisLabel.Formatter
|
template := item.AxisLabel.Formatter
|
||||||
if template != "" {
|
if template != "" {
|
||||||
opt.Formater = func(v interface{}) string {
|
opt.Formater = func(v interface{}) string {
|
||||||
|
|
@ -200,7 +296,7 @@ func (e *ECharsOptions) ToOptions() Options {
|
||||||
|
|
||||||
o.Series = series
|
o.Series = series
|
||||||
|
|
||||||
if e.XAxis.BoundaryGap == nil || *e.XAxis.BoundaryGap {
|
if boundaryGap {
|
||||||
tickPosition = chart.TickPositionBetweenTicks
|
tickPosition = chart.TickPositionBetweenTicks
|
||||||
}
|
}
|
||||||
o.TickPosition = tickPosition
|
o.TickPosition = tickPosition
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,19 @@ var chartOptions = []map[string]string{
|
||||||
{
|
{
|
||||||
"title": "折线图",
|
"title": "折线图",
|
||||||
"option": `{
|
"option": `{
|
||||||
|
"title": {
|
||||||
|
"text": "line",
|
||||||
|
"textAlign": "left",
|
||||||
|
"textStyle": {
|
||||||
|
"color": "#333",
|
||||||
|
"fontSize": 24,
|
||||||
|
"height": 40
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"yAxis": {
|
||||||
|
"min": 0,
|
||||||
|
"max": 300
|
||||||
|
},
|
||||||
"xAxis": {
|
"xAxis": {
|
||||||
"type": "category",
|
"type": "category",
|
||||||
"data": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
|
"data": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
|
||||||
|
|
@ -61,6 +74,8 @@ var chartOptions = []map[string]string{
|
||||||
"text": "Multi Line"
|
"text": "Multi Line"
|
||||||
},
|
},
|
||||||
"legend": {
|
"legend": {
|
||||||
|
"align": "left",
|
||||||
|
"padding": [5, 0, 0, 50],
|
||||||
"data": ["Email", "Union Ads", "Video Ads", "Direct", "Search Engine"]
|
"data": ["Email", "Union Ads", "Video Ads", "Direct", "Search Engine"]
|
||||||
},
|
},
|
||||||
"xAxis": {
|
"xAxis": {
|
||||||
|
|
@ -288,6 +303,9 @@ var chartOptions = []map[string]string{
|
||||||
func render(theme string) ([]byte, error) {
|
func render(theme string) ([]byte, error) {
|
||||||
data := bytes.Buffer{}
|
data := bytes.Buffer{}
|
||||||
for _, m := range chartOptions {
|
for _, m := range chartOptions {
|
||||||
|
// if m["title"] != "多柱状图" {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
chartHTML := []byte(`<div>
|
chartHTML := []byte(`<div>
|
||||||
<h1>{{title}}</h1>
|
<h1>{{title}}</h1>
|
||||||
<pre>{{option}}</pre>
|
<pre>{{option}}</pre>
|
||||||
|
|
|
||||||
12
legend.go
12
legend.go
|
|
@ -28,6 +28,7 @@ import (
|
||||||
|
|
||||||
type LegendOption struct {
|
type LegendOption struct {
|
||||||
Style chart.Style
|
Style chart.Style
|
||||||
|
Padding chart.Box
|
||||||
Align string
|
Align string
|
||||||
TextPosition string
|
TextPosition string
|
||||||
Theme string
|
Theme string
|
||||||
|
|
@ -61,7 +62,7 @@ func DefaultLegendIconDraw(r chart.Renderer, opt LegendIconDrawOption) {
|
||||||
stokeWidth := opt.Style.GetStrokeWidth()
|
stokeWidth := opt.Style.GetStrokeWidth()
|
||||||
r.SetStrokeWidth(stokeWidth)
|
r.SetStrokeWidth(stokeWidth)
|
||||||
height := opt.Box.Bottom - opt.Box.Top
|
height := opt.Box.Bottom - opt.Box.Top
|
||||||
ly := (height / 2) + 1
|
ly := opt.Box.Top - (height / 2) + 2
|
||||||
r.MoveTo(opt.Box.Left, ly)
|
r.MoveTo(opt.Box.Left, ly)
|
||||||
r.LineTo(opt.Box.Right, ly)
|
r.LineTo(opt.Box.Right, ly)
|
||||||
r.Stroke()
|
r.Stroke()
|
||||||
|
|
@ -126,11 +127,14 @@ func LegendCustomize(c *chart.Chart, opt LegendOption) chart.Renderable {
|
||||||
left = (cb.Width() - labelWidth) / 2
|
left = (cb.Width() - labelWidth) / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
left += opt.Padding.Left
|
||||||
|
top := opt.Padding.Top
|
||||||
|
|
||||||
legendBox := chart.Box{
|
legendBox := chart.Box{
|
||||||
Left: left,
|
Left: left,
|
||||||
Right: left + labelWidth,
|
Right: left + labelWidth,
|
||||||
Top: 0,
|
Top: top,
|
||||||
Bottom: 0 + legendBoxHeight,
|
Bottom: top + legendBoxHeight,
|
||||||
}
|
}
|
||||||
|
|
||||||
chart.Draw.Box(r, legendBox, legendDefaults)
|
chart.Draw.Box(r, legendBox, legendDefaults)
|
||||||
|
|
@ -142,7 +146,7 @@ func LegendCustomize(c *chart.Chart, opt LegendOption) chart.Renderable {
|
||||||
lineTextGap := 5
|
lineTextGap := 5
|
||||||
|
|
||||||
startX := legendBox.Left + legendStyle.Padding.Left
|
startX := legendBox.Left + legendStyle.Padding.Left
|
||||||
ty := legendYMargin + legendStyle.Padding.Top + textHeight
|
ty := top + legendYMargin + legendStyle.Padding.Top + textHeight
|
||||||
var label string
|
var label string
|
||||||
var x int
|
var x int
|
||||||
iconDraw := opt.IconDraw
|
iconDraw := opt.IconDraw
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue