diff --git a/README.md b/README.md
index 382a2f2..c387af2 100644
--- a/README.md
+++ b/README.md
@@ -57,6 +57,7 @@ func main() {
- `title` 图表标题,包括标题内容、高度、颜色等
- `title.text` 标题内容
- `title.left` 标题与容器左侧的距离,可设置为`left`, `right`, `center`, `20%` 以及 `20` 这样的具体数值
+ - `title.top` 标题与容器顶部的距离,暂仅支持具体数值,如`20`
- `title.textStyle.color` 标题文字颜色
- `title.textStyle.fontSize` 标题文字字体大小
- `title.textStyle.height` 标题高度
@@ -87,7 +88,7 @@ func main() {
-简单的图表生成PNG在20ms左右,而SVG的性能则更快,性能上比起使用`chrome headless`加载`echarts`图表展示页面,截图生成的方式大幅度提升,基本能满足简单的图表生成需求。
+简单的图表生成PNG在20ms左右,而SVG的性能则更快,性能上比起使用`chrome headless`加载`echarts`图表展示页面再截图生成的方式大幅度提升,满足简单的图表生成需求。
```bash
goos: darwin
diff --git a/charts.go b/charts.go
index 595f569..17b5975 100644
--- a/charts.go
+++ b/charts.go
@@ -49,6 +49,7 @@ type (
Style chart.Style
Font *truetype.Font
Left string
+ Top string
}
Legend struct {
Data []string
@@ -194,10 +195,11 @@ func newPieChart(opt Options) *chart.PieChart {
if opt.Title.Left == "" {
opt.Title.Left = "center"
}
- themeColorPalette := &ThemeColorPalette{
- Theme: opt.Theme,
+ titleColor := drawing.ColorBlack
+ if opt.Theme == ThemeDark {
+ titleColor = drawing.ColorWhite
}
- titleRender := newTitleRenderable(opt.Title, p.GetFont(), themeColorPalette.TextColor())
+ titleRender := newTitleRenderable(opt.Title, p.GetFont(), titleColor)
if titleRender != nil {
p.Elements = []chart.Renderable{
titleRender,
diff --git a/echarts.go b/echarts.go
index cc4ad2f..bf88206 100644
--- a/echarts.go
+++ b/echarts.go
@@ -182,6 +182,7 @@ type ECharsOptions struct {
Title struct {
Text string `json:"text"`
Left Position `json:"left"`
+ Top Position `json:"top"`
TextStyle struct {
Color string `json:"color"`
FontFamily string `json:"fontFamily"`
@@ -282,6 +283,7 @@ func (e *ECharsOptions) ToOptions() Options {
o.Title = Title{
Text: e.Title.Text,
Left: string(e.Title.Left),
+ Top: string(e.Title.Top),
Style: chart.Style{
FontColor: parseColor(titleTextStyle.Color),
FontSize: titleTextStyle.FontSize,
diff --git a/examples/demo/main.go b/examples/demo/main.go
new file mode 100644
index 0000000..9c77078
--- /dev/null
+++ b/examples/demo/main.go
@@ -0,0 +1,365 @@
+package main
+
+import (
+ "bytes"
+ "net/http"
+
+ charts "github.com/vicanso/go-charts"
+)
+
+var html = `
+
+
+
+
+
+
+
+ go-charts
+
+
+ {{body}}
+
+
+`
+
+var chartOptions = []map[string]string{
+ {
+ "option": `{
+ "title": {
+ "text": "Line",
+ "left": "center"
+ },
+ "yAxis": {
+ "min": 0,
+ "max": 300
+ },
+ "xAxis": {
+ "type": "category",
+ "data": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
+ },
+ "series": [
+ {
+ "data": [150, 230, 224, 218, 135, 147, 260],
+ "type": "line"
+ }
+ ]
+}`,
+ },
+ {
+ "option": `{
+ "legend": {
+ "align": "left",
+ "left": 0,
+ "data": ["Email", "Union Ads", "Video Ads", "Direct", "Search Engine"]
+ },
+ "xAxis": {
+ "type": "category",
+ "boundaryGap": false,
+ "data": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
+ },
+ "series": [
+ {
+ "type": "line",
+ "data": [120, 132, 101, 134, 90, 230, 210]
+ },
+ {
+ "data": [220, 182, 191, 234, 290, 330, 310]
+ },
+ {
+ "data": [150, 232, 201, 154, 190, 330, 410]
+ },
+ {
+ "data": [320, 332, 301, 334, 390, 330, 320]
+ },
+ {
+ "data": [820, 932, 901, 934, 1290, 1330, 1320]
+ }
+ ]
+}`,
+ },
+ {
+ "title": "柱状图(自定义颜色)",
+ "option": `{
+ "xAxis": {
+ "type": "category",
+ "data": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
+ },
+ "series": [
+ {
+ "data": [
+ 120,
+ {
+ "value": 200,
+ "itemStyle": {
+ "color": "#a90000"
+ }
+ },
+ 150,
+ 80,
+ 70,
+ 110,
+ 130
+ ],
+ "type": "bar"
+ }
+ ]
+}`,
+ },
+ {
+ "title": "多柱状图",
+ "option": `{
+ "title": {
+ "text": "Rainfall vs Evaporation",
+ "top": 10
+ },
+ "legend": {
+ "data": ["Rainfall", "Evaporation"]
+ },
+ "xAxis": {
+ "type": "category",
+ "splitNumber": 12,
+ "data": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
+ },
+ "series": [
+ {
+ "name": "Rainfall",
+ "type": "bar",
+ "data": [2, 4.9, 7, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20, 6.4, 3.3]
+ },
+ {
+ "name": "Evaporation",
+ "type": "bar",
+ "data": [2.6, 5.9, 9, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6, 2.3]
+ }
+ ]
+}`,
+ },
+ {
+ "title": "折柱混合",
+ "option": `{
+ "legend": {
+ "data": [
+ "Evaporation",
+ "Precipitation",
+ "Temperature"
+ ]
+ },
+ "xAxis": {
+ "type": "category",
+ "data": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
+ },
+ "yAxis": [
+ {
+ "type": "value",
+ "name": "Precipitation",
+ "min": 0,
+ "max": 250,
+ "interval": 50,
+ "axisLabel": {
+ "formatter": "{value} ml"
+ }
+ },
+ {
+ "type": "value",
+ "name": "Temperature",
+ "min": 0,
+ "max": 25,
+ "interval": 5,
+ "axisLabel": {
+ "formatter": "{value} °C"
+ }
+ }
+ ],
+ "series": [
+ {
+ "name": "Evaporation",
+ "type": "bar",
+ "itemStyle": {
+ "color": "#0052d9"
+ },
+ "data": [2, 4.9, 7, 23.2, 25.6, 76.7, 135.6]
+ },
+ {
+ "name": "Precipitation",
+ "type": "bar",
+ "data": [2.6, 5.9, 9, 26.4, 28.7, 70.7, 175.6]
+ },
+ {
+ "name": "Temperature",
+ "type": "line",
+ "yAxisIndex": 1,
+ "data": [2, 2.2, 3.3, 4.5, 6.3, 10.2, 20.3]
+ }
+ ]
+}`,
+ },
+ {
+ "title": "降雨量",
+ "option": `{
+ "title": {
+ "text": "Rainfall",
+ "left": "right"
+ },
+ "legend": {
+ "data": ["GZ", "SH"]
+ },
+ "xAxis": {
+ "type": "category",
+ "splitNumber": 6,
+ "data": ["01-01","01-02","01-03","01-04","01-05","01-06","01-07","01-08","01-09","01-10","01-11","01-12","01-13","01-14","01-15","01-16","01-17","01-18","01-19","01-20","01-21","01-22","01-23","01-24","01-25","01-26","01-27","01-28","01-29","01-30","01-31"]
+ },
+ "yAxis": {
+ "axisLabel": {
+ "formatter": "{value} mm"
+ }
+ },
+ "series": [
+ {
+ "type": "bar",
+ "data": [928,821,889,600,547,783,197,853,430,346,63,465,309,334,141,538,792,58,922,807,298,243,744,885,812,231,330,220,984,221,429]
+ },
+ {
+ "type": "bar",
+ "data": [749,201,296,579,255,159,902,246,149,158,507,776,186,79,390,222,601,367,221,411,714,620,966,73,203,631,833,610,487,677,596]
+ }
+ ]
+}`,
+ },
+ {
+ "title": "饼图",
+ "option": `{
+ "title": {
+ "text": "Referer of a Website"
+ },
+ "series": [
+ {
+ "name": "Access From",
+ "type": "pie",
+ "radius": "50%",
+ "data": [
+ {
+ "value": 1048,
+ "name": "Search Engine"
+ },
+ {
+ "value": 735,
+ "name": "Direct"
+ },
+ {
+ "value": 580,
+ "name": "Email"
+ },
+ {
+ "value": 484,
+ "name": "Union Ads"
+ },
+ {
+ "value": 300,
+ "name": "Video Ads"
+ }
+ ]
+ }
+ ]
+}`,
+ },
+}
+
+type renderOptions struct {
+ theme string
+ width int
+ height int
+ onlyCharts bool
+}
+
+func render(opts renderOptions) ([]byte, error) {
+ data := bytes.Buffer{}
+ for _, m := range chartOptions {
+ chartHTML := []byte(`
+ {{svg}}
+
`)
+ o, err := charts.ParseECharsOptions(m["option"])
+ if err != nil {
+ return nil, err
+ }
+ if opts.width > 0 {
+ o.Width = opts.width
+ }
+ if opts.height > 0 {
+ o.Height = opts.height
+ }
+
+ for _, theme := range []string{
+ charts.ThemeDark,
+ charts.ThemeLight,
+ } {
+ o.Theme = theme
+ g, err := charts.New(o)
+ if err != nil {
+ return nil, err
+ }
+ buf, err := charts.ToSVG(g)
+ if err != nil {
+ return nil, err
+ }
+ buf = bytes.ReplaceAll(chartHTML, []byte("{{svg}}"), buf)
+ data.Write(buf)
+ }
+ }
+ return data.Bytes(), nil
+}
+
+func indexHandler(w http.ResponseWriter, r *http.Request) {
+ if r.URL.Path != "/" {
+ return
+ }
+ query := r.URL.Query()
+ opts := renderOptions{
+ theme: query.Get("theme"),
+ width: 400,
+ height: 200,
+ onlyCharts: true,
+ }
+ buf, err := render(opts)
+ if err != nil {
+ w.WriteHeader(400)
+ w.Write([]byte(err.Error()))
+ return
+ }
+
+ data := bytes.ReplaceAll([]byte(html), []byte("{{body}}"), buf)
+ w.Header().Set("Content-Type", "text/html")
+ w.Write(data)
+}
+
+func main() {
+ http.HandleFunc("/", indexHandler)
+ http.ListenAndServe(":3012", nil)
+}
diff --git a/title.go b/title.go
index 3d75b5d..228b2c0 100644
--- a/title.go
+++ b/title.go
@@ -88,6 +88,11 @@ func NewTitleCustomize(title Title) chart.Renderable {
}
titleY := cb.Top + title.Style.Padding.GetTop(chart.DefaultTitleTop) + (textHeight >> 1)
+ // TOP 暂只支持数值
+ if title.Top != "" {
+ value, _ := strconv.Atoi(title.Top)
+ titleY += value
+ }
for _, item := range measureOptions {
x := titleX + (textWidth-item.width)>>1