feat: support customize title
This commit is contained in:
parent
2772798122
commit
06c326bdc3
9 changed files with 170 additions and 44 deletions
|
|
@ -56,9 +56,11 @@ func main() {
|
|||
- `padding:[5, 10, 5, 10]` 分别设置`上右下左`边距
|
||||
- `title` 图表标题,包括标题内容、高度、颜色等
|
||||
- `title.text` 标题内容
|
||||
- `title.left` 标题与容器左侧的距离,可设置为`left`, `right`, `center`, `20%` 以及 `20` 这样的具体数值
|
||||
- `title.textStyle.color` 标题文字颜色
|
||||
- `title.textStyle.fontSize` 标题文字字体大小
|
||||
- `title.textStyle.height` 标题高度
|
||||
- `title.textStyle.fontFamily` 标题文字的字体系列,需要注意此配置是会影响整个图表的字体
|
||||
- `xAxis` 直角坐标系grid中的x轴,由于go-charts仅支持单一个x轴,因此若参数为数组多个x轴,只使用第一个配置
|
||||
- `xAxis.boundaryGap` 坐标轴两边留白策略,仅支持三种设置方式`null`, `true`或者`false`。`null`或`true`时则数据点展示在两个刻度中间
|
||||
- `xAxis.splitNumber` 坐标轴的分割段数,需要注意的是这个分割段数只是个预估值,最后实际显示的段数会在这个基础上根据分割后坐标轴刻度显示的易读程度作调整
|
||||
|
|
|
|||
|
|
@ -168,5 +168,5 @@ func TestGetYAxis(t *testing.T) {
|
|||
yAxis = GetSecondaryYAxis(ThemeDark, nil)
|
||||
assert.False(yAxis.GridMajorStyle.Hidden)
|
||||
assert.False(yAxis.GridMajorStyle.Hidden)
|
||||
assert.True(yAxis.Style.StrokeColor.IsZero())
|
||||
assert.True(yAxis.Style.StrokeColor.IsTransparent())
|
||||
}
|
||||
|
|
|
|||
45
charts.go
45
charts.go
|
|
@ -30,6 +30,7 @@ import (
|
|||
|
||||
"github.com/golang/freetype/truetype"
|
||||
"github.com/wcharczuk/go-chart/v2"
|
||||
"github.com/wcharczuk/go-chart/v2/drawing"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -46,6 +47,8 @@ type (
|
|||
Title struct {
|
||||
Text string
|
||||
Style chart.Style
|
||||
Font *truetype.Font
|
||||
Left string
|
||||
}
|
||||
Legend struct {
|
||||
Data []string
|
||||
|
|
@ -156,6 +159,17 @@ func ToSVG(g Graph) ([]byte, error) {
|
|||
return render(g, chart.SVG)
|
||||
}
|
||||
|
||||
func newTitleRenderable(title Title, font *truetype.Font, textColor drawing.Color) chart.Renderable {
|
||||
if title.Text == "" || title.Style.Hidden {
|
||||
return nil
|
||||
}
|
||||
title.Font = font
|
||||
if title.Style.FontColor.IsZero() {
|
||||
title.Style.FontColor = textColor
|
||||
}
|
||||
return NewTitleCustomize(title)
|
||||
}
|
||||
|
||||
func newPieChart(opt Options) *chart.PieChart {
|
||||
values := make(chart.Values, len(opt.Series))
|
||||
for index, item := range opt.Series {
|
||||
|
|
@ -164,11 +178,9 @@ func newPieChart(opt Options) *chart.PieChart {
|
|||
Label: item.Name,
|
||||
}
|
||||
}
|
||||
return &chart.PieChart{
|
||||
p := &chart.PieChart{
|
||||
Font: opt.Font,
|
||||
Background: opt.getBackground(),
|
||||
Title: opt.Title.Text,
|
||||
TitleStyle: opt.Title.Style,
|
||||
Width: opt.getWidth(),
|
||||
Height: opt.getHeight(),
|
||||
Values: values,
|
||||
|
|
@ -178,6 +190,17 @@ func newPieChart(opt Options) *chart.PieChart {
|
|||
},
|
||||
},
|
||||
}
|
||||
// pie 图表默认设置为居中
|
||||
if opt.Title.Left == "" {
|
||||
opt.Title.Left = "center"
|
||||
}
|
||||
titleRender := newTitleRenderable(opt.Title, p.GetFont(), p.GetColorPalette().TextColor())
|
||||
if titleRender != nil {
|
||||
p.Elements = []chart.Renderable{
|
||||
titleRender,
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func newChart(opt Options) *chart.Chart {
|
||||
|
|
@ -214,8 +237,6 @@ func newChart(opt Options) *chart.Chart {
|
|||
ColorPalette: &ThemeColorPalette{
|
||||
Theme: opt.Theme,
|
||||
},
|
||||
Title: opt.Title.Text,
|
||||
TitleStyle: opt.Title.Style,
|
||||
Width: opt.getWidth(),
|
||||
Height: opt.getHeight(),
|
||||
XAxis: xAxis,
|
||||
|
|
@ -224,10 +245,10 @@ func newChart(opt Options) *chart.Chart {
|
|||
Series: GetSeries(opt.Series, tickPosition, opt.Theme),
|
||||
}
|
||||
|
||||
// 设置secondary的样式
|
||||
elements := make([]chart.Renderable, 0)
|
||||
|
||||
if legendSize != 0 {
|
||||
c.Elements = []chart.Renderable{
|
||||
LegendCustomize(c.Series, LegendOption{
|
||||
elements = append(elements, NewLegendCustomize(c.Series, LegendOption{
|
||||
Theme: opt.Theme,
|
||||
IconDraw: DefaultLegendIconDraw,
|
||||
Align: opt.Legend.Align,
|
||||
|
|
@ -236,8 +257,14 @@ func newChart(opt Options) *chart.Chart {
|
|||
Right: opt.Legend.Right,
|
||||
Top: opt.Legend.Top,
|
||||
Bottom: opt.Legend.Bottom,
|
||||
}),
|
||||
}))
|
||||
}
|
||||
titleRender := newTitleRenderable(opt.Title, c.GetFont(), c.GetColorPalette().TextColor())
|
||||
if titleRender != nil {
|
||||
elements = append(elements, titleRender)
|
||||
}
|
||||
if len(elements) != 0 {
|
||||
c.Elements = elements
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
|
|
|||
12
echarts.go
12
echarts.go
|
|
@ -82,9 +82,9 @@ type EChartsPadding struct {
|
|||
box chart.Box
|
||||
}
|
||||
|
||||
type LegendPostion string
|
||||
type Position string
|
||||
|
||||
func (lp *LegendPostion) UnmarshalJSON(data []byte) error {
|
||||
func (lp *Position) UnmarshalJSON(data []byte) error {
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -181,8 +181,7 @@ type ECharsOptions struct {
|
|||
Padding EChartsPadding `json:"padding"`
|
||||
Title struct {
|
||||
Text string `json:"text"`
|
||||
// 暂不支持(go-chart默认title只能居中)
|
||||
TextAlign string `json:"textAlign"`
|
||||
Left Position `json:"left"`
|
||||
TextStyle struct {
|
||||
Color string `json:"color"`
|
||||
FontFamily string `json:"fontFamily"`
|
||||
|
|
@ -196,8 +195,8 @@ type ECharsOptions struct {
|
|||
Data []string `json:"data"`
|
||||
Align string `json:"align"`
|
||||
Padding EChartsPadding `json:"padding"`
|
||||
Left LegendPostion `json:"left"`
|
||||
Right LegendPostion `json:"right"`
|
||||
Left Position `json:"left"`
|
||||
Right Position `json:"right"`
|
||||
// Top string `json:"top"`
|
||||
// Bottom string `json:"bottom"`
|
||||
} `json:"legend"`
|
||||
|
|
@ -282,6 +281,7 @@ func (e *ECharsOptions) ToOptions() Options {
|
|||
titleTextStyle := e.Title.TextStyle
|
||||
o.Title = Title{
|
||||
Text: e.Title.Text,
|
||||
Left: string(e.Title.Left),
|
||||
Style: chart.Style{
|
||||
FontColor: parseColor(titleTextStyle.Color),
|
||||
FontSize: titleTextStyle.FontSize,
|
||||
|
|
|
|||
|
|
@ -55,8 +55,8 @@ var chartOptions = []map[string]string{
|
|||
"title": "折线图",
|
||||
"option": `{
|
||||
"title": {
|
||||
"text": "Line",
|
||||
"textAlign": "left",
|
||||
"text": "Line\nHello World",
|
||||
"left": "right",
|
||||
"textStyle": {
|
||||
"fontSize": 24,
|
||||
"height": 40
|
||||
|
|
@ -363,6 +363,9 @@ func render(opts renderOptions) ([]byte, error) {
|
|||
}
|
||||
|
||||
func indexHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.RequestURI != "/" {
|
||||
return
|
||||
}
|
||||
query := r.URL.Query()
|
||||
opts := renderOptions{
|
||||
theme: query.Get("theme"),
|
||||
|
|
|
|||
20
legend.go
20
legend.go
|
|
@ -83,8 +83,8 @@ func covertPercent(value string) float64 {
|
|||
return float64(v) / 100
|
||||
}
|
||||
|
||||
func getLegendLeft(width, legendBoxWidth int, opt LegendOption) int {
|
||||
left := (width - legendBoxWidth) / 2
|
||||
func getLegendLeft(canvasWidth, legendBoxWidth int, opt LegendOption) int {
|
||||
left := (canvasWidth - legendBoxWidth) / 2
|
||||
leftValue := opt.Left
|
||||
if leftValue == "auto" || leftValue == "center" {
|
||||
leftValue = ""
|
||||
|
|
@ -106,7 +106,7 @@ func getLegendLeft(width, legendBoxWidth int, opt LegendOption) int {
|
|||
if leftValue != "" {
|
||||
percent := covertPercent(leftValue)
|
||||
if percent >= 0 {
|
||||
return int(float64(width) * percent)
|
||||
return int(float64(canvasWidth) * percent)
|
||||
}
|
||||
v, _ := strconv.Atoi(leftValue)
|
||||
return v
|
||||
|
|
@ -114,10 +114,10 @@ func getLegendLeft(width, legendBoxWidth int, opt LegendOption) int {
|
|||
if rightValue != "" {
|
||||
percent := covertPercent(rightValue)
|
||||
if percent >= 0 {
|
||||
return width - legendBoxWidth - int(float64(width)*percent)
|
||||
return canvasWidth - legendBoxWidth - int(float64(canvasWidth)*percent)
|
||||
}
|
||||
v, _ := strconv.Atoi(rightValue)
|
||||
return width - legendBoxWidth - v
|
||||
return canvasWidth - legendBoxWidth - v
|
||||
}
|
||||
return left
|
||||
}
|
||||
|
|
@ -127,7 +127,7 @@ func getLegendTop(height, legendBoxHeight int, opt LegendOption) int {
|
|||
return 0
|
||||
}
|
||||
|
||||
func LegendCustomize(series []chart.Series, opt LegendOption) chart.Renderable {
|
||||
func NewLegendCustomize(series []chart.Series, opt LegendOption) chart.Renderable {
|
||||
return func(r chart.Renderer, cb chart.Box, chartDefaults chart.Style) {
|
||||
legendDefaults := chart.Style{
|
||||
FontColor: getTextColor(opt.Theme),
|
||||
|
|
@ -154,7 +154,6 @@ func LegendCustomize(series []chart.Series, opt LegendOption) chart.Renderable {
|
|||
}
|
||||
|
||||
var textHeight int
|
||||
var textWidth int
|
||||
var textBox chart.Box
|
||||
labelWidth := 0
|
||||
// 计算文本宽度与高度(取最大值)
|
||||
|
|
@ -163,7 +162,6 @@ func LegendCustomize(series []chart.Series, opt LegendOption) chart.Renderable {
|
|||
textBox = r.MeasureText(labels[x])
|
||||
labelWidth += textBox.Width()
|
||||
textHeight = chart.MaxInt(textBox.Height(), textHeight)
|
||||
textWidth = chart.MaxInt(textBox.Width(), textWidth)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -175,15 +173,15 @@ func LegendCustomize(series []chart.Series, opt LegendOption) chart.Renderable {
|
|||
lineTextGap := 5
|
||||
|
||||
iconAllWidth := iconWidth * len(labels)
|
||||
spaceAllWidth := chart.DefaultMinimumTickHorizontalSpacing * (len(labels) - 1)
|
||||
spaceAllWidth := (chart.DefaultMinimumTickHorizontalSpacing + lineTextGap) * (len(labels) - 1)
|
||||
|
||||
legendBoxWidth := labelWidth + iconAllWidth + spaceAllWidth
|
||||
|
||||
left := getLegendLeft(cb.Width(), legendBoxWidth, opt)
|
||||
top := getLegendTop(cb.Height(), legendBoxHeight, opt)
|
||||
|
||||
left += opt.Padding.Left
|
||||
top += opt.Padding.Top
|
||||
left += (opt.Padding.Left + cb.Left)
|
||||
top += (opt.Padding.Top + cb.Top)
|
||||
|
||||
legendBox := chart.Box{
|
||||
Left: left,
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import (
|
|||
"github.com/wcharczuk/go-chart/v2"
|
||||
)
|
||||
|
||||
func TestLegendCustomize(t *testing.T) {
|
||||
func TestNewLegendCustomize(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
series := GetSeries([]Series{
|
||||
|
|
@ -48,18 +48,18 @@ func TestLegendCustomize(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
align: LegendAlignLeft,
|
||||
svg: "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"800\" height=\"600\">\\n<path d=\"M 404 100\nL 532 100\nL 532 110\nL 404 110\nL 404 100\" style=\"stroke-width:0;stroke:rgba(51,51,51,1.0);fill:none\"/><path d=\"M 404 107\nL 429 107\" style=\"stroke-width:2;stroke:rgba(84,112,198,1.0);fill:none\"/><circle cx=\"416\" cy=\"107\" r=\"5\" style=\"stroke-width:2;stroke:rgba(84,112,198,1.0);fill:rgba(255,255,255,1.0)\"/><path d=\"\" style=\"stroke-width:2;stroke:rgba(84,112,198,1.0);fill:rgba(255,255,255,1.0)\"/><text x=\"434\" y=\"110\" style=\"stroke-width:0;stroke:none;fill:rgba(51,51,51,1.0);font-size:10.2px;font-family:'Roboto Medium',sans-serif\">chrome</text><path d=\"M 489 107\nL 514 107\" style=\"stroke-width:2;stroke:rgba(145,204,117,1.0);fill:rgba(255,255,255,1.0)\"/><circle cx=\"501\" cy=\"107\" r=\"5\" style=\"stroke-width:2;stroke:rgba(145,204,117,1.0);fill:rgba(255,255,255,1.0)\"/><path d=\"\" style=\"stroke-width:2;stroke:rgba(145,204,117,1.0);fill:rgba(255,255,255,1.0)\"/><text x=\"519\" y=\"110\" style=\"stroke-width:0;stroke:none;fill:rgba(51,51,51,1.0);font-size:10.2px;font-family:'Roboto Medium',sans-serif\">edge</text></svg>",
|
||||
svg: "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"800\" height=\"600\">\\n<path d=\"M 449 111\nL 582 111\nL 582 121\nL 449 121\nL 449 111\" style=\"stroke-width:0;stroke:rgba(51,51,51,1.0);fill:none\"/><path d=\"M 449 118\nL 474 118\" style=\"stroke-width:2;stroke:rgba(84,112,198,1.0);fill:none\"/><circle cx=\"461\" cy=\"118\" r=\"5\" style=\"stroke-width:2;stroke:rgba(84,112,198,1.0);fill:rgba(255,255,255,1.0)\"/><path d=\"\" style=\"stroke-width:2;stroke:rgba(84,112,198,1.0);fill:rgba(255,255,255,1.0)\"/><text x=\"479\" y=\"121\" style=\"stroke-width:0;stroke:none;fill:rgba(51,51,51,1.0);font-size:10.2px;font-family:'Roboto Medium',sans-serif\">chrome</text><path d=\"M 534 118\nL 559 118\" style=\"stroke-width:2;stroke:rgba(145,204,117,1.0);fill:rgba(255,255,255,1.0)\"/><circle cx=\"546\" cy=\"118\" r=\"5\" style=\"stroke-width:2;stroke:rgba(145,204,117,1.0);fill:rgba(255,255,255,1.0)\"/><path d=\"\" style=\"stroke-width:2;stroke:rgba(145,204,117,1.0);fill:rgba(255,255,255,1.0)\"/><text x=\"564\" y=\"121\" style=\"stroke-width:0;stroke:none;fill:rgba(51,51,51,1.0);font-size:10.2px;font-family:'Roboto Medium',sans-serif\">edge</text></svg>",
|
||||
},
|
||||
{
|
||||
align: LegendAlignRight,
|
||||
svg: "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"800\" height=\"600\">\\n<path d=\"M 404 100\nL 532 100\nL 532 110\nL 404 110\nL 404 100\" style=\"stroke-width:0;stroke:rgba(51,51,51,1.0);fill:none\"/><text x=\"404\" y=\"110\" style=\"stroke-width:0;stroke:none;fill:rgba(51,51,51,1.0);font-size:10.2px;font-family:'Roboto Medium',sans-serif\">chrome</text><path d=\"M 444 107\nL 469 107\" style=\"stroke-width:2;stroke:rgba(84,112,198,1.0);fill:none\"/><circle cx=\"456\" cy=\"107\" r=\"5\" style=\"stroke-width:2;stroke:rgba(84,112,198,1.0);fill:rgba(255,255,255,1.0)\"/><path d=\"\" style=\"stroke-width:2;stroke:rgba(84,112,198,1.0);fill:rgba(255,255,255,1.0)\"/><text x=\"494\" y=\"110\" style=\"stroke-width:0;stroke:none;fill:rgba(51,51,51,1.0);font-size:10.2px;font-family:'Roboto Medium',sans-serif\">edge</text><path d=\"M 522 107\nL 547 107\" style=\"stroke-width:2;stroke:rgba(145,204,117,1.0);fill:rgba(255,255,255,1.0)\"/><circle cx=\"534\" cy=\"107\" r=\"5\" style=\"stroke-width:2;stroke:rgba(145,204,117,1.0);fill:rgba(255,255,255,1.0)\"/><path d=\"\" style=\"stroke-width:2;stroke:rgba(145,204,117,1.0);fill:rgba(255,255,255,1.0)\"/></svg>",
|
||||
svg: "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"800\" height=\"600\">\\n<path d=\"M 449 111\nL 582 111\nL 582 121\nL 449 121\nL 449 111\" style=\"stroke-width:0;stroke:rgba(51,51,51,1.0);fill:none\"/><text x=\"449\" y=\"121\" style=\"stroke-width:0;stroke:none;fill:rgba(51,51,51,1.0);font-size:10.2px;font-family:'Roboto Medium',sans-serif\">chrome</text><path d=\"M 489 118\nL 514 118\" style=\"stroke-width:2;stroke:rgba(84,112,198,1.0);fill:none\"/><circle cx=\"501\" cy=\"118\" r=\"5\" style=\"stroke-width:2;stroke:rgba(84,112,198,1.0);fill:rgba(255,255,255,1.0)\"/><path d=\"\" style=\"stroke-width:2;stroke:rgba(84,112,198,1.0);fill:rgba(255,255,255,1.0)\"/><text x=\"539\" y=\"121\" style=\"stroke-width:0;stroke:none;fill:rgba(51,51,51,1.0);font-size:10.2px;font-family:'Roboto Medium',sans-serif\">edge</text><path d=\"M 567 118\nL 592 118\" style=\"stroke-width:2;stroke:rgba(145,204,117,1.0);fill:rgba(255,255,255,1.0)\"/><circle cx=\"579\" cy=\"118\" r=\"5\" style=\"stroke-width:2;stroke:rgba(145,204,117,1.0);fill:rgba(255,255,255,1.0)\"/><path d=\"\" style=\"stroke-width:2;stroke:rgba(145,204,117,1.0);fill:rgba(255,255,255,1.0)\"/></svg>",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
r, err := chart.SVG(800, 600)
|
||||
assert.Nil(err)
|
||||
fn := LegendCustomize(series, LegendOption{
|
||||
fn := NewLegendCustomize(series, LegendOption{
|
||||
Align: tt.align,
|
||||
IconDraw: DefaultLegendIconDraw,
|
||||
Padding: chart.Box{
|
||||
|
|
|
|||
2
theme.go
2
theme.go
|
|
@ -31,7 +31,7 @@ import (
|
|||
"github.com/wcharczuk/go-chart/v2/drawing"
|
||||
)
|
||||
|
||||
var hiddenColor = drawing.Color{R: 0, G: 0, B: 0, A: 0}
|
||||
var hiddenColor = drawing.Color{R: 255, G: 255, B: 255, A: 0}
|
||||
|
||||
var AxisColorLight = drawing.Color{
|
||||
R: 110,
|
||||
|
|
|
|||
96
title.go
Normal file
96
title.go
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
// 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 (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/wcharczuk/go-chart/v2"
|
||||
)
|
||||
|
||||
type titleMeasureOption struct {
|
||||
width int
|
||||
height int
|
||||
text string
|
||||
}
|
||||
|
||||
func NewTitleCustomize(title Title) chart.Renderable {
|
||||
return func(r chart.Renderer, cb chart.Box, chartDefaults chart.Style) {
|
||||
if len(title.Text) == 0 || title.Style.Hidden {
|
||||
return
|
||||
}
|
||||
if title.Font != nil {
|
||||
r.SetFont(title.Font)
|
||||
}
|
||||
r.SetFontColor(title.Style.FontColor)
|
||||
titleFontSize := title.Style.GetFontSize(chart.DefaultTitleFontSize)
|
||||
r.SetFontSize(titleFontSize)
|
||||
|
||||
arr := strings.Split(title.Text, "\n")
|
||||
textWidth := 0
|
||||
textHeight := 0
|
||||
measureOptions := make([]titleMeasureOption, len(arr))
|
||||
for index, str := range arr {
|
||||
textBox := r.MeasureText(str)
|
||||
|
||||
w := textBox.Width()
|
||||
h := textBox.Height()
|
||||
if w > textWidth {
|
||||
textWidth = w
|
||||
}
|
||||
if h > textHeight {
|
||||
textHeight = h
|
||||
}
|
||||
measureOptions[index] = titleMeasureOption{
|
||||
text: str,
|
||||
width: w,
|
||||
height: h,
|
||||
}
|
||||
}
|
||||
|
||||
titleX := 0
|
||||
switch title.Left {
|
||||
case "right":
|
||||
titleX = cb.Left + cb.Width() - textWidth
|
||||
case "center":
|
||||
titleX = cb.Left + cb.Width()>>1 - (textWidth >> 1)
|
||||
default:
|
||||
if strings.HasSuffix(title.Left, "%") {
|
||||
value, _ := strconv.Atoi(strings.ReplaceAll(title.Left, "%", ""))
|
||||
titleX = cb.Left + cb.Width()*value/100
|
||||
} else {
|
||||
value, _ := strconv.Atoi(title.Left)
|
||||
titleX = cb.Left + value
|
||||
}
|
||||
}
|
||||
|
||||
titleY := cb.Top + title.Style.Padding.GetTop(chart.DefaultTitleTop) + (textHeight >> 1)
|
||||
|
||||
for _, item := range measureOptions {
|
||||
x := titleX + (textWidth-item.width)>>1
|
||||
r.Text(item.text, x, titleY)
|
||||
titleY += textHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue