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
|
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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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{
|
||||||
|
|
|
||||||
23
echarts.go
23
echarts.go
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
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)
|
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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue