diff --git a/axis.go b/axis.go
index 14648cf..29cc3cc 100644
--- a/axis.go
+++ b/axis.go
@@ -78,14 +78,17 @@ func NewAxis(d *Draw, data AxisDataList, style AxisOption) *axis {
}
+// GetLabelMargin returns the label margin value
func (as *AxisOption) GetLabelMargin() int {
return getDefaultInt(as.LabelMargin, 8)
}
+// GetTickLength returns the tick length value
func (as *AxisOption) GetTickLength() int {
return getDefaultInt(as.TickLength, 5)
}
+// Style returns the style of axis
func (as *AxisOption) Style(f *truetype.Font) chart.Style {
s := chart.Style{
ClassName: as.ClassName,
@@ -109,6 +112,7 @@ type AxisData struct {
}
type AxisDataList []AxisData
+// TextList returns the text list of axis data
func (l AxisDataList) TextList() []string {
textList := make([]string, len(l))
for index, item := range l {
@@ -125,6 +129,7 @@ type axisRenderOption struct {
modValue int
}
+// NewAxisDataListFromStringList creates a new axis data list from string list
func NewAxisDataListFromStringList(textList []string) AxisDataList {
list := make(AxisDataList, len(textList))
for index, text := range textList {
diff --git a/bar_chart.go b/bar_chart.go
index d9917f1..7a5805b 100644
--- a/bar_chart.go
+++ b/bar_chart.go
@@ -37,7 +37,7 @@ func barChartRender(opt ChartOption, result *basicRenderResult) (*Draw, error) {
if err != nil {
return nil, err
}
- yRange := result.yRange
+ // yRange := result.yRange
xRange := result.xRange
x0, x1 := xRange.GetRange(0)
width := int(x1 - x0)
@@ -50,7 +50,7 @@ func barChartRender(opt ChartOption, result *basicRenderResult) (*Draw, error) {
// 总的宽度-两个margin-(总数-1)的barMargin
barWidth := (width - 2*margin - barMargin*(seriesCount-1)) / len(opt.SeriesList)
- barMaxHeight := yRange.Size
+ barMaxHeight := result.getYRange(0).Size
theme := NewTheme(opt.Theme)
seriesNames := opt.Legend.Data
@@ -58,6 +58,9 @@ func barChartRender(opt ChartOption, result *basicRenderResult) (*Draw, error) {
r := d.Render
for i, series := range opt.SeriesList {
+ yRange := result.getYRange(series.YAxisIndex)
+ points := make([]Point, len(series.Data))
+ seriesColor := theme.GetSeriesColor(i)
for j, item := range series.Data {
x0, _ := xRange.GetRange(j)
x := int(x0)
@@ -67,26 +70,32 @@ func barChartRender(opt ChartOption, result *basicRenderResult) (*Draw, error) {
}
h := int(yRange.getHeight(item.Value))
- fillColor := theme.GetSeriesColor(i)
+ fillColor := seriesColor
if !item.Style.FillColor.IsZero() {
fillColor = item.Style.FillColor
}
-
+ top := barMaxHeight - h
d.Bar(chart.Box{
- Top: barMaxHeight - h,
+ Top: top,
Left: x,
Right: x + barWidth,
Bottom: barMaxHeight - 1,
}, BarStyle{
FillColor: fillColor,
})
+ // 用于生成marker point
+ points[j] = Point{
+ // 居中的位置
+ X: x + barWidth>>1,
+ Y: top,
+ }
if !series.Label.Show {
continue
}
text := NewValueLabelFormater(seriesNames, series.Label.Formatter)(i, item.Value, -1)
labelStyle := chart.Style{
FontColor: theme.GetTextColor(),
- FontSize: 10,
+ FontSize: labelFontSize,
Font: opt.Font,
}
if !series.Label.Color.IsZero() {
@@ -96,6 +105,12 @@ func barChartRender(opt ChartOption, result *basicRenderResult) (*Draw, error) {
textBox := r.MeasureText(text)
d.text(text, x+(barWidth-textBox.Width())>>1, barMaxHeight-h-5)
}
+ markPointRender(d, markPointRenderOption{
+ FillColor: seriesColor,
+ Font: opt.Font,
+ Points: points,
+ Series: &series,
+ })
}
return result.d, nil
diff --git a/chart.go b/chart.go
index 7c511de..dbc4db4 100644
--- a/chart.go
+++ b/chart.go
@@ -42,6 +42,8 @@ type Point struct {
Y int
}
+const labelFontSize = 10
+
type ChartOption struct {
Type string
Font *truetype.Font
@@ -49,7 +51,7 @@ type ChartOption struct {
Title TitleOption
Legend LegendOption
XAxis XAxisOption
- YAxis YAxisOption
+ YAxisList []YAxisOption
Width int
Height int
Parent *Draw
@@ -61,6 +63,17 @@ type ChartOption struct {
func (o *ChartOption) FillDefault(theme string) {
t := NewTheme(theme)
+ // 如果为空,初始化
+ yAxisCount := 1
+ for _, series := range o.SeriesList {
+ if series.YAxisIndex >= yAxisCount {
+ yAxisCount++
+ }
+ }
+ yAxisList := make([]YAxisOption, yAxisCount)
+ copy(yAxisList, o.YAxisList)
+ o.YAxisList = yAxisList
+
if o.Font == nil {
o.Font, _ = chart.GetDefaultFont()
}
@@ -136,9 +149,13 @@ func (o *ChartOption) getHeight() int {
return o.Height
}
-func (o *ChartOption) getYRange(axisIndex int) Range {
+func (o *ChartOption) newYRange(axisIndex int) Range {
min := math.MaxFloat64
max := -math.MaxFloat64
+ if axisIndex >= len(o.YAxisList) {
+ axisIndex = 0
+ }
+ yAxis := o.YAxisList[axisIndex]
for _, series := range o.SeriesList {
if series.YAxisIndex != axisIndex {
@@ -155,21 +172,21 @@ func (o *ChartOption) getYRange(axisIndex int) Range {
}
min = min * 0.9
max = max * 1.1
- if o.YAxis.Min != nil {
- min = *o.YAxis.Min
+ if yAxis.Min != nil {
+ min = *yAxis.Min
}
- if o.YAxis.Max != nil {
- max = *o.YAxis.Max
+ if yAxis.Max != nil {
+ max = *yAxis.Max
}
divideCount := 6
// y轴分设置默认划分为6块
r := NewRange(min, max, divideCount)
// 由于NewRange会重新计算min max
- if o.YAxis.Min != nil {
+ if yAxis.Min != nil {
r.Min = min
}
- if o.YAxis.Max != nil {
+ if yAxis.Max != nil {
r.Max = max
}
@@ -177,10 +194,17 @@ func (o *ChartOption) getYRange(axisIndex int) Range {
}
type basicRenderResult struct {
- xRange *Range
- yRange *Range
- d *Draw
- titleBox chart.Box
+ xRange *Range
+ yRangeList []*Range
+ d *Draw
+ titleBox chart.Box
+}
+
+func (r *basicRenderResult) getYRange(index int) *Range {
+ if index >= len(r.yRangeList) {
+ index = 0
+ }
+ return r.yRangeList[index]
}
func Render(opt ChartOption) (*Draw, error) {
@@ -206,7 +230,9 @@ func Render(opt ChartOption) (*Draw, error) {
// pie不需要axis
if isPieChart {
opt.XAxis.Hidden = true
- opt.YAxis.Hidden = true
+ for index := range opt.YAxisList {
+ opt.YAxisList[index].Hidden = true
+ }
}
result, err := chartBasicRender(&opt)
if err != nil {
@@ -284,6 +310,9 @@ func chartBasicRender(opt *ChartOption) (*basicRenderResult, error) {
}
opt.FillDefault(opt.Theme)
+ if len(opt.YAxisList) > 2 {
+ return nil, errors.New("y axis should not be gt 2")
+ }
if opt.Parent == nil {
d.setBackground(opt.getWidth(), opt.getHeight(), opt.BackgroundColor)
}
@@ -299,26 +328,30 @@ func chartBasicRender(opt *ChartOption) (*basicRenderResult, error) {
if !opt.XAxis.Hidden {
// xAxis
- xAxisHeight, xRange, err = drawXAxis(d, &opt.XAxis)
+ xAxisHeight, xRange, err = drawXAxis(d, &opt.XAxis, len(opt.YAxisList))
if err != nil {
return nil, err
}
}
- // 暂时仅支持单一yaxis
- var yRange *Range
- if !opt.YAxis.Hidden {
- yRange, err = drawYAxis(d, opt, xAxisHeight, chart.Box{
- Top: titleBox.Height(),
- })
- if err != nil {
- return nil, err
+ yRangeList := make([]*Range, len(opt.YAxisList))
+
+ for index, yAxis := range opt.YAxisList {
+ var yRange *Range
+ if !yAxis.Hidden {
+ yRange, err = drawYAxis(d, opt, index, xAxisHeight, chart.Box{
+ Top: titleBox.Height(),
+ })
+ if err != nil {
+ return nil, err
+ }
+ yRangeList[index] = yRange
}
}
return &basicRenderResult{
- xRange: xRange,
- yRange: yRange,
- d: d,
- titleBox: titleBox,
+ xRange: xRange,
+ yRangeList: yRangeList,
+ d: d,
+ titleBox: titleBox,
}, nil
}
diff --git a/draw.go b/draw.go
index c7f0849..5c65d87 100644
--- a/draw.go
+++ b/draw.go
@@ -173,7 +173,60 @@ func (d *Draw) pin(x, y, width int) {
cx := x
cy := y + int(r*2.5)
d.Render.QuadCurveTo(cx+left, cy+top, endX+left, endY+top)
- d.Render.Fill()
+ d.Render.Stroke()
+}
+
+func (d *Draw) arrowLeft(x, y, width, height int) {
+ d.arrow(x, y, width, height, PositionLeft)
+}
+
+func (d *Draw) arrowRight(x, y, width, height int) {
+ d.arrow(x, y, width, height, PositionRight)
+}
+
+func (d *Draw) arrowTop(x, y, width, height int) {
+ d.arrow(x, y, width, height, PositionTop)
+}
+func (d *Draw) arrowBottom(x, y, width, height int) {
+ d.arrow(x, y, width, height, PositionBottom)
+}
+
+func (d *Draw) arrow(x, y, width, height int, direction string) {
+ halfWidth := width >> 1
+ halfHeight := height >> 1
+ if direction == PositionTop || direction == PositionBottom {
+ x0 := x - halfWidth
+ x1 := x0 + width
+ dy := -height / 3
+ y0 := y
+ y1 := y0 - height
+ if direction == PositionBottom {
+ y0 = y - height
+ y1 = y
+ dy = 2 * dy
+ }
+ d.moveTo(x0, y0)
+ d.lineTo(x0+halfWidth, y1)
+ d.lineTo(x1, y0)
+ d.lineTo(x0+halfWidth, y+dy)
+ d.lineTo(x0, y0)
+ } else {
+ x0 := x + width
+ x1 := x0 - width
+ y0 := y - halfHeight
+ dx := -width / 3
+ if direction == PositionRight {
+ x0 = x - width
+ dx = -dx
+ x1 = x0 + width
+ }
+ d.moveTo(x0, y0)
+ d.lineTo(x1, y0+halfHeight)
+ d.lineTo(x0, y0+height)
+ d.lineTo(x0+dx, y0+halfHeight)
+ d.lineTo(x0, y0)
+ }
+ d.Render.Stroke()
}
func (d *Draw) circle(radius float64, x, y int) {
diff --git a/draw_test.go b/draw_test.go
index dcba961..efda7eb 100644
--- a/draw_test.go
+++ b/draw_test.go
@@ -239,6 +239,7 @@ func TestDraw(t *testing.T) {
},
result: "",
},
+ // pin
{
fn: func(d *Draw) {
chart.Style{
@@ -260,6 +261,94 @@ func TestDraw(t *testing.T) {
},
result: "",
},
+ // arrow left
+ {
+ fn: func(d *Draw) {
+ chart.Style{
+ StrokeWidth: 1,
+ StrokeColor: drawing.Color{
+ R: 84,
+ G: 112,
+ B: 198,
+ A: 255,
+ },
+ FillColor: drawing.Color{
+ R: 84,
+ G: 112,
+ B: 198,
+ A: 255,
+ },
+ }.WriteToRenderer(d.Render)
+ d.arrowLeft(30, 30, 16, 10)
+ },
+ result: "",
+ },
+ // arrow right
+ {
+ fn: func(d *Draw) {
+ chart.Style{
+ StrokeWidth: 1,
+ StrokeColor: drawing.Color{
+ R: 84,
+ G: 112,
+ B: 198,
+ A: 255,
+ },
+ FillColor: drawing.Color{
+ R: 84,
+ G: 112,
+ B: 198,
+ A: 255,
+ },
+ }.WriteToRenderer(d.Render)
+ d.arrowRight(30, 30, 16, 10)
+ },
+ result: "",
+ },
+ // arrow top
+ {
+ fn: func(d *Draw) {
+ chart.Style{
+ StrokeWidth: 1,
+ StrokeColor: drawing.Color{
+ R: 84,
+ G: 112,
+ B: 198,
+ A: 255,
+ },
+ FillColor: drawing.Color{
+ R: 84,
+ G: 112,
+ B: 198,
+ A: 255,
+ },
+ }.WriteToRenderer(d.Render)
+ d.arrowTop(30, 30, 10, 16)
+ },
+ result: "",
+ },
+ // arrow bottom
+ {
+ fn: func(d *Draw) {
+ chart.Style{
+ StrokeWidth: 1,
+ StrokeColor: drawing.Color{
+ R: 84,
+ G: 112,
+ B: 198,
+ A: 255,
+ },
+ FillColor: drawing.Color{
+ R: 84,
+ G: 112,
+ B: 198,
+ A: 255,
+ },
+ }.WriteToRenderer(d.Render)
+ d.arrowBottom(30, 30, 10, 16)
+ },
+ result: "",
+ },
}
for _, tt := range tests {
d, err := NewDraw(DrawOption{
diff --git a/line_chart.go b/line_chart.go
index 91e1a6d..39d8b4d 100644
--- a/line_chart.go
+++ b/line_chart.go
@@ -23,8 +23,6 @@
package charts
import (
- "math"
-
"github.com/wcharczuk/go-chart/v2"
"github.com/wcharczuk/go-chart/v2/drawing"
)
@@ -45,36 +43,25 @@ func lineChartRender(opt ChartOption, result *basicRenderResult) (*Draw, error)
seriesNames := opt.Legend.Data
r := d.Render
- yRange := result.yRange
xRange := result.xRange
for i, series := range opt.SeriesList {
- points := make([]Point, 0)
- minIndex := -1
- maxIndex := -1
- minValue := math.MaxFloat64
- maxValue := -math.MaxFloat64
+ yRange := result.getYRange(series.YAxisIndex)
+ points := make([]Point, len(series.Data))
+
for j, item := range series.Data {
- if item.Value < minValue {
- minIndex = j
- minValue = item.Value
- }
- if item.Value > maxValue {
- maxIndex = j
- maxValue = item.Value
- }
y := yRange.getRestHeight(item.Value)
x := xRange.getWidth(float64(j))
- points = append(points, Point{
+ points[j] = Point{
Y: y,
X: x,
- })
+ }
if !series.Label.Show {
continue
}
text := NewValueLabelFormater(seriesNames, series.Label.Formatter)(i, item.Value, -1)
labelStyle := chart.Style{
FontColor: theme.GetTextColor(),
- FontSize: 10,
+ FontSize: labelFontSize,
Font: opt.Font,
}
if !series.Label.Color.IsZero() {
@@ -101,33 +88,12 @@ func lineChartRender(opt ChartOption, result *basicRenderResult) (*Draw, error)
DotFillColor: dotFillColor,
})
// draw mark point
- symbolSize := 30
- if series.MarkPoint.SymbolSize > 0 {
- symbolSize = series.MarkPoint.SymbolSize
- }
- for _, markPointData := range series.MarkPoint.Data {
- p := points[minIndex]
- value := minValue
- switch markPointData.Type {
- case SeriesMarkPointDataTypeMax:
- p = points[maxIndex]
- value = maxValue
- }
- chart.Style{
- FillColor: seriesColor,
- }.WriteToRenderer(r)
- d.pin(p.X, p.Y-symbolSize>>1, symbolSize)
-
- chart.Style{
- FontColor: NewTheme(ThemeDark).GetTextColor(),
- FontSize: 10,
- StrokeWidth: 1,
- Font: opt.Font,
- }.WriteTextOptionsToRenderer(d.Render)
- text := commafWithDigits(value)
- textBox := r.MeasureText(text)
- d.text(text, p.X-textBox.Width()>>1, p.Y-symbolSize>>1-2)
- }
+ markPointRender(d, markPointRenderOption{
+ FillColor: seriesColor,
+ Font: opt.Font,
+ Points: points,
+ Series: &series,
+ })
}
return result.d, nil
diff --git a/mark_point.go b/mark_point.go
new file mode 100644
index 0000000..f1a429f
--- /dev/null
+++ b/mark_point.go
@@ -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 (
+ "github.com/golang/freetype/truetype"
+ "github.com/wcharczuk/go-chart/v2"
+ "github.com/wcharczuk/go-chart/v2/drawing"
+)
+
+type markPointRenderOption struct {
+ FillColor drawing.Color
+ Font *truetype.Font
+ Series *Series
+ Points []Point
+}
+
+func markPointRender(d *Draw, opt markPointRenderOption) {
+ s := opt.Series
+ if len(s.MarkPoint.Data) == 0 {
+ return
+ }
+ points := opt.Points
+ summary := s.Summary()
+ symbolSize := s.MarkPoint.SymbolSize
+ if symbolSize == 0 {
+ symbolSize = 30
+ }
+ r := d.Render
+ // 设置填充样式
+ chart.Style{
+ FillColor: opt.FillColor,
+ }.WriteToRenderer(r)
+ // 设置文本样式
+ chart.Style{
+ FontColor: NewTheme(ThemeDark).GetTextColor(),
+ FontSize: 10,
+ StrokeWidth: 1,
+ Font: opt.Font,
+ }.WriteTextOptionsToRenderer(r)
+ for _, markPointData := range s.MarkPoint.Data {
+ p := points[summary.MinIndex]
+ value := summary.MinValue
+ switch markPointData.Type {
+ case SeriesMarkPointDataTypeMax:
+ p = points[summary.MaxIndex]
+ value = summary.MaxValue
+ }
+
+ d.pin(p.X, p.Y-symbolSize>>1, symbolSize)
+ text := commafWithDigits(value)
+ textBox := r.MeasureText(text)
+ d.text(text, p.X-textBox.Width()>>1, p.Y-symbolSize>>1-2)
+ }
+}
diff --git a/pie_chart.go b/pie_chart.go
index d2cd029..84751f9 100644
--- a/pie_chart.go
+++ b/pie_chart.go
@@ -136,7 +136,7 @@ func pieChartRender(opt ChartOption, result *basicRenderResult) (*Draw, error) {
r.Stroke()
textStyle := chart.Style{
FontColor: theme.GetTextColor(),
- FontSize: 10,
+ FontSize: labelFontSize,
Font: opt.Font,
}
if !series.Label.Color.IsZero() {
diff --git a/range.go b/range.go
index 0d1b459..308dba8 100644
--- a/range.go
+++ b/range.go
@@ -40,9 +40,15 @@ func NewRange(min, max float64, divideCount int) Range {
// 最小单位计算
unit := 2
if r > 10 {
+ unit = 4
+ }
+ if r > 30 {
unit = 5
}
if r > 100 {
+ unit = 10
+ }
+ if r > 200 {
unit = 20
}
unit = int((r/float64(divideCount))/float64(unit))*unit + unit
diff --git a/range_test.go b/range_test.go
index b10002f..06c25fd 100644
--- a/range_test.go
+++ b/range_test.go
@@ -37,12 +37,16 @@ func TestRange(t *testing.T) {
r = NewRange(0, 12, 6)
assert.Equal(0.0, r.Min)
- assert.Equal(30.0, r.Max)
+ assert.Equal(24.0, r.Max)
r = NewRange(-13, 18, 6)
assert.Equal(-20.0, r.Min)
assert.Equal(40.0, r.Max)
+ r = NewRange(0, 150, 6)
+ assert.Equal(0.0, r.Min)
+ assert.Equal(180.0, r.Max)
+
r = NewRange(0, 400, 6)
assert.Equal(0.0, r.Min)
assert.Equal(480.0, r.Max)
diff --git a/series.go b/series.go
index 8b5161d..b37e13d 100644
--- a/series.go
+++ b/series.go
@@ -23,6 +23,7 @@
package charts
import (
+ "math"
"strings"
"github.com/dustin/go-humanize"
@@ -86,6 +87,40 @@ type Series struct {
MarkPoint SeriesMarkPoint
}
+type seriesSummary struct {
+ MaxIndex int
+ MaxValue float64
+ MinIndex int
+ MinValue float64
+ AverageValue float64
+}
+
+func (s *Series) Summary() seriesSummary {
+ minIndex := -1
+ maxIndex := -1
+ minValue := math.MaxFloat64
+ maxValue := -math.MaxFloat64
+ sum := float64(0)
+ for j, item := range s.Data {
+ if item.Value < minValue {
+ minIndex = j
+ minValue = item.Value
+ }
+ if item.Value > maxValue {
+ maxIndex = j
+ maxValue = item.Value
+ }
+ sum += item.Value
+ }
+ return seriesSummary{
+ MaxIndex: maxIndex,
+ MaxValue: maxValue,
+ MinIndex: minIndex,
+ MinValue: minValue,
+ AverageValue: sum / float64(len(s.Data)),
+ }
+}
+
type LabelFormatter func(index int, value float64, percent float64) string
func NewPieLabelFormatter(seriesNames []string, layout string) LabelFormatter {
diff --git a/util.go b/util.go
index 5c1415d..3064668 100644
--- a/util.go
+++ b/util.go
@@ -106,7 +106,7 @@ func isFalse(flag *bool) bool {
return false
}
-func toFloatPoint(f float64) *float64 {
+func NewFloatPoint(f float64) *float64 {
v := f
return &v
}
@@ -114,11 +114,11 @@ func commafWithDigits(value float64) string {
decimals := 2
m := float64(1000 * 1000)
if value >= m {
- return humanize.CommafWithDigits(value/m, decimals) + " M"
+ return humanize.CommafWithDigits(value/m, decimals) + "M"
}
k := float64(1000)
if value >= k {
- return humanize.CommafWithDigits(value/k, decimals) + " K"
+ return humanize.CommafWithDigits(value/k, decimals) + "k"
}
return humanize.CommafWithDigits(value, decimals)
}
diff --git a/xaxis.go b/xaxis.go
index 60c7c23..5c21c14 100644
--- a/xaxis.go
+++ b/xaxis.go
@@ -22,7 +22,9 @@
package charts
-import "github.com/wcharczuk/go-chart/v2"
+import (
+ "github.com/wcharczuk/go-chart/v2"
+)
type XAxisOption struct {
// The boundary gap on both sides of a coordinate axis.
@@ -39,16 +41,19 @@ type XAxisOption struct {
}
// drawXAxis draws x axis, and returns the height, range of if.
-func drawXAxis(p *Draw, opt *XAxisOption) (int, *Range, error) {
+func drawXAxis(p *Draw, opt *XAxisOption, yAxisCount int) (int, *Range, error) {
if opt.Hidden {
return 0, nil, nil
}
+ left := YAxisWidth
+ right := (yAxisCount - 1) * YAxisWidth
dXAxis, err := NewDraw(
DrawOption{
Parent: p,
},
PaddingOption(chart.Box{
- Left: YAxisWidth,
+ Left: left,
+ Right: right,
}),
)
if err != nil {
diff --git a/xaxis_test.go b/xaxis_test.go
index bce3863..44e8a3f 100644
--- a/xaxis_test.go
+++ b/xaxis_test.go
@@ -81,7 +81,7 @@ func TestDrawXAxis(t *testing.T) {
for _, tt := range tests {
d := tt.newDraw()
- height, _, err := drawXAxis(d, tt.newOption())
+ height, _, err := drawXAxis(d, tt.newOption(), 1)
assert.Nil(err)
assert.Equal(25, height)
data, err := d.Bytes()
diff --git a/yaxis.go b/yaxis.go
index 1742e99..b978d08 100644
--- a/yaxis.go
+++ b/yaxis.go
@@ -41,11 +41,11 @@ type YAxisOption struct {
const YAxisWidth = 40
-func drawYAxis(p *Draw, opt *ChartOption, xAxisHeight int, padding chart.Box) (*Range, error) {
+func drawYAxis(p *Draw, opt *ChartOption, axisIndex, xAxisHeight int, padding chart.Box) (*Range, error) {
theme := NewTheme(opt.Theme)
- yRange := opt.getYRange(0)
+ yRange := opt.newYRange(axisIndex)
values := yRange.Values()
- formatter := opt.YAxis.Formatter
+ formatter := opt.YAxisList[axisIndex].Formatter
if len(formatter) != 0 {
for index, text := range values {
values[index] = strings.ReplaceAll(formatter, "{value}", text)
@@ -64,12 +64,21 @@ func drawYAxis(p *Draw, opt *ChartOption, xAxisHeight int, padding chart.Box) (*
}
width := NewAxis(p, data, style).measureAxis()
- padding.Left += (YAxisWidth - width)
+ yAxisCount := len(opt.YAxisList)
+ boxWidth := p.Box.Width()
+ if axisIndex > 0 {
+ style.SplitLineShow = false
+ style.Position = PositionRight
+ padding.Right += (axisIndex - 1) * YAxisWidth
+ } else {
+ boxWidth = p.Box.Width() - (yAxisCount-1)*YAxisWidth
+ padding.Left += (YAxisWidth - width)
+ }
dYAxis, err := NewDraw(
DrawOption{
Parent: p,
- Width: p.Box.Width(),
+ Width: boxWidth,
// 减去x轴的高
Height: p.Box.Height() - xAxisHeight,
},
diff --git a/yaxis_test.go b/yaxis_test.go
index 634ff59..5c66caa 100644
--- a/yaxis_test.go
+++ b/yaxis_test.go
@@ -49,8 +49,10 @@ func TestDrawYAxis(t *testing.T) {
newDraw: newDraw,
newOption: func() *ChartOption {
return &ChartOption{
- YAxis: YAxisOption{
- Max: toFloatPoint(20),
+ YAxisList: []YAxisOption{
+ {
+ Max: NewFloatPoint(20),
+ },
},
SeriesList: []Series{
{
@@ -72,7 +74,7 @@ func TestDrawYAxis(t *testing.T) {
for _, tt := range tests {
d := tt.newDraw()
- r, err := drawYAxis(d, tt.newOption(), tt.xAxisHeight, chart.NewBox(10, 10, 10, 10))
+ r, err := drawYAxis(d, tt.newOption(), 0, tt.xAxisHeight, chart.NewBox(10, 10, 10, 10))
assert.Nil(err)
assert.Equal(&Range{
divideCount: 6,