//go:build ignore package main import ( "image" "image/color" "image/gif" "log" "math" "os" ) const ( width = 16 height = 32 radius = 2.6 levels = 16 samples = 4 delay = 8 ) var frames = []rune("⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏") var dots = [6][2]float64{{4.5, 8}, {4.5, 16}, {4.5, 24}, {11.5, 8}, {11.5, 16}, {11.5, 24}} func main() { render("img/spinner-light.gif", color.RGBA{0x1a, 0x45, 0xbd, 0xff}, color.RGBA{0xfb, 0xfb, 0xfa, 0xff}) render("img/spinner-dark.gif", color.RGBA{0x8a, 0xb4, 0xff, 0xff}, color.RGBA{0x15, 0x17, 0x1a, 0xff}) } func render(path string, fg, bg color.RGBA) { palette := color.Palette{color.RGBA{}} for i := 1; i <= levels; i++ { palette = append(palette, blend(bg, fg, float64(i)/levels)) } anim := &gif.GIF{} for _, r := range frames { bits := int(r - 0x2800) img := image.NewPaletted(image.Rect(0, 0, width, height), palette) for y := 0; y < height; y++ { for x := 0; x < width; x++ { level := int(math.Round(coverage(bits, x, y) * levels)) img.SetColorIndex(x, y, uint8(level)) } } anim.Image = append(anim.Image, img) anim.Delay = append(anim.Delay, delay) anim.Disposal = append(anim.Disposal, gif.DisposalBackground) } f, err := os.Create(path) if err != nil { log.Fatal(err) } defer f.Close() err = gif.EncodeAll(f, anim) if err != nil { log.Fatal(err) } } func coverage(bits, x, y int) float64 { hit := 0 for sy := 0; sy < samples; sy++ { for sx := 0; sx < samples; sx++ { px := float64(x) + (float64(sx)+0.5)/samples py := float64(y) + (float64(sy)+0.5)/samples for i, d := range dots { if bits&(1<