refactor: add examples

This commit is contained in:
vicanso 2021-12-25 09:29:39 +08:00
parent c289ba7cde
commit 6aad7b6067
10 changed files with 231 additions and 46 deletions

View file

@ -1 +1,47 @@
# go-chart
# go-echarts
![Alt](https://repobeats.axiom.co/api/embed/9071915842d72a909465be75eb6c12ffb7de2dcf.svg "Repobeats analytics image")
[go-chart](https://github.com/wcharczuk/go-chart)是golang常用的可视化图表库支持`svg``png`的输出,`Apache ECharts`在前端开发中得到众多开发者的认可。go-echarts则是结合两者的方式兼容`Apache ECharts`的配置参数,简单快捷的生成相似的图表(`svg``png`)方便插入至Email或分享使用。下面为常用的几种图表截图
![](./assets/go-echarts.jpg)
## 支持图表类型
暂仅支持三种的图表类型:`line`, `bar` 以及 `pie`
## 示例
`go-echarts`兼容了`echarts`的参数配置可简单的使用json形式的配置字符串则可快速生成图表。
```go
package main
import (
"os"
charts "github.com/vicanso/echarts"
)
func main() {
buf, err := charts.RenderEChartsToPNG(`{
"title": {
"text": "Line"
},
"xAxis": {
"type": "category",
"data": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
},
"series": [
{
"data": [150, 230, 224, 218, 135, 147, 260]
}
]
}`)
if err != nil {
panic(err)
}
os.WriteFile("output.png", buf, 0600)
}
```

BIN
assets/go-echarts.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

16
axis.go
View file

@ -50,6 +50,16 @@ type YAxisOption struct {
const axisStrokeWidth = 1
func maxInt(values ...int) int {
result := 0
for _, v := range values {
if v > result {
result = v
}
}
return result
}
// GetXAxisAndValues returns x axis by theme, and the values of axis.
func GetXAxisAndValues(xAxis XAxis, tickPosition chart.TickPosition, theme string) (chart.XAxis, []float64) {
data := xAxis.Data
@ -66,10 +76,8 @@ func GetXAxisAndValues(xAxis XAxis, tickPosition chart.TickPosition, theme strin
xValues := make([]float64, size)
ticks := make([]chart.Tick, 0)
maxTicks := xAxis.SplitNumber
if maxTicks == 0 {
maxTicks = 10
}
// tick width
maxTicks := maxInt(xAxis.SplitNumber, 10)
// 计息最多每个unit至少放多个
minUnitSize := originalSize / maxTicks

View file

@ -188,6 +188,7 @@ type ECharsOptions struct {
Data []ECharsSeriesData `json:"data"`
Type string `json:"type"`
YAxisIndex int `json:"yAxisIndex"`
ItemStyle EChartStyle `json:"itemStyle"`
} `json:"series"`
}
@ -201,7 +202,15 @@ func convertEChartsSeries(e *ECharsOptions) ([]Series, chart.TickPosition) {
if seriesType == SeriesPie {
series := make([]Series, len(e.Series[0].Data))
for index, item := range e.Series[0].Data {
style := chart.Style{}
if item.ItemStyle.Color != "" {
c := parseColor(item.ItemStyle.Color)
style.FillColor = c
style.StrokeColor = c
}
series[index] = Series{
Style: style,
Data: []SeriesData{
{
Value: item.Value,
@ -219,6 +228,12 @@ func convertEChartsSeries(e *ECharsOptions) ([]Series, chart.TickPosition) {
if item.Type == SeriesBar {
tickPosition = chart.TickPositionBetweenTicks
}
style := chart.Style{}
if item.ItemStyle.Color != "" {
c := parseColor(item.ItemStyle.Color)
style.FillColor = c
style.StrokeColor = c
}
data := make([]SeriesData, len(item.Data))
for j, itemData := range item.Data {
sd := SeriesData{
@ -232,6 +247,7 @@ func convertEChartsSeries(e *ECharsOptions) ([]Series, chart.TickPosition) {
data[j] = sd
}
series[index] = Series{
Style: style,
YAxisIndex: item.YAxisIndex,
Data: data,
Type: item.Type,
@ -317,3 +333,23 @@ func ParseECharsOptions(options string) (Options, error) {
return e.ToOptions(), nil
}
func echartsRender(options string, rp chart.RendererProvider) ([]byte, error) {
o, err := ParseECharsOptions(options)
if err != nil {
return nil, err
}
g, err := New(o)
if err != nil {
return nil, err
}
return render(g, rp)
}
func RenderEChartsToPNG(options string) ([]byte, error) {
return echartsRender(options, chart.PNG)
}
func RenderEChartsToSVG(options string) ([]byte, error) {
return echartsRender(options, chart.SVG)
}

View file

@ -28,6 +28,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/wcharczuk/go-chart/v2"
"github.com/wcharczuk/go-chart/v2/drawing"
)
func TestConvertToArray(t *testing.T) {
@ -288,6 +289,9 @@ func TestParseECharsOptions(t *testing.T) {
{
"name": "Precipitation",
"type": "bar",
"itemStyle": {
"color": "#0052d9"
},
"data": [2.6, 5.9, 9, 26.4, 28.7, 70.7, 175.6]
},
{
@ -368,6 +372,20 @@ func TestParseECharsOptions(t *testing.T) {
2.6, 5.9, 9, 26.4, 28.7, 70.7, 175.6,
}),
Type: SeriesBar,
Style: chart.Style{
StrokeColor: drawing.Color{
R: 0,
G: 82,
B: 217,
A: 255,
},
FillColor: drawing.Color{
R: 0,
G: 82,
B: 217,
A: 255,
},
},
},
{
Data: NewSeriesDataListFromFloat([]float64{

28
examples/basic/main.go Normal file
View file

@ -0,0 +1,28 @@
package main
import (
"os"
charts "github.com/vicanso/echarts"
)
func main() {
buf, err := charts.RenderEChartsToPNG(`{
"title": {
"text": "Line"
},
"xAxis": {
"type": "category",
"data": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
},
"series": [
{
"data": [150, 230, 224, 218, 135, 147, 260]
}
]
}`)
if err != nil {
panic(err)
}
os.WriteFile("output.png", buf, 0600)
}

View file

@ -9,32 +9,44 @@ import (
var html = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<link type="text/css" rel="styleSheet" href="https://unpkg.com/normalize.css@8.0.1/normalize.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
h1 {
text-align: center;
}
pre {
width: 800px;
margin: auto auto 20px auto;
max-height: 300px;
overflow: auto;
display: block;
}
svg{
margin: auto auto 50px auto;
display: block;
}
</style>
<title>go-echarts</title>
</head>
<body>
{{body}}
</body>
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<link type="text/css" rel="styleSheet" href="https://unpkg.com/normalize.css@8.0.1/normalize.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
.charts {
width: 830px;
margin: 10px auto;
overflow: hidden;
}
.grid {
float: left;
margin-right: 10px;
}
.grid svg {
margin-bottom: 10px;
}
h1 {
text-align: center;
}
pre {
width: 100%;
margin: auto auto 20px auto;
max-height: 300px;
overflow: auto;
display: block;
}
svg{
margin: auto auto 50px auto;
display: block;
}
</style>
<title>go-echarts</title>
</head>
<body>
<div class="charts">{{body}}</div>
</body>
</html>
`
@ -43,10 +55,9 @@ var chartOptions = []map[string]string{
"title": "折线图",
"option": `{
"title": {
"text": "line",
"text": "Line",
"textAlign": "left",
"textStyle": {
"color": "#333",
"fontSize": 24,
"height": 40
}
@ -214,6 +225,9 @@ var chartOptions = []map[string]string{
{
"name": "Evaporation",
"type": "bar",
"itemStyle": {
"color": "#0052d9"
},
"data": [2, 4.9, 7, 23.2, 25.6, 76.7, 135.6]
},
{
@ -241,7 +255,7 @@ var chartOptions = []map[string]string{
},
"xAxis": {
"type": "category",
"splitNumber": 12,
"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": {
@ -300,19 +314,35 @@ var chartOptions = []map[string]string{
},
}
func render(theme string) ([]byte, error) {
type renderOptions struct {
theme string
width int
height int
onlyCharts bool
}
func render(opts renderOptions) ([]byte, error) {
data := bytes.Buffer{}
for _, m := range chartOptions {
// if m["title"] != "多柱状图" {
// continue
// }
chartHTML := []byte(`<div>
<h1>{{title}}</h1>
<pre>{{option}}</pre>
{{svg}}
</div>`)
if opts.onlyCharts {
chartHTML = []byte(`<div class="grid">
{{svg}}
</div>`)
}
o, err := charts.ParseECharsOptions(m["option"])
o.Theme = theme
if opts.width > 0 {
o.Width = opts.width
}
if opts.height > 0 {
o.Height = opts.height
}
o.Theme = opts.theme
if err != nil {
return nil, err
}
@ -333,8 +363,17 @@ func render(theme string) ([]byte, error) {
}
func indexHandler(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
opts := renderOptions{
theme: query.Get("theme"),
}
if query.Get("view") == "grid" {
opts.width = 400
opts.height = 200
opts.onlyCharts = true
}
buf, err := render(r.URL.Query().Get("theme"))
buf, err := render(opts)
if err != nil {
w.WriteHeader(400)
w.Write([]byte(err.Error()))

6
go.mod
View file

@ -9,9 +9,9 @@ require (
)
require (
github.com/davecgh/go-spew v1.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)

11
go.sum
View file

@ -1,5 +1,6 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
@ -11,10 +12,14 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/wcharczuk/go-chart/v2 v2.1.0 h1:tY2slqVQ6bN+yHSnDYwZebLQFkphK4WNrVwnt7CJZ2I=
github.com/wcharczuk/go-chart/v2 v2.1.0/go.mod h1:yx7MvAVNcP/kN9lKXM/NTce4au4DFN99j6i1OwDclNA=
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 h1:QelT11PB4FXiDEXucrfNckHoFxwt8USGY1ajP1ZF5lM=
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ=
golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -37,6 +37,7 @@ type Series struct {
Data []SeriesData
XValues []float64
YAxisIndex int
Style chart.Style
}
const lineStrokeWidth = 2
@ -75,6 +76,10 @@ func GetSeries(series []Series, tickPosition chart.TickPosition, theme string) [
DotColor: getSeriesColor(theme, index),
DotWidth: dotWith,
}
if !item.Style.StrokeColor.IsZero() {
style.StrokeColor = item.Style.StrokeColor
style.DotColor = item.Style.StrokeColor
}
pointIndexOffset := 0
// 如果居中,需要多增加一个点
if tickPosition == chart.TickPositionBetweenTicks {