diff --git a/examples/canvas/main.go b/examples/canvas/main.go index cafa15d..54abf22 100644 --- a/examples/canvas/main.go +++ b/examples/canvas/main.go @@ -8,81 +8,129 @@ import ( ) const ( - width = 1024 - height = 768 - fps = 60 - bgColor = "#111111" + canvasWidth = 1024 + canvasHeight = 768 + fps = 60 + circleRadius = 100 + bgColor = "#111111" + idleTimeout = time.Second * 5 +) + +type State int + +const ( + unstarted State = iota + running + timeout ) func main() { var ( - circ = newCircle() - clickedYet bool + circ Circle + state State + lastClick time.Time ) c := canvas.NewCanvas(&canvas.CanvasConfig{ - Width: width, - Height: height, + Width: canvasWidth, + Height: canvasHeight, FrameRate: fps, Title: "Harmonica", }) c.Draw(func(ctx *canvas.Context) { - ctx.SetHexColor(bgColor) ctx.Clear() + ctx.SetHexColor(bgColor) - if ctx.IsMouseDragged { - if !clickedYet { - // Set starting positions. - circ.x = ctx.Mouse.X - circ.y = ctx.Mouse.Y + switch state { + case unstarted: + if ctx.IsMouseDragged { + const frequency = 0.8 + const damping = 1.0 - circ.Radius = 100 - clickedYet = true + circ = Circle{ + // Setup a new spring. + spring: harmonica.NewSpring(harmonica.TimeDelta(time.Second/fps), frequency, damping), + + // Set target radius. + Radius: circleRadius, + } + + // Set starting position. + circ.SetPos(ctx.Mouse.X, ctx.Mouse.Y) + + lastClick = time.Now() + state = running } - circ.X = ctx.Mouse.X - circ.Y = ctx.Mouse.Y - } - if clickedYet { - circ.draw(ctx) + case running: + if ctx.IsMouseDragged { + lastClick = time.Now() + + if circ.Hidden { + circ.SetPos(ctx.Mouse.X, ctx.Mouse.Y) + circ.Hidden = false + } + + // Update targets for our spring animation. + circ.X = ctx.Mouse.X + circ.Y = ctx.Mouse.Y + circ.Radius = circleRadius + } + + circ.Draw(ctx) + + if time.Now().After(lastClick.Add(idleTimeout)) { + state = timeout + } + + case timeout: + circ.Radius = 0 + circ.Draw(ctx) + if circ.rad == 0 { + state = running + lastClick = time.Now() + } } }) } -type circle struct { - // Target values. This is what the spring will seek. +type Circle struct { + // Target values. This is what the spring will use as an equilibrium to + // animate towards. X, Y, Radius float64 - // Managed values. These are the initial x, xVel float64 y, yVel float64 rad, radVel float64 + spring harmonica.Spring - spring harmonica.Spring + Hidden bool } -func newCircle() circle { - const ( - frequency = 0.8 - damping = 0.99 - ) - - return circle{ - spring: harmonica.NewSpring(harmonica.TimeDelta(time.Second/fps), frequency, damping), - } +func (c *Circle) SetPos(x, y float64) { + c.x = x + c.y = y + c.Hidden = false } -func (c *circle) draw(ctx *canvas.Context) { - // Update +func (c *Circle) Draw(ctx *canvas.Context) { + // Update position and radius. c.spring.Update(&c.x, &c.xVel, c.X) c.spring.Update(&c.y, &c.yVel, c.Y) c.spring.Update(&c.rad, &c.radVel, c.Radius) + if c.rad < 0 { + c.rad = 0 + c.radVel = 0 + c.Hidden = true + } + const color = "#f1f1f1" ctx.Push() ctx.DrawCircle(c.x, c.y, c.rad) ctx.SetHexColor(color) ctx.Fill() + ctx.Pop() }