confetty/confetti/confetti.go

105 lines
2.5 KiB
Go
Raw Permalink Normal View History

2021-08-07 16:57:00 -07:00
package confetti
2021-08-06 18:24:56 -07:00
import (
"math/rand"
"time"
2021-08-07 11:46:09 -07:00
"github.com/maaslalani/confetty/array"
2021-08-08 19:49:30 -07:00
"github.com/maaslalani/confetty/simulation"
2021-08-06 19:00:08 -07:00
2021-08-06 18:24:56 -07:00
tea "github.com/charmbracelet/bubbletea"
2021-11-04 21:24:58 -07:00
"github.com/charmbracelet/harmonica"
2021-08-06 18:24:56 -07:00
"github.com/charmbracelet/lipgloss"
)
2021-08-06 20:43:02 -07:00
const (
2021-11-04 21:08:48 -07:00
framesPerSecond = 30.0
2021-08-06 20:43:02 -07:00
numParticles = 75
)
2021-08-06 18:24:56 -07:00
2021-08-06 19:08:18 -07:00
var (
colors = []string{"#a864fd", "#29cdff", "#78ff44", "#ff718d", "#fdff6a"}
2021-08-07 11:43:40 -07:00
characters = []string{"█", "▓", "▒", "░", "▄", "▀"}
2021-08-06 19:08:18 -07:00
)
2021-08-06 18:24:56 -07:00
type frameMsg time.Time
func animate() tea.Cmd {
2021-08-06 20:43:02 -07:00
return tea.Tick(time.Second/framesPerSecond, func(t time.Time) tea.Msg {
2021-08-06 18:24:56 -07:00
return frameMsg(t)
})
}
2021-08-06 20:43:02 -07:00
// Confetti model
2021-08-06 18:24:56 -07:00
type model struct {
2021-08-08 19:49:30 -07:00
system *simulation.System
2021-08-06 18:24:56 -07:00
}
func Spawn(width, height int) []*simulation.Particle {
particles := []*simulation.Particle{}
2021-08-06 20:43:02 -07:00
for i := 0; i < numParticles; i++ {
2021-08-06 18:24:56 -07:00
x := float64(width / 2)
2021-08-08 19:49:30 -07:00
y := float64(0)
2021-08-06 18:24:56 -07:00
2021-08-08 19:49:30 -07:00
p := simulation.Particle{
2021-11-04 21:24:58 -07:00
Physics: harmonica.NewProjectile(
harmonica.FPS(framesPerSecond),
harmonica.Point{X: x + (float64(width/4) * (rand.Float64() - 0.5)), Y: y, Z: 0},
harmonica.Vector{X: (rand.Float64() - 0.5) * 100, Y: rand.Float64() * 50, Z: 0},
harmonica.TerminalGravity,
2021-08-06 19:00:08 -07:00
),
2021-08-08 19:49:30 -07:00
Char: lipgloss.NewStyle().
2021-08-06 19:08:18 -07:00
Foreground(lipgloss.Color(array.Sample(colors))).
Render(array.Sample(characters)),
2021-08-06 18:24:56 -07:00
}
2021-08-06 19:08:18 -07:00
particles = append(particles, &p)
2021-08-06 18:24:56 -07:00
}
2021-08-08 19:49:30 -07:00
return particles
}
func InitialModel() model {
2021-08-08 19:49:30 -07:00
return model{system: &simulation.System{
Particles: []*simulation.Particle{},
Frame: simulation.Frame{},
2021-08-08 19:49:30 -07:00
}}
2021-08-06 18:24:56 -07:00
}
2021-08-06 20:43:02 -07:00
// Init initializes the confetti after a small delay
2021-08-06 18:24:56 -07:00
func (m model) Init() tea.Cmd {
2021-08-08 19:49:30 -07:00
return animate()
2021-08-06 18:24:56 -07:00
}
2021-08-06 20:43:02 -07:00
// Update updates the model every frame, it handles the animation loop and
// updates the particle physics every frame
2021-08-06 18:24:56 -07:00
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
2021-08-08 19:49:30 -07:00
switch msg.String() {
case "ctrl+c", "q":
return m, tea.Quit
2021-08-07 13:13:35 -07:00
}
m.system.Particles = append(m.system.Particles, Spawn(m.system.Frame.Width, m.system.Frame.Height)...)
2021-08-08 19:49:30 -07:00
return m, nil
case frameMsg:
m.system.Update()
2021-08-06 18:24:56 -07:00
return m, animate()
case tea.WindowSizeMsg:
if m.system.Frame.Width == 0 && m.system.Frame.Height == 0 {
// For the first frameMsg spawn a system of particles
m.system.Particles = Spawn(msg.Width, msg.Height)
}
2021-08-08 19:49:30 -07:00
m.system.Frame.Width = msg.Width
m.system.Frame.Height = msg.Height
2021-08-06 18:24:56 -07:00
return m, nil
default:
return m, nil
}
}
2021-08-06 20:43:02 -07:00
// View displays all the particles on the screen
2021-08-06 18:24:56 -07:00
func (m model) View() string {
2021-08-08 19:49:30 -07:00
return m.system.Render()
2021-08-06 18:24:56 -07:00
}