From eb421892feb86d9b300135591f20736438f98c55 Mon Sep 17 00:00:00 2001 From: vicanso Date: Sat, 15 Jan 2022 10:43:38 +0800 Subject: [PATCH] feat: support label for series, #1 --- bar_series.go | 11 +++++++ base_series.go | 7 ++++ charts.go | 7 +++- echarts.go | 27 +++++++++++++-- examples/demo/main.go | 15 +++++++-- label.go | 77 +++++++++++++++++++++++++++++++++++++++++++ line_series.go | 4 +++ series.go | 7 ++-- 8 files changed, 148 insertions(+), 7 deletions(-) create mode 100644 label.go diff --git a/bar_series.go b/bar_series.go index b730099..b9e8fc1 100644 --- a/bar_series.go +++ b/bar_series.go @@ -23,6 +23,7 @@ package charts import ( + "github.com/dustin/go-humanize" "github.com/wcharczuk/go-chart/v2" ) @@ -110,6 +111,7 @@ func (bs BarSeries) Render(r chart.Renderer, canvasBox chart.Box, xrange, yrange cb := canvasBox.Bottom cl := canvasBox.Left widthValues := bs.getWidthValues(canvasBox.Width()) + labelValues := make([]LabelValue, 0) for i := 0; i < bs.Len(); i++ { vx, vy := bs.GetValues(i) @@ -133,5 +135,14 @@ func (bs BarSeries) Render(r chart.Renderer, canvasBox chart.Box, xrange, yrange Right: x + widthValues.barWidth, Bottom: canvasBox.Bottom - 1, }, cloneStyle) + labelValues = append(labelValues, LabelValue{ + Left: x + widthValues.barWidth/2, + Top: y, + Text: humanize.CommafWithDigits(vy, 2), + }) } + lr := LabelRenderer{ + Options: bs.Label, + } + lr.CustomizeRender(r, style, labelValues) } diff --git a/base_series.go b/base_series.go index e17b4b9..37e3689 100644 --- a/base_series.go +++ b/base_series.go @@ -35,6 +35,11 @@ var ( _ chart.LastValuesProvider = (*BaseSeries)(nil) ) +type SeriesLabel struct { + Show bool + Offset chart.Box +} + // BaseSeries represents a line on a chart. type BaseSeries struct { Name string @@ -48,6 +53,8 @@ type BaseSeries struct { XValues []float64 YValues []float64 + + Label SeriesLabel } // GetName returns the name of the time series. diff --git a/charts.go b/charts.go index 17b5975..5957fdb 100644 --- a/charts.go +++ b/charts.go @@ -28,6 +28,7 @@ import ( "io" "sync" + "github.com/dustin/go-humanize" "github.com/golang/freetype/truetype" "github.com/wcharczuk/go-chart/v2" "github.com/wcharczuk/go-chart/v2/drawing" @@ -174,9 +175,13 @@ func newTitleRenderable(title Title, font *truetype.Font, textColor drawing.Colo func newPieChart(opt Options) *chart.PieChart { values := make(chart.Values, len(opt.Series)) for index, item := range opt.Series { + label := item.Name + if item.Label.Show { + label += ":" + humanize.CommafWithDigits(item.Data[0].Value, 2) + } values[index] = chart.Value{ Value: item.Data[0].Value, - Label: item.Name, + Label: label, } } p := &chart.PieChart{ diff --git a/echarts.go b/echarts.go index bf88206..7e1884c 100644 --- a/echarts.go +++ b/echarts.go @@ -176,6 +176,24 @@ func (ex *EChartsXAxis) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, &ex.Data) } +type EChartsLabelOption struct { + Show bool `json:"show"` + Distance int `json:"distance"` +} + +func (elo EChartsLabelOption) ToLabel() SeriesLabel { + if !elo.Show { + return SeriesLabel{} + } + return SeriesLabel{ + Show: true, + Offset: chart.Box{ + // 默认位置为top,因此设置为负 + Top: -elo.Distance, + }, + } +} + type ECharsOptions struct { Theme string `json:"theme"` Padding EChartsPadding `json:"padding"` @@ -206,6 +224,8 @@ type ECharsOptions struct { Type string `json:"type"` YAxisIndex int `json:"yAxisIndex"` ItemStyle EChartStyle `json:"itemStyle"` + // label的配置 + Label EChartsLabelOption `json:"label"` } `json:"series"` } @@ -218,6 +238,7 @@ func convertEChartsSeries(e *ECharsOptions) ([]Series, chart.TickPosition) { seriesType := e.Series[0].Type if seriesType == SeriesPie { series := make([]Series, len(e.Series[0].Data)) + label := e.Series[0].Label.ToLabel() for index, item := range e.Series[0].Data { style := chart.Style{} if item.ItemStyle.Color != "" { @@ -233,8 +254,9 @@ func convertEChartsSeries(e *ECharsOptions) ([]Series, chart.TickPosition) { Value: item.Value, }, }, - Type: seriesType, - Name: item.Name, + Type: seriesType, + Name: item.Name, + Label: label, } } return series, tickPosition @@ -268,6 +290,7 @@ func convertEChartsSeries(e *ECharsOptions) ([]Series, chart.TickPosition) { YAxisIndex: item.YAxisIndex, Data: data, Type: item.Type, + Label: item.Label.ToLabel(), } } return series, tickPosition diff --git a/examples/demo/main.go b/examples/demo/main.go index 0eedc68..6fb652b 100644 --- a/examples/demo/main.go +++ b/examples/demo/main.go @@ -71,7 +71,11 @@ var chartOptions = []map[string]string{ "series": [ { "data": [150, 230, 224, 218, 135, 147, 260], - "type": "line" + "type": "line", + "label": { + "show": true, + "distance": 5 + } } ] }`, @@ -131,7 +135,11 @@ var chartOptions = []map[string]string{ 110, 130 ], - "type": "bar" + "type": "bar", + "label": { + "show": true, + "distance": 10 + } } ] }`, @@ -267,6 +275,9 @@ var chartOptions = []map[string]string{ "name": "Access From", "type": "pie", "radius": "50%", + "label": { + "show": true + }, "data": [ { "value": 1048, diff --git a/label.go b/label.go new file mode 100644 index 0000000..6d77eb2 --- /dev/null +++ b/label.go @@ -0,0 +1,77 @@ +// MIT License + +// Copyright (c) 2022 Tree Xie + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package charts + +import ( + "github.com/dustin/go-humanize" + "github.com/wcharczuk/go-chart/v2" +) + +type LabelRenderer struct { + Options SeriesLabel + Offset chart.Box +} +type LabelValue struct { + Left int + Top int + Text string +} + +func (l LabelRenderer) Render(r chart.Renderer, canvasBox chart.Box, xrange, yrange chart.Range, style chart.Style, vs chart.ValuesProvider) { + if !l.Options.Show { + return + } + r.SetFontColor(style.FontColor) + r.SetFontSize(style.FontSize) + r.SetFont(style.Font) + offsetX := l.Options.Offset.Left + l.Offset.Left + offsetY := l.Options.Offset.Top + l.Offset.Top + for i := 0; i < vs.Len(); i++ { + vx, vy := vs.GetValues(i) + x := canvasBox.Left + xrange.Translate(vx) + offsetX + y := canvasBox.Bottom - yrange.Translate(vy) + offsetY + + text := humanize.CommafWithDigits(vy, 2) + // 往左移一半距离 + x -= r.MeasureText(text).Width() >> 1 + r.Text(text, x, y) + } +} + +func (l LabelRenderer) CustomizeRender(r chart.Renderer, style chart.Style, values []LabelValue) { + if !l.Options.Show { + return + } + r.SetFont(style.Font) + r.SetFontColor(style.FontColor) + r.SetFontSize(style.FontSize) + offsetX := l.Options.Offset.Left + l.Offset.Left + offsetY := l.Options.Offset.Top + l.Offset.Top + for _, value := range values { + x := value.Left + offsetX + y := value.Top + offsetY + text := value.Text + x -= r.MeasureText(text).Width() >> 1 + r.Text(text, x, y) + } +} diff --git a/line_series.go b/line_series.go index f3686ad..93a1479 100644 --- a/line_series.go +++ b/line_series.go @@ -42,4 +42,8 @@ func (ls LineSeries) Render(r chart.Renderer, canvasBox chart.Box, xrange, yrang style := ls.Style.InheritFrom(defaults) xrange = ls.getXRange(xrange) chart.Draw.LineSeries(r, canvasBox, xrange, yrange, style, ls) + lr := LabelRenderer{ + Options: ls.Label, + } + lr.Render(r, canvasBox, xrange, yrange, style, ls) } diff --git a/series.go b/series.go index 36e57bd..f645749 100644 --- a/series.go +++ b/series.go @@ -38,6 +38,7 @@ type Series struct { XValues []float64 YAxisIndex int Style chart.Style + Label SeriesLabel } const lineStrokeWidth = 2 @@ -73,8 +74,9 @@ func GetSeries(series []Series, tickPosition chart.TickPosition, theme string) [ StrokeWidth: lineStrokeWidth, StrokeColor: getSeriesColor(theme, index), // TODO 调整为通过dot with color 生成 - DotColor: getSeriesColor(theme, index), - DotWidth: dotWith, + DotColor: getSeriesColor(theme, index), + DotWidth: dotWith, + FontColor: getAxisColor(theme), } if !item.Style.StrokeColor.IsZero() { style.StrokeColor = item.Style.StrokeColor @@ -109,6 +111,7 @@ func GetSeries(series []Series, tickPosition chart.TickPosition, theme string) [ YValues: yValues, TickPosition: tickPosition, YAxis: chart.YAxisSecondary, + Label: item.Label, } if item.YAxisIndex != 0 { baseSeries.YAxis = chart.YAxisPrimary