010-lissajous(二)
lissajous 曲线随着参数的不同,曲线的形状也会随之改变。如果每次修改一下参数再生成一张图片,未免也太麻烦了。这一次,我们希望生成一个动态图(GIF,Graphics Interchange Format).
前面我已经学会了将 in-memory 图像编码成某种格式(png, jpeg)的方法,如果要将 in-memory 编码成 gif,那就使用 gif 编码器就好了。同样的,gif 也会有相应的 Encode
方法。不过,动态图的一个特点是它是由很多很多的帧表示,每一帧都是一帧图像。gif 也提供了一个 EncodeAll
图像,用于将多个 in-memory 编码成 gif.
1. gif 编码
gif 包的 EncodeAll
定义如下:
func EncodeAll(w io.Writer, g *GIF) error
其中 GIF 定义是这样的:
type GIF struct {
Image []*image.Paletted // in-memory 的 slice
Delay []int // 当前帧延时,单位是 10ms
LoopCount int // 循环次数
// ..
}
可以看到 GIF 这个『结构体』(暂且这么称呼它)里有一个成员是 Image 『数组』。这就好办了,只要我们生成所有不同参数下的 in-memory 图像再塞到这个『数组』就 ok。(注意,这里我尽量避免使用 slice 这个类型,说数组你会更容易理解。实际上在 go 里数组和 slice 是两种不同的东西)
2. 程序
这个程序的路径是 gopl/tutorial/image/lissajous/lissajous2.go
- 代码
// lissajous2.go
package main
import (
"image"
"image/color"
"image/gif"
"math"
"os"
)
const (
size = 128
nframes = 100
)
func main() {
p := 1.0
q := 2.0
phi := 0.0
palett := []color.Color{color.RGBA{0xee, 0xee, 0xee, 0xff}, color.RGBA{0xff, 0, 0, 0xff}}
rec := image.Rect(0, 0, 2*size, 2*size)
s := float64(size - 10)
step := 0.01
// 生成 100 帧
anim := gif.GIF{LoopCount: 1}
for frames := 0; frames < nframes; frames++ {
img := image.NewPaletted(rec, palett)
for t := -2 * math.Pi; t <= 2*math.Pi; t += 0.0001 {
x := math.Sin(p * t)
y := math.Sin(q*t + phi)
img.SetColorIndex(size+int(s*x+0.5), size+int(s*y+0.5), 1)
}
anim.Image = append(anim.Image, img)
anim.Delay = append(anim.Delay, 10)
// 每一帧 q 递增 0.01,这里相当于 q/p 以 0.01 步长递增
p += step
}
gif.EncodeAll(os.Stdout, &anim)
}
- 运行
$ go run lissajous2.go
图1
图 1 所示的动态图,初始值 ,每一帧的 p 值增幅是 0.01,也就是说 以 0.01 为步长递增,一直增加到 1.
- append 函数
append 函数是 go 的内建函数,用于给 slice 追加元素。定义如下:
func append(slice []Type, elems ...Type) []Type
append 第一个参数传入 slice 对象,第二个参数类似 C 语言里的变长参数。append 返回一个新的 slice. 如果传入的 slice 容量不足,append 会自动对其扩容。
3. 总结
- 掌握动态图生成方法
- 掌握 append 函数
练习:
- 尝试不同的相位(
phi
变量)
上一篇: utils.js使用案例详解