feat: support title render function
This commit is contained in:
parent
ccdaf70dcb
commit
ffbda8f214
12 changed files with 455 additions and 141 deletions
11
axis.go
11
axis.go
|
|
@ -239,6 +239,10 @@ func (d *Draw) axisTick(opt *axisOption) {
|
||||||
tickCount--
|
tickCount--
|
||||||
}
|
}
|
||||||
labelMargin := style.GetLabelMargin()
|
labelMargin := style.GetLabelMargin()
|
||||||
|
tickShow := true
|
||||||
|
if opt.style.TickShow != nil && !*opt.style.TickShow {
|
||||||
|
tickShow = false
|
||||||
|
}
|
||||||
|
|
||||||
tickLengthValue := style.GetTickLength()
|
tickLengthValue := style.GetTickLength()
|
||||||
labelHeight := labelMargin + opt.textMaxHeight
|
labelHeight := labelMargin + opt.textMaxHeight
|
||||||
|
|
@ -254,6 +258,7 @@ func (d *Draw) axisTick(opt *axisOption) {
|
||||||
if style.Position == PositionLeft {
|
if style.Position == PositionLeft {
|
||||||
x0 = labelWidth
|
x0 = labelWidth
|
||||||
}
|
}
|
||||||
|
if tickShow {
|
||||||
for _, v := range values {
|
for _, v := range values {
|
||||||
x := x0
|
x := x0
|
||||||
y := v
|
y := v
|
||||||
|
|
@ -261,10 +266,12 @@ func (d *Draw) axisTick(opt *axisOption) {
|
||||||
d.lineTo(x+tickLengthValue, y)
|
d.lineTo(x+tickLengthValue, y)
|
||||||
r.Stroke()
|
r.Stroke()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// 辅助线
|
// 辅助线
|
||||||
if style.SplitLineShow && !style.SplitLineColor.IsZero() {
|
if style.SplitLineShow && !style.SplitLineColor.IsZero() {
|
||||||
r.SetStrokeColor(style.SplitLineColor)
|
r.SetStrokeColor(style.SplitLineColor)
|
||||||
splitLineWidth := width - labelWidth
|
splitLineWidth := width - labelWidth - tickLengthValue
|
||||||
|
x0 = labelWidth + tickLengthValue
|
||||||
if position == PositionRight {
|
if position == PositionRight {
|
||||||
x0 = 0
|
x0 = 0
|
||||||
splitLineWidth = width - labelWidth - 1
|
splitLineWidth = width - labelWidth - 1
|
||||||
|
|
@ -284,6 +291,7 @@ func (d *Draw) axisTick(opt *axisOption) {
|
||||||
if position == PositionTop {
|
if position == PositionTop {
|
||||||
y0 = labelHeight
|
y0 = labelHeight
|
||||||
}
|
}
|
||||||
|
if tickShow {
|
||||||
for _, v := range values {
|
for _, v := range values {
|
||||||
x := v
|
x := v
|
||||||
y := y0
|
y := y0
|
||||||
|
|
@ -291,6 +299,7 @@ func (d *Draw) axisTick(opt *axisOption) {
|
||||||
d.lineTo(x, y)
|
d.lineTo(x, y)
|
||||||
r.Stroke()
|
r.Stroke()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// 辅助线
|
// 辅助线
|
||||||
if style.SplitLineShow && !style.SplitLineColor.IsZero() {
|
if style.SplitLineShow && !style.SplitLineColor.IsZero() {
|
||||||
r.SetStrokeColor(style.SplitLineColor)
|
r.SetStrokeColor(style.SplitLineColor)
|
||||||
|
|
|
||||||
|
|
@ -140,7 +140,7 @@ func TestAxis(t *testing.T) {
|
||||||
opt.style.BoundaryGap = FalseFlag()
|
opt.style.BoundaryGap = FalseFlag()
|
||||||
return opt
|
return opt
|
||||||
},
|
},
|
||||||
result: "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"400\" height=\"300\">\\n<path d=\"M 44 5\nL 44 295\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 5\nL 44 5\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 54\nL 44 54\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 103\nL 44 103\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 151\nL 44 151\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 199\nL 44 199\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 247\nL 44 247\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 295\nL 44 295\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 5\nL 395 5\" style=\"stroke-width:1;stroke:rgba(0,0,0,0.2);fill:none\"/><path d=\"M 39 54\nL 395 54\" style=\"stroke-width:1;stroke:rgba(0,0,0,0.2);fill:none\"/><path d=\"M 39 103\nL 395 103\" style=\"stroke-width:1;stroke:rgba(0,0,0,0.2);fill:none\"/><path d=\"M 39 151\nL 395 151\" style=\"stroke-width:1;stroke:rgba(0,0,0,0.2);fill:none\"/><path d=\"M 39 199\nL 395 199\" style=\"stroke-width:1;stroke:rgba(0,0,0,0.2);fill:none\"/><path d=\"M 39 247\nL 395 247\" style=\"stroke-width:1;stroke:rgba(0,0,0,0.2);fill:none\"/><text x=\"12\" y=\"299\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:12.8px;font-family:'Roboto Medium',sans-serif\">Mon</text><text x=\"16\" y=\"251\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:12.8px;font-family:'Roboto Medium',sans-serif\">Tue</text><text x=\"12\" y=\"203\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:12.8px;font-family:'Roboto Medium',sans-serif\">Wed</text><text x=\"16\" y=\"155\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:12.8px;font-family:'Roboto Medium',sans-serif\">Thu</text><text x=\"23\" y=\"107\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:12.8px;font-family:'Roboto Medium',sans-serif\">Fri</text><text x=\"19\" y=\"58\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:12.8px;font-family:'Roboto Medium',sans-serif\">Sat</text><text x=\"16\" y=\"9\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:12.8px;font-family:'Roboto Medium',sans-serif\">Sun</text></svg>",
|
result: "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"400\" height=\"300\">\\n<path d=\"M 44 5\nL 44 295\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 5\nL 44 5\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 54\nL 44 54\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 103\nL 44 103\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 151\nL 44 151\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 199\nL 44 199\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 247\nL 44 247\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 295\nL 44 295\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 44 5\nL 395 5\" style=\"stroke-width:1;stroke:rgba(0,0,0,0.2);fill:none\"/><path d=\"M 44 54\nL 395 54\" style=\"stroke-width:1;stroke:rgba(0,0,0,0.2);fill:none\"/><path d=\"M 44 103\nL 395 103\" style=\"stroke-width:1;stroke:rgba(0,0,0,0.2);fill:none\"/><path d=\"M 44 151\nL 395 151\" style=\"stroke-width:1;stroke:rgba(0,0,0,0.2);fill:none\"/><path d=\"M 44 199\nL 395 199\" style=\"stroke-width:1;stroke:rgba(0,0,0,0.2);fill:none\"/><path d=\"M 44 247\nL 395 247\" style=\"stroke-width:1;stroke:rgba(0,0,0,0.2);fill:none\"/><text x=\"12\" y=\"299\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:12.8px;font-family:'Roboto Medium',sans-serif\">Mon</text><text x=\"16\" y=\"251\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:12.8px;font-family:'Roboto Medium',sans-serif\">Tue</text><text x=\"12\" y=\"203\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:12.8px;font-family:'Roboto Medium',sans-serif\">Wed</text><text x=\"16\" y=\"155\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:12.8px;font-family:'Roboto Medium',sans-serif\">Thu</text><text x=\"23\" y=\"107\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:12.8px;font-family:'Roboto Medium',sans-serif\">Fri</text><text x=\"19\" y=\"58\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:12.8px;font-family:'Roboto Medium',sans-serif\">Sat</text><text x=\"16\" y=\"9\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:12.8px;font-family:'Roboto Medium',sans-serif\">Sun</text></svg>",
|
||||||
},
|
},
|
||||||
// 文本居中展示
|
// 文本居中展示
|
||||||
// axis位于left
|
// axis位于left
|
||||||
|
|
@ -150,7 +150,7 @@ func TestAxis(t *testing.T) {
|
||||||
opt.style.Position = PositionLeft
|
opt.style.Position = PositionLeft
|
||||||
return opt
|
return opt
|
||||||
},
|
},
|
||||||
result: "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"400\" height=\"300\">\\n<path d=\"M 44 5\nL 44 295\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 5\nL 44 5\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 47\nL 44 47\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 89\nL 44 89\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 131\nL 44 131\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 172\nL 44 172\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 213\nL 44 213\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 254\nL 44 254\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 295\nL 44 295\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 5\nL 395 5\" style=\"stroke-width:1;stroke:rgba(0,0,0,0.2);fill:none\"/><path d=\"M 39 47\nL 395 47\" style=\"stroke-width:1;stroke:rgba(0,0,0,0.2);fill:none\"/><path d=\"M 39 89\nL 395 89\" style=\"stroke-width:1;stroke:rgba(0,0,0,0.2);fill:none\"/><path d=\"M 39 131\nL 395 131\" style=\"stroke-width:1;stroke:rgba(0,0,0,0.2);fill:none\"/><path d=\"M 39 172\nL 395 172\" style=\"stroke-width:1;stroke:rgba(0,0,0,0.2);fill:none\"/><path d=\"M 39 213\nL 395 213\" style=\"stroke-width:1;stroke:rgba(0,0,0,0.2);fill:none\"/><path d=\"M 39 254\nL 395 254\" style=\"stroke-width:1;stroke:rgba(0,0,0,0.2);fill:none\"/><text x=\"12\" y=\"280\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:12.8px;font-family:'Roboto Medium',sans-serif\">Mon</text><text x=\"16\" y=\"239\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:12.8px;font-family:'Roboto Medium',sans-serif\">Tue</text><text x=\"12\" y=\"198\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:12.8px;font-family:'Roboto Medium',sans-serif\">Wed</text><text x=\"16\" y=\"157\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:12.8px;font-family:'Roboto Medium',sans-serif\">Thu</text><text x=\"23\" y=\"115\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:12.8px;font-family:'Roboto Medium',sans-serif\">Fri</text><text x=\"19\" y=\"73\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:12.8px;font-family:'Roboto Medium',sans-serif\">Sat</text><text x=\"16\" y=\"31\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:12.8px;font-family:'Roboto Medium',sans-serif\">Sun</text></svg>",
|
result: "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"400\" height=\"300\">\\n<path d=\"M 44 5\nL 44 295\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 5\nL 44 5\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 47\nL 44 47\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 89\nL 44 89\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 131\nL 44 131\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 172\nL 44 172\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 213\nL 44 213\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 254\nL 44 254\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 39 295\nL 44 295\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 44 5\nL 395 5\" style=\"stroke-width:1;stroke:rgba(0,0,0,0.2);fill:none\"/><path d=\"M 44 47\nL 395 47\" style=\"stroke-width:1;stroke:rgba(0,0,0,0.2);fill:none\"/><path d=\"M 44 89\nL 395 89\" style=\"stroke-width:1;stroke:rgba(0,0,0,0.2);fill:none\"/><path d=\"M 44 131\nL 395 131\" style=\"stroke-width:1;stroke:rgba(0,0,0,0.2);fill:none\"/><path d=\"M 44 172\nL 395 172\" style=\"stroke-width:1;stroke:rgba(0,0,0,0.2);fill:none\"/><path d=\"M 44 213\nL 395 213\" style=\"stroke-width:1;stroke:rgba(0,0,0,0.2);fill:none\"/><path d=\"M 44 254\nL 395 254\" style=\"stroke-width:1;stroke:rgba(0,0,0,0.2);fill:none\"/><text x=\"12\" y=\"280\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:12.8px;font-family:'Roboto Medium',sans-serif\">Mon</text><text x=\"16\" y=\"239\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:12.8px;font-family:'Roboto Medium',sans-serif\">Tue</text><text x=\"12\" y=\"198\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:12.8px;font-family:'Roboto Medium',sans-serif\">Wed</text><text x=\"16\" y=\"157\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:12.8px;font-family:'Roboto Medium',sans-serif\">Thu</text><text x=\"23\" y=\"115\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:12.8px;font-family:'Roboto Medium',sans-serif\">Fri</text><text x=\"19\" y=\"73\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:12.8px;font-family:'Roboto Medium',sans-serif\">Sat</text><text x=\"16\" y=\"31\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:12.8px;font-family:'Roboto Medium',sans-serif\">Sun</text></svg>",
|
||||||
},
|
},
|
||||||
// 文本按起始位置展示
|
// 文本按起始位置展示
|
||||||
// axis位于right
|
// axis位于right
|
||||||
|
|
|
||||||
75
chart.go
75
chart.go
|
|
@ -23,17 +23,13 @@
|
||||||
package charts
|
package charts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math"
|
||||||
|
|
||||||
"github.com/dustin/go-humanize"
|
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
type XAxisOption struct {
|
|
||||||
BoundaryGap *bool
|
|
||||||
Data []string
|
|
||||||
// TODO split number
|
|
||||||
}
|
|
||||||
|
|
||||||
type SeriesData struct {
|
type SeriesData struct {
|
||||||
Value float64
|
Value float64
|
||||||
Style chart.Style
|
Style chart.Style
|
||||||
|
|
@ -43,31 +39,6 @@ type Point struct {
|
||||||
Y int
|
Y int
|
||||||
}
|
}
|
||||||
|
|
||||||
type Range struct {
|
|
||||||
originalMin float64
|
|
||||||
originalMax float64
|
|
||||||
divideCount int
|
|
||||||
Min float64
|
|
||||||
Max float64
|
|
||||||
Size int
|
|
||||||
Boundary bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Range) getHeight(value float64) int {
|
|
||||||
v := 1 - value/(r.Max-r.Min)
|
|
||||||
return int(v * float64(r.Size))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Range) getWidth(value float64) int {
|
|
||||||
v := value / (r.Max - r.Min)
|
|
||||||
// 移至居中
|
|
||||||
if r.Boundary &&
|
|
||||||
r.divideCount != 0 {
|
|
||||||
v += 1 / float64(r.divideCount*2)
|
|
||||||
}
|
|
||||||
return int(v * float64(r.Size))
|
|
||||||
}
|
|
||||||
|
|
||||||
type Series struct {
|
type Series struct {
|
||||||
Type string
|
Type string
|
||||||
Name string
|
Name string
|
||||||
|
|
@ -78,6 +49,7 @@ type Series struct {
|
||||||
|
|
||||||
type ChartOption struct {
|
type ChartOption struct {
|
||||||
Theme string
|
Theme string
|
||||||
|
Title TitleOption
|
||||||
XAxis XAxisOption
|
XAxis XAxisOption
|
||||||
Width int
|
Width int
|
||||||
Height int
|
Height int
|
||||||
|
|
@ -87,6 +59,29 @@ type ChartOption struct {
|
||||||
BackgroundColor drawing.Color
|
BackgroundColor drawing.Color
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *ChartOption) FillDefault(t *Theme) {
|
||||||
|
if o.BackgroundColor.IsZero() {
|
||||||
|
o.BackgroundColor = t.GetBackgroundColor()
|
||||||
|
}
|
||||||
|
if o.Title.Style.FontColor.IsZero() {
|
||||||
|
o.Title.Style.FontColor = t.GetTitleColor()
|
||||||
|
}
|
||||||
|
if o.Title.Style.FontSize == 0 {
|
||||||
|
o.Title.Style.FontSize = 14
|
||||||
|
}
|
||||||
|
if o.Title.Style.Font == nil {
|
||||||
|
o.Title.Style.Font, _ = chart.GetDefaultFont()
|
||||||
|
}
|
||||||
|
if o.Title.Style.Padding.IsZero() {
|
||||||
|
o.Title.Style.Padding = chart.Box{
|
||||||
|
Left: 5,
|
||||||
|
Top: 5,
|
||||||
|
Right: 5,
|
||||||
|
Bottom: 5,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (o *ChartOption) getWidth() int {
|
func (o *ChartOption) getWidth() int {
|
||||||
if o.Width == 0 {
|
if o.Width == 0 {
|
||||||
return 600
|
return 600
|
||||||
|
|
@ -102,8 +97,8 @@ func (o *ChartOption) getHeight() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *ChartOption) getYRange(axisIndex int) Range {
|
func (o *ChartOption) getYRange(axisIndex int) Range {
|
||||||
min := float64(0)
|
min := math.MaxFloat64
|
||||||
max := float64(0)
|
max := -math.MaxFloat64
|
||||||
|
|
||||||
for _, series := range o.SeriesList {
|
for _, series := range o.SeriesList {
|
||||||
if series.YAxisIndex != axisIndex {
|
if series.YAxisIndex != axisIndex {
|
||||||
|
|
@ -118,18 +113,8 @@ func (o *ChartOption) getYRange(axisIndex int) Range {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO 对于小数的处理
|
// y轴分设置默认划分为6块
|
||||||
|
r := NewRange(min*0.9, max*1.1, 6)
|
||||||
divideCount := 6
|
|
||||||
r := Range{
|
|
||||||
originalMin: min,
|
|
||||||
originalMax: max,
|
|
||||||
Min: float64(int(min * 0.8)),
|
|
||||||
Max: max * 1.2,
|
|
||||||
divideCount: divideCount,
|
|
||||||
}
|
|
||||||
value := int((r.Max - r.Min) / float64(divideCount))
|
|
||||||
r.Max = float64(int(float64(value*divideCount) + r.Min))
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
1
draw.go
1
draw.go
|
|
@ -34,6 +34,7 @@ import (
|
||||||
const (
|
const (
|
||||||
PositionLeft = "left"
|
PositionLeft = "left"
|
||||||
PositionRight = "right"
|
PositionRight = "right"
|
||||||
|
PositionCenter = "center"
|
||||||
PositionTop = "top"
|
PositionTop = "top"
|
||||||
PositionBottom = "bottom"
|
PositionBottom = "bottom"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -31,78 +31,6 @@ type LineChartOption struct {
|
||||||
ChartOption
|
ChartOption
|
||||||
}
|
}
|
||||||
|
|
||||||
const YAxisWidth = 50
|
|
||||||
|
|
||||||
func drawXAxis(d *Draw, opt *XAxisOption, theme *Theme) (int, *Range, error) {
|
|
||||||
dXAxis, err := NewDraw(
|
|
||||||
DrawOption{
|
|
||||||
Parent: d,
|
|
||||||
},
|
|
||||||
PaddingOption(chart.Box{
|
|
||||||
Left: YAxisWidth,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return 0, nil, err
|
|
||||||
}
|
|
||||||
data := NewAxisDataListFromStringList(opt.Data)
|
|
||||||
style := AxisStyle{
|
|
||||||
BoundaryGap: opt.BoundaryGap,
|
|
||||||
StrokeColor: theme.GetAxisStrokeColor(),
|
|
||||||
FontColor: theme.GetAxisStrokeColor(),
|
|
||||||
StrokeWidth: 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
boundary := true
|
|
||||||
max := float64(len(opt.Data))
|
|
||||||
if opt.BoundaryGap != nil && !*opt.BoundaryGap {
|
|
||||||
boundary = false
|
|
||||||
max--
|
|
||||||
}
|
|
||||||
|
|
||||||
dXAxis.Axis(data, style)
|
|
||||||
return d.measureAxis(data, style), &Range{
|
|
||||||
divideCount: len(opt.Data),
|
|
||||||
Min: 0,
|
|
||||||
Max: max,
|
|
||||||
Size: dXAxis.Box.Width(),
|
|
||||||
Boundary: boundary,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func drawYAxis(d *Draw, opt *ChartOption, theme *Theme, xAxisHeight int) (*Range, error) {
|
|
||||||
yRange := opt.getYRange(0)
|
|
||||||
data := NewAxisDataListFromStringList(yRange.Values())
|
|
||||||
style := AxisStyle{
|
|
||||||
Position: PositionLeft,
|
|
||||||
BoundaryGap: FalseFlag(),
|
|
||||||
// StrokeColor: theme.GetAxisStrokeColor(),
|
|
||||||
FontColor: theme.GetAxisStrokeColor(),
|
|
||||||
StrokeWidth: 1,
|
|
||||||
SplitLineColor: theme.GetAxisSplitLineColor(),
|
|
||||||
SplitLineShow: true,
|
|
||||||
}
|
|
||||||
width := d.measureAxis(data, style)
|
|
||||||
|
|
||||||
dYAxis, err := NewDraw(
|
|
||||||
DrawOption{
|
|
||||||
Parent: d,
|
|
||||||
Width: d.Box.Width(),
|
|
||||||
// 减去x轴的高
|
|
||||||
Height: d.Box.Height() - xAxisHeight,
|
|
||||||
},
|
|
||||||
PaddingOption(chart.Box{
|
|
||||||
Left: YAxisWidth - width,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
dYAxis.Axis(data, style)
|
|
||||||
yRange.Size = dYAxis.Box.Height()
|
|
||||||
return &yRange, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLineChart(opt LineChartOption) (*Draw, error) {
|
func NewLineChart(opt LineChartOption) (*Draw, error) {
|
||||||
d, err := NewDraw(
|
d, err := NewDraw(
|
||||||
DrawOption{
|
DrawOption{
|
||||||
|
|
@ -119,27 +47,35 @@ func NewLineChart(opt LineChartOption) (*Draw, error) {
|
||||||
theme := Theme{
|
theme := Theme{
|
||||||
mode: opt.Theme,
|
mode: opt.Theme,
|
||||||
}
|
}
|
||||||
// 设置背景色
|
opt.FillDefault(&theme)
|
||||||
bg := opt.BackgroundColor
|
|
||||||
if bg.IsZero() {
|
|
||||||
bg = theme.GetBackgroundColor()
|
|
||||||
}
|
|
||||||
if opt.Parent == nil {
|
if opt.Parent == nil {
|
||||||
d.setBackground(opt.getWidth(), opt.getHeight(), bg)
|
d.setBackground(opt.getWidth(), opt.getHeight(), opt.BackgroundColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 标题
|
||||||
|
_, titleHeight, err := drawTitle(d, &opt.Title)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// xAxis
|
||||||
xAxisHeight, xRange, err := drawXAxis(d, &opt.XAxis, &theme)
|
xAxisHeight, xRange, err := drawXAxis(d, &opt.XAxis, &theme)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 暂时仅支持单一yaxis
|
// 暂时仅支持单一yaxis
|
||||||
yRange, err := drawYAxis(d, &opt.ChartOption, &theme, xAxisHeight)
|
yRange, err := drawYAxis(d, &opt.ChartOption, &theme, xAxisHeight, chart.Box{
|
||||||
|
Top: titleHeight,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
sd, err := NewDraw(DrawOption{
|
sd, err := NewDraw(DrawOption{
|
||||||
Parent: d,
|
Parent: d,
|
||||||
}, PaddingOption(chart.Box{
|
}, PaddingOption(chart.Box{
|
||||||
|
Top: titleHeight,
|
||||||
Left: YAxisWidth,
|
Left: YAxisWidth,
|
||||||
}))
|
}))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -166,9 +102,7 @@ func NewLineChart(opt LineChartOption) (*Draw, error) {
|
||||||
DotFillColor: dotFillColor,
|
DotFillColor: dotFillColor,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
// fmt.Println(yRange)
|
|
||||||
|
|
||||||
return d, nil
|
return d, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
75
range.go
Normal file
75
range.go
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
// 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 (
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Range struct {
|
||||||
|
divideCount int
|
||||||
|
Min float64
|
||||||
|
Max float64
|
||||||
|
Size int
|
||||||
|
Boundary bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRange(min, max float64, divideCount int) Range {
|
||||||
|
r := math.Abs(max - min)
|
||||||
|
|
||||||
|
// 最小单位计算
|
||||||
|
unit := 5
|
||||||
|
if r > 100 {
|
||||||
|
unit = 20
|
||||||
|
}
|
||||||
|
unit = int((r/float64(divideCount))/float64(unit))*unit + unit
|
||||||
|
|
||||||
|
if min != 0 {
|
||||||
|
min = float64(int(min/float64(unit)) * unit)
|
||||||
|
// 如果是小于0,int的时候向上取整了,因此调整
|
||||||
|
if min < 0 {
|
||||||
|
min -= float64(unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
max = min + float64(unit*divideCount)
|
||||||
|
return Range{
|
||||||
|
Min: min,
|
||||||
|
Max: max,
|
||||||
|
divideCount: divideCount,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Range) getHeight(value float64) int {
|
||||||
|
v := 1 - (value-r.Min)/(r.Max-r.Min)
|
||||||
|
return int(v * float64(r.Size))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Range) getWidth(value float64) int {
|
||||||
|
v := value / (r.Max - r.Min)
|
||||||
|
// 移至居中
|
||||||
|
if r.Boundary &&
|
||||||
|
r.divideCount != 0 {
|
||||||
|
v += 1 / float64(r.divideCount*2)
|
||||||
|
}
|
||||||
|
return int(v * float64(r.Size))
|
||||||
|
}
|
||||||
49
range_test.go
Normal file
49
range_test.go
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
// 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 (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRange(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
r := NewRange(0, 8, 6)
|
||||||
|
assert.Equal(0.0, r.Min)
|
||||||
|
assert.Equal(30.0, r.Max)
|
||||||
|
|
||||||
|
r = NewRange(0, 12, 6)
|
||||||
|
assert.Equal(0.0, r.Min)
|
||||||
|
assert.Equal(30.0, r.Max)
|
||||||
|
|
||||||
|
r = NewRange(-13, 18, 6)
|
||||||
|
assert.Equal(-20.0, r.Min)
|
||||||
|
assert.Equal(40.0, r.Max)
|
||||||
|
|
||||||
|
r = NewRange(0, 400, 6)
|
||||||
|
assert.Equal(0.0, r.Min)
|
||||||
|
assert.Equal(480.0, r.Max)
|
||||||
|
}
|
||||||
17
theme.go
17
theme.go
|
|
@ -120,3 +120,20 @@ func (t *Theme) GetBackgroundColor() drawing.Color {
|
||||||
}
|
}
|
||||||
return drawing.ColorWhite
|
return drawing.ColorWhite
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Theme) GetTitleColor() drawing.Color {
|
||||||
|
if t.IsDark() {
|
||||||
|
return drawing.Color{
|
||||||
|
R: 238,
|
||||||
|
G: 241,
|
||||||
|
B: 250,
|
||||||
|
A: 255,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return drawing.Color{
|
||||||
|
R: 70,
|
||||||
|
G: 70,
|
||||||
|
B: 70,
|
||||||
|
A: 255,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
112
title.go
Normal file
112
title.go
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
// 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 (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/wcharczuk/go-chart/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TitleOption struct {
|
||||||
|
Text string
|
||||||
|
Style chart.Style
|
||||||
|
Left string
|
||||||
|
Top string
|
||||||
|
}
|
||||||
|
type titleMeasureOption struct {
|
||||||
|
width int
|
||||||
|
height int
|
||||||
|
text string
|
||||||
|
}
|
||||||
|
|
||||||
|
func drawTitle(d *Draw, opt *TitleOption) (int, int, error) {
|
||||||
|
if len(opt.Text) == 0 {
|
||||||
|
return 0, 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
padding := opt.Style.Padding
|
||||||
|
titleDraw, err := NewDraw(DrawOption{
|
||||||
|
Parent: d,
|
||||||
|
}, PaddingOption(padding))
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
r := titleDraw.Render
|
||||||
|
opt.Style.GetTextOptions().WriteToRenderer(r)
|
||||||
|
arr := strings.Split(opt.Text, "\n")
|
||||||
|
textMaxWidth := 0
|
||||||
|
textMaxHeight := 0
|
||||||
|
width := 0
|
||||||
|
measureOptions := make([]titleMeasureOption, len(arr))
|
||||||
|
for index, str := range arr {
|
||||||
|
textBox := r.MeasureText(str)
|
||||||
|
|
||||||
|
w := textBox.Width()
|
||||||
|
h := textBox.Height()
|
||||||
|
if w > textMaxWidth {
|
||||||
|
textMaxWidth = w
|
||||||
|
}
|
||||||
|
if h > textMaxHeight {
|
||||||
|
textMaxHeight = h
|
||||||
|
}
|
||||||
|
measureOptions[index] = titleMeasureOption{
|
||||||
|
text: str,
|
||||||
|
width: w,
|
||||||
|
height: h,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
width = textMaxWidth
|
||||||
|
titleX := 0
|
||||||
|
b := titleDraw.Box
|
||||||
|
switch opt.Left {
|
||||||
|
case PositionRight:
|
||||||
|
titleX = b.Width() - textMaxWidth
|
||||||
|
case PositionCenter:
|
||||||
|
titleX = b.Width()>>1 - (textMaxWidth >> 1)
|
||||||
|
default:
|
||||||
|
if strings.HasSuffix(opt.Left, "%") {
|
||||||
|
value, _ := strconv.Atoi(strings.ReplaceAll(opt.Left, "%", ""))
|
||||||
|
titleX = b.Width() * value / 100
|
||||||
|
} else {
|
||||||
|
value, _ := strconv.Atoi(opt.Left)
|
||||||
|
titleX = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
titleY := 0
|
||||||
|
// TODO TOP 暂只支持数值
|
||||||
|
if opt.Top != "" {
|
||||||
|
value, _ := strconv.Atoi(opt.Top)
|
||||||
|
titleY += value
|
||||||
|
}
|
||||||
|
for _, item := range measureOptions {
|
||||||
|
x := titleX + (textMaxWidth-item.width)>>1
|
||||||
|
titleDraw.text(item.text, x, titleY)
|
||||||
|
titleY += textMaxHeight
|
||||||
|
}
|
||||||
|
height := titleY + padding.Top + padding.Bottom
|
||||||
|
|
||||||
|
return width, height, nil
|
||||||
|
}
|
||||||
1
util.go
1
util.go
|
|
@ -67,6 +67,7 @@ func maxInt(values ...int) int {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// measureTextMaxWidthHeight returns maxWidth and maxHeight of text list
|
||||||
func measureTextMaxWidthHeight(textList []string, r chart.Renderer) (int, int) {
|
func measureTextMaxWidthHeight(textList []string, r chart.Renderer) (int, int) {
|
||||||
maxWidth := 0
|
maxWidth := 0
|
||||||
maxHeight := 0
|
maxHeight := 0
|
||||||
|
|
|
||||||
69
xaxis.go
Normal file
69
xaxis.go
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
// 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/wcharczuk/go-chart/v2"
|
||||||
|
|
||||||
|
type XAxisOption struct {
|
||||||
|
BoundaryGap *bool
|
||||||
|
Data []string
|
||||||
|
// TODO split number
|
||||||
|
}
|
||||||
|
|
||||||
|
// drawXAxis draws x axis, and returns the height, range of if.
|
||||||
|
func drawXAxis(d *Draw, opt *XAxisOption, theme *Theme) (int, *Range, error) {
|
||||||
|
dXAxis, err := NewDraw(
|
||||||
|
DrawOption{
|
||||||
|
Parent: d,
|
||||||
|
},
|
||||||
|
PaddingOption(chart.Box{
|
||||||
|
Left: YAxisWidth,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, err
|
||||||
|
}
|
||||||
|
data := NewAxisDataListFromStringList(opt.Data)
|
||||||
|
style := AxisStyle{
|
||||||
|
BoundaryGap: opt.BoundaryGap,
|
||||||
|
StrokeColor: theme.GetAxisStrokeColor(),
|
||||||
|
FontColor: theme.GetAxisStrokeColor(),
|
||||||
|
StrokeWidth: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
boundary := true
|
||||||
|
max := float64(len(opt.Data))
|
||||||
|
if opt.BoundaryGap != nil && !*opt.BoundaryGap {
|
||||||
|
boundary = false
|
||||||
|
max--
|
||||||
|
}
|
||||||
|
|
||||||
|
dXAxis.Axis(data, style)
|
||||||
|
return d.measureAxis(data, style), &Range{
|
||||||
|
divideCount: len(opt.Data),
|
||||||
|
Min: 0,
|
||||||
|
Max: max,
|
||||||
|
Size: dXAxis.Box.Width(),
|
||||||
|
Boundary: boundary,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
62
yaxis.go
Normal file
62
yaxis.go
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
// 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/wcharczuk/go-chart/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
const YAxisWidth = 40
|
||||||
|
|
||||||
|
func drawYAxis(d *Draw, opt *ChartOption, theme *Theme, xAxisHeight int, padding chart.Box) (*Range, error) {
|
||||||
|
yRange := opt.getYRange(0)
|
||||||
|
data := NewAxisDataListFromStringList(yRange.Values())
|
||||||
|
style := AxisStyle{
|
||||||
|
Position: PositionLeft,
|
||||||
|
BoundaryGap: FalseFlag(),
|
||||||
|
FontColor: theme.GetAxisStrokeColor(),
|
||||||
|
TickShow: FalseFlag(),
|
||||||
|
StrokeWidth: 1,
|
||||||
|
SplitLineColor: theme.GetAxisSplitLineColor(),
|
||||||
|
SplitLineShow: true,
|
||||||
|
}
|
||||||
|
width := d.measureAxis(data, style)
|
||||||
|
|
||||||
|
padding.Left += (YAxisWidth - width)
|
||||||
|
|
||||||
|
dYAxis, err := NewDraw(
|
||||||
|
DrawOption{
|
||||||
|
Parent: d,
|
||||||
|
Width: d.Box.Width(),
|
||||||
|
// 减去x轴的高
|
||||||
|
Height: d.Box.Height() - xAxisHeight,
|
||||||
|
},
|
||||||
|
PaddingOption(padding),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dYAxis.Axis(data, style)
|
||||||
|
yRange.Size = dYAxis.Box.Height()
|
||||||
|
return &yRange, nil
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue