refactor: support customize legend function
This commit is contained in:
parent
deb77f5761
commit
40b7eb3ee2
3 changed files with 207 additions and 16 deletions
9
axis.go
9
axis.go
|
|
@ -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"
|
||||||
"github.com/wcharczuk/go-chart/v2/drawing"
|
"github.com/wcharczuk/go-chart/v2/drawing"
|
||||||
)
|
)
|
||||||
|
|
@ -80,7 +81,13 @@ func GetYAxis(theme string) chart.YAxis {
|
||||||
A: 255,
|
A: 255,
|
||||||
}
|
}
|
||||||
return chart.YAxis{
|
return chart.YAxis{
|
||||||
|
ValueFormatter: func(v interface{}) string {
|
||||||
|
value, ok := v.(float64)
|
||||||
|
if !ok {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return humanize.Commaf(value)
|
||||||
|
},
|
||||||
AxisType: chart.YAxisSecondary,
|
AxisType: chart.YAxisSecondary,
|
||||||
GridMajorStyle: chart.Style{
|
GridMajorStyle: chart.Style{
|
||||||
StrokeColor: strokeColor,
|
StrokeColor: strokeColor,
|
||||||
|
|
|
||||||
18
charts.go
18
charts.go
|
|
@ -68,7 +68,6 @@ func ToPNG(c *chart.Chart) ([]byte, error) {
|
||||||
func ToSVG(c *chart.Chart) ([]byte, error) {
|
func ToSVG(c *chart.Chart) ([]byte, error) {
|
||||||
return render(c, chart.SVG)
|
return render(c, chart.SVG)
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(opt Option) *chart.Chart {
|
func New(opt Option) *chart.Chart {
|
||||||
xAxis, xValues := GetXAxisAndValues(opt.XAxis, opt.Theme)
|
xAxis, xValues := GetXAxisAndValues(opt.XAxis, opt.Theme)
|
||||||
|
|
||||||
|
|
@ -90,22 +89,11 @@ func New(opt Option) *chart.Chart {
|
||||||
YAxis: GetYAxis(opt.Theme),
|
YAxis: GetYAxis(opt.Theme),
|
||||||
Series: GetSeries(opt.Series, opt.Theme),
|
Series: GetSeries(opt.Series, opt.Theme),
|
||||||
}
|
}
|
||||||
|
// 设置secondary的样式
|
||||||
|
c.YAxisSecondary.Style = c.YAxis.Style
|
||||||
if legendSize != 0 {
|
if legendSize != 0 {
|
||||||
c.Elements = []chart.Renderable{
|
c.Elements = []chart.Renderable{
|
||||||
chart.LegendThin(c, chart.Style{
|
DefaultLegend(c),
|
||||||
FillColor: hiddenColor,
|
|
||||||
// FillColor: drawing.ColorWhite,
|
|
||||||
// FontColor: DefaultTextColor,
|
|
||||||
FontSize: 8.0,
|
|
||||||
StrokeColor: hiddenColor,
|
|
||||||
// StrokeWidth: DefaultAxisLineWidth,
|
|
||||||
// Padding: Box{
|
|
||||||
// Top: 2,
|
|
||||||
// Left: 7,
|
|
||||||
// Right: 7,
|
|
||||||
// Bottom: 5,
|
|
||||||
// },
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return c
|
return c
|
||||||
|
|
|
||||||
196
legend.go
Normal file
196
legend.go
Normal file
|
|
@ -0,0 +1,196 @@
|
||||||
|
// MIT License
|
||||||
|
|
||||||
|
// Copyright (c) 2021 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/wcharczuk/go-chart/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LegendOption struct {
|
||||||
|
Style chart.Style
|
||||||
|
Align string
|
||||||
|
TextPosition string
|
||||||
|
Theme string
|
||||||
|
IconDraw LegendIconDraw
|
||||||
|
}
|
||||||
|
|
||||||
|
type LegendIconDrawOption struct {
|
||||||
|
Box chart.Box
|
||||||
|
Style chart.Style
|
||||||
|
Index int
|
||||||
|
Theme string
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
LegendAlignLeft = "left"
|
||||||
|
LegendAlignCenter = "center"
|
||||||
|
LegendAlignRight = "right"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
LegendTextPositionRight = "right"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LegendIconDraw func(r chart.Renderer, opt LegendIconDrawOption)
|
||||||
|
|
||||||
|
func DefaultLegendIconDraw(r chart.Renderer, opt LegendIconDrawOption) {
|
||||||
|
if opt.Box.IsZero() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.SetStrokeColor(opt.Style.GetStrokeColor())
|
||||||
|
stokeWidth := opt.Style.GetStrokeWidth()
|
||||||
|
r.SetStrokeWidth(stokeWidth)
|
||||||
|
height := opt.Box.Bottom - opt.Box.Top
|
||||||
|
ly := (height / 2) + 1
|
||||||
|
r.MoveTo(opt.Box.Left, ly)
|
||||||
|
r.LineTo(opt.Box.Right, ly)
|
||||||
|
r.Stroke()
|
||||||
|
r.SetFillColor(chart.ColorWhite)
|
||||||
|
r.Circle(5, (opt.Box.Left+opt.Box.Right)/2, ly)
|
||||||
|
r.FillStroke()
|
||||||
|
}
|
||||||
|
|
||||||
|
func DefaultLegend(c *chart.Chart) chart.Renderable {
|
||||||
|
return LegendCustomize(c, LegendOption{
|
||||||
|
TextPosition: LegendTextPositionRight,
|
||||||
|
IconDraw: DefaultLegendIconDraw,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func LegendCustomize(c *chart.Chart, opt LegendOption) chart.Renderable {
|
||||||
|
return func(r chart.Renderer, cb chart.Box, chartDefaults chart.Style) {
|
||||||
|
legendDefaults := chart.Style{
|
||||||
|
FontColor: chart.DefaultTextColor,
|
||||||
|
FontSize: 8.0,
|
||||||
|
StrokeColor: chart.DefaultAxisColor,
|
||||||
|
}
|
||||||
|
|
||||||
|
legendStyle := opt.Style.InheritFrom(chartDefaults.InheritFrom(legendDefaults))
|
||||||
|
|
||||||
|
r.SetFont(legendStyle.GetFont())
|
||||||
|
r.SetFontColor(legendStyle.GetFontColor())
|
||||||
|
r.SetFontSize(legendStyle.GetFontSize())
|
||||||
|
|
||||||
|
var labels []string
|
||||||
|
var lines []chart.Style
|
||||||
|
for _, s := range c.Series {
|
||||||
|
if !s.GetStyle().Hidden {
|
||||||
|
if _, isAnnotationSeries := s.(chart.AnnotationSeries); !isAnnotationSeries {
|
||||||
|
labels = append(labels, s.GetName())
|
||||||
|
lines = append(lines, s.GetStyle())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var textHeight int
|
||||||
|
var textWidth int
|
||||||
|
var textBox chart.Box
|
||||||
|
labelWidth := 0
|
||||||
|
for x := 0; x < len(labels); x++ {
|
||||||
|
if len(labels[x]) > 0 {
|
||||||
|
textBox = r.MeasureText(labels[x])
|
||||||
|
labelWidth += textBox.Width()
|
||||||
|
textHeight = chart.MaxInt(textBox.Height(), textHeight)
|
||||||
|
textWidth = chart.MaxInt(textBox.Width(), textWidth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
legendBoxHeight := textHeight + legendStyle.Padding.Top + legendStyle.Padding.Bottom
|
||||||
|
chartPadding := cb.Top
|
||||||
|
legendYMargin := (chartPadding - legendBoxHeight) >> 1
|
||||||
|
|
||||||
|
lineLengthMinimum := 25
|
||||||
|
|
||||||
|
labelWidth += lineLengthMinimum * len(labels)
|
||||||
|
|
||||||
|
left := 0
|
||||||
|
switch opt.Align {
|
||||||
|
case LegendAlignLeft:
|
||||||
|
left = 0
|
||||||
|
case LegendAlignRight:
|
||||||
|
left = cb.Width() - labelWidth
|
||||||
|
default:
|
||||||
|
left = (cb.Width() - labelWidth) / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
legendBox := chart.Box{
|
||||||
|
Left: left,
|
||||||
|
Right: left + labelWidth,
|
||||||
|
Top: 0,
|
||||||
|
Bottom: 0 + legendBoxHeight,
|
||||||
|
}
|
||||||
|
|
||||||
|
chart.Draw.Box(r, legendBox, legendDefaults)
|
||||||
|
|
||||||
|
r.SetFont(legendStyle.GetFont())
|
||||||
|
r.SetFontColor(legendStyle.GetFontColor())
|
||||||
|
r.SetFontSize(legendStyle.GetFontSize())
|
||||||
|
|
||||||
|
lineTextGap := 5
|
||||||
|
|
||||||
|
startX := legendBox.Left + legendStyle.Padding.Left
|
||||||
|
ty := legendYMargin + legendStyle.Padding.Top + textHeight
|
||||||
|
var label string
|
||||||
|
var x int
|
||||||
|
iconDraw := opt.IconDraw
|
||||||
|
if iconDraw == nil {
|
||||||
|
iconDraw = DefaultLegendIconDraw
|
||||||
|
}
|
||||||
|
for index := range labels {
|
||||||
|
label = labels[index]
|
||||||
|
if len(label) > 0 {
|
||||||
|
x = startX
|
||||||
|
|
||||||
|
// 如果文本靠左显示
|
||||||
|
if opt.TextPosition != LegendTextPositionRight {
|
||||||
|
textBox = r.MeasureText(label)
|
||||||
|
r.Text(label, x, ty)
|
||||||
|
x = startX + textBox.Width() + lineTextGap
|
||||||
|
}
|
||||||
|
|
||||||
|
// 图标
|
||||||
|
iconDraw(r, LegendIconDrawOption{
|
||||||
|
Theme: opt.Theme,
|
||||||
|
Index: index,
|
||||||
|
Style: lines[index],
|
||||||
|
Box: chart.Box{
|
||||||
|
Left: x,
|
||||||
|
Top: ty,
|
||||||
|
Right: x + lineLengthMinimum,
|
||||||
|
Bottom: ty + textHeight,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
x += (lineLengthMinimum + lineTextGap)
|
||||||
|
|
||||||
|
// 如果文本靠右显示
|
||||||
|
if opt.TextPosition == LegendTextPositionRight {
|
||||||
|
textBox = r.MeasureText(label)
|
||||||
|
r.Text(label, x, ty)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算下一个legend的位置
|
||||||
|
startX += textBox.Width() + chart.DefaultMinimumTickHorizontalSpacing + lineTextGap + lineLengthMinimum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue