/
wildsvg.go
124 lines (98 loc) · 2.97 KB
/
wildsvg.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package govfx
import (
"time"
"honnef.co/go/js/dom"
)
// CurvePoint defines a area along a svg path for a specific progress and x value.
type CurvePoint struct {
Length int
X float64
Y float64
Xd float64
Yd float64
SampleProgress float64
}
// SVGPathEaser defines a easing provider using SVG Paths.
type SVGPathEaser struct {
pathlength int
conf SVGConfig
samples []CurvePoint
svgElement dom.Element
start time.Time
end time.Time
delta time.Duration
}
// SVGConfig defines a configuration object for the SVGPathEaser.
// WARNING: Requires to be loaded within the dom.
type SVGConfig struct {
SVGPath string
Width int
Height int
SamplingSize int
}
// NewSVGPathEaser returns a new instance of the SVGPathEaser.
func NewSVGPathEaser(conf SVGConfig) *SVGPathEaser {
var svg SVGPathEaser
svg.conf = conf
svg.svgElement = dom.WrapElement(Document().Underlying().Call("createElementNS", "http://www.w3.org/2000/svg", "path"))
svg.svgElement.SetAttribute("d", conf.SVGPath)
svg.pathlength = svg.svgElement.Underlying().Call("getTotalLength", nil).Int()
svg.generateSampling()
return &svg
}
// Sample for the giving t value between 0..1, we return a CurvePoint that is
// either at that range or within that specified range. Also returns a bool
// whether it was a success or failure.
func (svg *SVGPathEaser) Sample(t float64) (CurvePoint, bool) {
var item CurvePoint
var found bool
for ind, elem := range svg.samples {
nextInd := ind + 1
// Clamp the nextInd to ensure it never overflows.
if nextInd >= svg.conf.SamplingSize {
nextInd = svg.conf.SamplingSize - 1
}
next := svg.samples[nextInd]
if elem.Xd == t || (elem.Xd < t && next.Xd > t) {
item = elem
found = true
break
}
}
return item, found
}
// Ease returns the giving value of t(time) within the giving svg sample.
func (svg *SVGPathEaser) Ease(t float64) float64 {
if item, ok := svg.Sample(t); ok {
return item.Yd
}
return 0
}
// Stats returns the giving time details taken to generate the samples.
func (svg *SVGPathEaser) Stats() (delta time.Duration, start time.Time, end time.Time) {
delta = svg.delta
start = svg.start
end = svg.end
return
}
// generateSampling the samplings based on the sampling size from the
// svg data.
func (svg *SVGPathEaser) generateSampling() {
samplePct := float64(1 / svg.conf.SamplingSize)
svg.start = time.Now()
for i := 0; i < svg.conf.SamplingSize; i++ {
step := float64(i) * samplePct
fsm := step * float64(svg.pathlength)
var point CurvePoint
point.SampleProgress = step
point.Length = int(fsm)
fsmObj := svg.svgElement.Underlying().Call("getPointAtLength", fsm)
point.X = fsmObj.Get("x").Float()
point.Xd = point.X / float64(svg.conf.Width)
point.Y = fsmObj.Get("y").Float()
point.Yd = 1 - (point.Y / float64(svg.conf.Height))
svg.samples = append(svg.samples, point)
}
svg.end = time.Now()
svg.delta = svg.end.Sub(svg.start)
}