feat: support label for series, #1

This commit is contained in:
vicanso 2022-01-15 10:43:38 +08:00
parent a577d30eb8
commit eb421892fe
8 changed files with 148 additions and 7 deletions

View file

@ -23,6 +23,7 @@
package charts package charts
import ( import (
"github.com/dustin/go-humanize"
"github.com/wcharczuk/go-chart/v2" "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 cb := canvasBox.Bottom
cl := canvasBox.Left cl := canvasBox.Left
widthValues := bs.getWidthValues(canvasBox.Width()) widthValues := bs.getWidthValues(canvasBox.Width())
labelValues := make([]LabelValue, 0)
for i := 0; i < bs.Len(); i++ { for i := 0; i < bs.Len(); i++ {
vx, vy := bs.GetValues(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, Right: x + widthValues.barWidth,
Bottom: canvasBox.Bottom - 1, Bottom: canvasBox.Bottom - 1,
}, cloneStyle) }, 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)
} }

View file

@ -35,6 +35,11 @@ var (
_ chart.LastValuesProvider = (*BaseSeries)(nil) _ chart.LastValuesProvider = (*BaseSeries)(nil)
) )
type SeriesLabel struct {
Show bool
Offset chart.Box
}
// BaseSeries represents a line on a chart. // BaseSeries represents a line on a chart.
type BaseSeries struct { type BaseSeries struct {
Name string Name string
@ -48,6 +53,8 @@ type BaseSeries struct {
XValues []float64 XValues []float64
YValues []float64 YValues []float64
Label SeriesLabel
} }
// GetName returns the name of the time series. // GetName returns the name of the time series.

View file

@ -28,6 +28,7 @@ import (
"io" "io"
"sync" "sync"
"github.com/dustin/go-humanize"
"github.com/golang/freetype/truetype" "github.com/golang/freetype/truetype"
"github.com/wcharczuk/go-chart/v2" "github.com/wcharczuk/go-chart/v2"
"github.com/wcharczuk/go-chart/v2/drawing" "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 { func newPieChart(opt Options) *chart.PieChart {
values := make(chart.Values, len(opt.Series)) values := make(chart.Values, len(opt.Series))
for index, item := range 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{ values[index] = chart.Value{
Value: item.Data[0].Value, Value: item.Data[0].Value,
Label: item.Name, Label: label,
} }
} }
p := &chart.PieChart{ p := &chart.PieChart{

View file

@ -176,6 +176,24 @@ func (ex *EChartsXAxis) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &ex.Data) 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 { type ECharsOptions struct {
Theme string `json:"theme"` Theme string `json:"theme"`
Padding EChartsPadding `json:"padding"` Padding EChartsPadding `json:"padding"`
@ -206,6 +224,8 @@ type ECharsOptions struct {
Type string `json:"type"` Type string `json:"type"`
YAxisIndex int `json:"yAxisIndex"` YAxisIndex int `json:"yAxisIndex"`
ItemStyle EChartStyle `json:"itemStyle"` ItemStyle EChartStyle `json:"itemStyle"`
// label的配置
Label EChartsLabelOption `json:"label"`
} `json:"series"` } `json:"series"`
} }
@ -218,6 +238,7 @@ func convertEChartsSeries(e *ECharsOptions) ([]Series, chart.TickPosition) {
seriesType := e.Series[0].Type seriesType := e.Series[0].Type
if seriesType == SeriesPie { if seriesType == SeriesPie {
series := make([]Series, len(e.Series[0].Data)) series := make([]Series, len(e.Series[0].Data))
label := e.Series[0].Label.ToLabel()
for index, item := range e.Series[0].Data { for index, item := range e.Series[0].Data {
style := chart.Style{} style := chart.Style{}
if item.ItemStyle.Color != "" { if item.ItemStyle.Color != "" {
@ -235,6 +256,7 @@ func convertEChartsSeries(e *ECharsOptions) ([]Series, chart.TickPosition) {
}, },
Type: seriesType, Type: seriesType,
Name: item.Name, Name: item.Name,
Label: label,
} }
} }
return series, tickPosition return series, tickPosition
@ -268,6 +290,7 @@ func convertEChartsSeries(e *ECharsOptions) ([]Series, chart.TickPosition) {
YAxisIndex: item.YAxisIndex, YAxisIndex: item.YAxisIndex,
Data: data, Data: data,
Type: item.Type, Type: item.Type,
Label: item.Label.ToLabel(),
} }
} }
return series, tickPosition return series, tickPosition

View file

@ -71,7 +71,11 @@ var chartOptions = []map[string]string{
"series": [ "series": [
{ {
"data": [150, 230, 224, 218, 135, 147, 260], "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, 110,
130 130
], ],
"type": "bar" "type": "bar",
"label": {
"show": true,
"distance": 10
}
} }
] ]
}`, }`,
@ -267,6 +275,9 @@ var chartOptions = []map[string]string{
"name": "Access From", "name": "Access From",
"type": "pie", "type": "pie",
"radius": "50%", "radius": "50%",
"label": {
"show": true
},
"data": [ "data": [
{ {
"value": 1048, "value": 1048,

77
label.go Normal file
View file

@ -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)
}
}

View file

@ -42,4 +42,8 @@ func (ls LineSeries) Render(r chart.Renderer, canvasBox chart.Box, xrange, yrang
style := ls.Style.InheritFrom(defaults) style := ls.Style.InheritFrom(defaults)
xrange = ls.getXRange(xrange) xrange = ls.getXRange(xrange)
chart.Draw.LineSeries(r, canvasBox, xrange, yrange, style, ls) chart.Draw.LineSeries(r, canvasBox, xrange, yrange, style, ls)
lr := LabelRenderer{
Options: ls.Label,
}
lr.Render(r, canvasBox, xrange, yrange, style, ls)
} }

View file

@ -38,6 +38,7 @@ type Series struct {
XValues []float64 XValues []float64
YAxisIndex int YAxisIndex int
Style chart.Style Style chart.Style
Label SeriesLabel
} }
const lineStrokeWidth = 2 const lineStrokeWidth = 2
@ -75,6 +76,7 @@ func GetSeries(series []Series, tickPosition chart.TickPosition, theme string) [
// TODO 调整为通过dot with color 生成 // TODO 调整为通过dot with color 生成
DotColor: getSeriesColor(theme, index), DotColor: getSeriesColor(theme, index),
DotWidth: dotWith, DotWidth: dotWith,
FontColor: getAxisColor(theme),
} }
if !item.Style.StrokeColor.IsZero() { if !item.Style.StrokeColor.IsZero() {
style.StrokeColor = item.Style.StrokeColor style.StrokeColor = item.Style.StrokeColor
@ -109,6 +111,7 @@ func GetSeries(series []Series, tickPosition chart.TickPosition, theme string) [
YValues: yValues, YValues: yValues,
TickPosition: tickPosition, TickPosition: tickPosition,
YAxis: chart.YAxisSecondary, YAxis: chart.YAxisSecondary,
Label: item.Label,
} }
if item.YAxisIndex != 0 { if item.YAxisIndex != 0 {
baseSeries.YAxis = chart.YAxisPrimary baseSeries.YAxis = chart.YAxisPrimary