Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add animation; #246

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
140 changes: 140 additions & 0 deletions charts/base.go
Expand Up @@ -2,13 +2,20 @@ package charts

import (
"bytes"
"context"
"encoding/json"
"fmt"
"html/template"
"net/http"
"sync"
"sync/atomic"
"time"

"github.com/go-echarts/go-echarts/v2/actions"
"github.com/go-echarts/go-echarts/v2/datasets"
"github.com/go-echarts/go-echarts/v2/opts"
"github.com/go-echarts/go-echarts/v2/render"
"github.com/gorilla/websocket"
)

// GlobalOpts sets the Global options for charts.
Expand All @@ -29,6 +36,7 @@ type BaseConfiguration struct {
opts.RadiusAxis `json:"radiusAxis"`
opts.Brush `json:"brush"`
*opts.AxisPointer `json:"axisPointer"`
opts.Anime

render.Renderer `json:"-"`
opts.Initialization `json:"-"`
Expand Down Expand Up @@ -71,6 +79,9 @@ type BaseConfiguration struct {
hasBrush bool

GridList []opts.Grid `json:"grid,omitempty"`

// UpdaterConfig use to update the chart option . if it's not nil, use RegisterMux to set handle.
*UpdaterConfig `json:"-"`
}

// BaseActions represents a dispatchAction set needed by all chart types.
Expand Down Expand Up @@ -226,6 +237,129 @@ func (bc *BaseConfiguration) setBaseGlobalOptions(opts ...GlobalOpts) {
}
}

type UpdaterConfig struct {
UpdateCh chan Updater `json:"-"`
Handle http.HandlerFunc
timeOut time.Duration
subs map[uint64]chan Updater
rwLk sync.RWMutex
once sync.Once
id uint64
}

func (u *UpdaterConfig) addSub() (uint64, <-chan Updater) {
id := atomic.AddUint64(&u.id, 1)
u.rwLk.Lock()
defer u.rwLk.Unlock()
u.subs[id] = make(chan Updater, 1)
return id, u.subs[id]
}

func (u *UpdaterConfig) cancelSub(id uint64) {
u.rwLk.Lock()
defer u.rwLk.Unlock()
delete(u.subs, id)
}
func (u *UpdaterConfig) pubDate() {

for data := range u.UpdateCh {
u.rwLk.RLock()
for _, ch := range u.subs {
select {
case ch <- data:
default:
}
}
u.rwLk.RUnlock()
}
}

type Updater interface {
JSONNotEscaped() template.HTML
}

func (u *BaseConfiguration) SetOptionUpdater() chan<- Updater {
if u.UpdaterConfig == nil {
u.UpdaterConfig = &UpdaterConfig{}
u.UpdateCh = make(chan Updater)
u.subs = make(map[uint64]chan Updater)
}
u.once.Do(func() {
go u.pubDate()
})

u.Handle = func(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
fmt.Println(err)
return
}
id, ch := u.addSub()
defer u.cancelSub(id)
defer conn.Close()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func() {
for {
select {
case <-ctx.Done():
return
default:
_, _, err := conn.ReadMessage()
if err != nil {
cancel()
return
}
}
}
}()
for {
select {
case chart := <-ch:
if err := conn.WriteMessage(websocket.TextMessage, []byte(chart.JSONNotEscaped())); err != nil {
return
}
case <-ctx.Done():
return
}
}
}
return u.UpdateCh
}

var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
HandshakeTimeout: 5 * time.Second,
CheckOrigin: func(r *http.Request) bool {
return true
},
}

func (bc *BaseConfiguration) RegisterMux(mux ...*http.ServeMux) {
if bc.UpdaterConfig == nil {
return
}
if len(mux) > 0 && mux[0] != nil {
mux[0].HandleFunc(fmt.Sprintf("/ws/%s", bc.GetChartID()), bc.Handle)
return
}
http.HandleFunc(fmt.Sprintf("/ws/%s", bc.GetChartID()), bc.Handle)

}
func (bc *BaseConfiguration) GetUpdaterHandlerFunc() http.HandlerFunc {
if bc.UpdaterConfig == nil {
bc.SetOptionUpdater()
}
return bc.Handle
}
func (bc *BaseConfiguration) GetChartID() string {
if bc.Initialization.ChartID == "" {
bc.Initialization.Validate()
}
return bc.ChartID
}

func (bc *BaseActions) setBaseGlobalActions(opts ...GlobalActions) {
for _, opt := range opts {
opt(bc)
Expand Down Expand Up @@ -403,3 +537,9 @@ func WithAxisPointerOpts(opt *opts.AxisPointer) GlobalOpts {
bc.AxisPointer = opt
}
}

func WithAnimationOpts(opt opts.Anime) GlobalOpts {
return func(bc *BaseConfiguration) {
bc.Anime = opt
}
}
25 changes: 12 additions & 13 deletions charts/series.go
Expand Up @@ -75,18 +75,11 @@ type SingleSeries struct {
RotationRange []float32 `json:"rotationRange,omitempty"`

// Sunburst
NodeClick string `json:"nodeClick,omitempty"`
Sort string `json:"sort,omitempty"`
RenderLabelForZeroData bool `json:"renderLabelForZeroData"`
SelectedMode bool `json:"selectedMode"`
Animation bool `json:"animation"`
AnimationThreshold int `json:"animationThreshold,omitempty"`
AnimationDuration int `json:"animationDuration,omitempty"`
AnimationEasing string `json:"animationEasing,omitempty"`
AnimationDelay int `json:"animationDelay,omitempty"`
AnimationDurationUpdate int `json:"animationDurationUpdate,omitempty"`
AnimationEasingUpdate string `json:"animationEasingUpdate,omitempty"`
AnimationDelayUpdate int `json:"animationDelayUpdate,omitempty"`
NodeClick string `json:"nodeClick,omitempty"`
Sort string `json:"sort,omitempty"`
RenderLabelForZeroData bool `json:"renderLabelForZeroData"`
SelectedMode bool `json:"selectedMode"`
opts.Anime

// series data
Data interface{} `json:"data"`
Expand Down Expand Up @@ -282,7 +275,7 @@ func WithTreeOpts(opt opts.TreeChart) SeriesOpts {
// WithTreeMapOpts sets the TreeMapChart options.
func WithTreeMapOpts(opt opts.TreeMapChart) SeriesOpts {
return func(s *SingleSeries) {
s.Animation = opt.Animation
s.Anime.Animation = opt.Animation
s.LeafDepth = opt.LeafDepth
s.Roam = opt.Roam
s.Levels = opt.Levels
Expand Down Expand Up @@ -494,3 +487,9 @@ func WithEncodeOpts(opt opts.Encode) SeriesOpts {
s.Encode = &opt
}
}

func WithSeriesAnimationOpts(opt opts.Anime)SeriesOpts{
return func(s *SingleSeries) {
s.Anime=opt
}
}
14 changes: 12 additions & 2 deletions components/page.go
@@ -1,6 +1,8 @@
package components

import (
"net/http"

"github.com/go-echarts/go-echarts/v2/opts"
"github.com/go-echarts/go-echarts/v2/render"
)
Expand All @@ -19,6 +21,7 @@ type Charter interface {
GetAssets() opts.Assets
FillDefaultValues()
Validate()
RegisterMux(mux ...*http.ServeMux)
}

// Page represents a page chart.
Expand All @@ -27,15 +30,16 @@ type Page struct {
opts.Initialization
opts.Assets

Charts []interface{}
Charts []Charter
Layout Layout
Mux *http.ServeMux
}

// NewPage creates a new page.
func NewPage() *Page {
page := &Page{}
page.Assets.InitAssets()
page.Renderer = render.NewPageRender(page, page.Validate)
page.Renderer = render.NewPageRender(page, page.Validate,page.ValidateMux)
page.Layout = PageCenterLayout
return page
}
Expand Down Expand Up @@ -68,3 +72,9 @@ func (page *Page) Validate() {
page.Initialization.Validate()
page.Assets.Validate(page.AssetsHost)
}

func (page *Page) ValidateMux() {
for _, v := range page.Charts {
v.RegisterMux(page.Mux)
}
}
1 change: 1 addition & 0 deletions go.mod
Expand Up @@ -5,6 +5,7 @@ go 1.15
require (
github.com/cinar/indicator v1.2.24 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gorilla/websocket v1.5.0
github.com/kr/pretty v0.1.0 // indirect
github.com/stretchr/testify v1.6.0
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Expand Up @@ -5,6 +5,8 @@ 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/go-echarts/go-echarts v1.0.0 h1:n181E4iXwj4zrU9VYmdM2m8dyhERt2w9k9YhHqdp6A8=
github.com/go-echarts/go-echarts v1.0.0/go.mod h1:qbmyAb/Rl1f2w7wKba1D4LoNq4U164yO4/wedFbcWyo=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
Expand Down
31 changes: 22 additions & 9 deletions opts/global.go
Expand Up @@ -383,8 +383,8 @@ type AxisPointerLink struct {
YAxisName string `json:"yAxisName,omitempty"`
}

//Brush is an area-selecting component, with which user can select part of data from a chart to display in detail, or do calculations with them.
//https://echarts.apache.org/en/option.html#brush
// Brush is an area-selecting component, with which user can select part of data from a chart to display in detail, or do calculations with them.
// https://echarts.apache.org/en/option.html#brush
type Brush struct {

//XAxisIndex Assigns which of the xAxisIndex can use brush selecting.
Expand All @@ -397,8 +397,8 @@ type Brush struct {
OutOfBrush *BrushOutOfBrush `json:"outOfBrush,omitempty"`
}

//BrushOutOfBrush
//https://echarts.apache.org/en/option.html#brush.outOfBrush
// BrushOutOfBrush
// https://echarts.apache.org/en/option.html#brush.outOfBrush
type BrushOutOfBrush struct {
ColorAlpha float32 `json:"colorAlpha,omitempty"`
}
Expand Down Expand Up @@ -484,8 +484,8 @@ type ToolBoxFeatureSaveAsImage struct {
Title string `json:"title,omitempty"`
}

//ToolBoxFeatureBrush brush-selecting icon.
//https://echarts.apache.org/en/option.html#toolbox.feature.brush
// ToolBoxFeatureBrush brush-selecting icon.
// https://echarts.apache.org/en/option.html#toolbox.feature.brush
type ToolBoxFeatureBrush struct {

//Icons used, whose values are:
Expand Down Expand Up @@ -1453,10 +1453,23 @@ type Grid struct {
Height string `json:"height,omitempty"`
}

//Dataset brings convenience in data management separated with styles and enables data reuse by different series.
//More importantly, it enables data encoding from data to visual, which brings convenience in some scenarios.
//https://echarts.apache.org/en/option.html#dataset.id
// Dataset brings convenience in data management separated with styles and enables data reuse by different series.
// More importantly, it enables data encoding from data to visual, which brings convenience in some scenarios.
// https://echarts.apache.org/en/option.html#dataset.id
type Dataset struct {
//source
Source interface{} `json:"source"`
}

//Whether to use animation
// https://echarts.apache.org/zh/option.html#animation
type Anime struct {
Animation bool `json:"animation"`
AnimationThreshold int `json:"animationThreshold,omitempty"`
AnimationDuration int `json:"animationDuration,omitempty"`
AnimationEasing string `json:"animationEasing,omitempty"`
AnimationDelay int `json:"animationDelay,omitempty"`
AnimationDurationUpdate int `json:"animationDurationUpdate,omitempty"`
AnimationEasingUpdate string `json:"animationEasingUpdate,omitempty"`
AnimationDelayUpdate int `json:"animationDelayUpdate,omitempty"`
}
24 changes: 24 additions & 0 deletions templates/base.go
Expand Up @@ -18,6 +18,30 @@ var BaseTpl = `
goecharts_{{ .ChartID | safeJS }}.setOption(option_{{ .ChartID | safeJS }});
goecharts_{{ .ChartID | safeJS }}.dispatchAction(action_{{ .ChartID | safeJS }});

{{if .UpdaterConfig}}
function UpdateOption() {
var ip_addr = document.location.host;
var ws = new WebSocket("ws://"+ip_addr+"/ws/{{ .ChartID| safeJS }}");
ws.onopen = function () {
{

console.log("sending...");
};

}
ws.onmessage = function (evt) {
var received_msg = evt.data;
console.log("data received...",received_msg);
var opt=JSON.parse(evt.data)
goecharts_{{ .ChartID | safeJS }}.setOption(opt)
};
ws.onclose = function () {
// close websocket
console.log("connection closed...");
};
}
UpdateOption();
{{end}}
{{- range .JSFunctions.Fns }}
{{ . | safeJS }}
{{- end }}
Expand Down