feat: support label for series, #1
This commit is contained in:
parent
a577d30eb8
commit
eb421892fe
8 changed files with 148 additions and 7 deletions
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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{
|
||||
|
|
|
|||
27
echarts.go
27
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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
77
label.go
Normal file
77
label.go
Normal 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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue