SVG动画制作:从基础到实践
立即解锁
发布时间: 2025-08-21 02:25:28 订阅数: 2 


实用Ruby项目:探索编程的无限可能
### SVG动画制作:从基础到实践
#### 1. SVG基础介绍
SVG(可缩放矢量图形)在编码多边形点时采用了特殊的字符串格式化约定,有人认为这一设计存在疑问。从XML的角度看,使用 `<point>` 类型的子节点来描述多边形路径或许是更自然的方式。不过,SVG的方式编写起来可能更简单,占用空间也更少。
若要在SVG中嵌入位图图像(甚至其他SVG图像),可以使用 `<image>` 标签。当非可缩放图形以非原生缩放比例显示时,查看器会尽力对其进行缩放。和其他标签一样,`<image>` 标签也有位置和大小属性。文件引用使用XLink标准的 `href` 属性(超文本引用的缩写),有HTML经验的人对这个属性应该很熟悉。由于 `href` 来自不同的XML命名空间,使用时需要在前面加上 `xlink:` 前缀。示例代码如下:
```ruby
<image x="200" y="200" width="100" height="100" xlink:href="other.bmp" />
```
除了这些基本标签,还有一些更复杂的标签,如用于描述曲线(如三次样条)的标签,能创建出像著名的SVG老虎那样令人惊叹的图像,但使用起来也相当复杂。
#### 2. 动画制作类 `Animation`
大多数将图片转换为动画的程序都期望得到传统的光栅化图像(JPEG似乎是最流行的格式)。为了将图片光栅化,需要创建一个完整的SVG文件,这正是 `Animation` 类的职责。
以下是 `Animation` 类的初始化代码:
```ruby
class Animation
attr_reader :frame, :width, :height, :objects
def initialize(width, height)
@width = width
@height = height
@objects = []
@frame = 0
@step_callbacks = []
@at_callbacks = Hash.new {|hash, key| hash[key] = [] }
end
end
```
初始化时需要指定动画的宽度和高度,同时还会准备其他实例变量,包括两个用于存储回调的变量、一个用于记录帧号的变量,以及一个最重要的列表,用于存储每一帧要转换为SVG的对象。`frame`、`height`、`width` 和 `objects` 列表都是公开可访问的,这样动画中的对象在回调时可以向动画器询问当前帧、尺寸甚至其他对象的信息。
需要注意的是,`@at_callbacks` 哈希表使用块进行初始化。每次使用不存在的键对哈希表进行索引时,这个块都会被调用,从而有机会向哈希表中插入默认值。而另一种常见的提供哈希表默认值的方法是 `Hash.new([])`,但使用时需要谨慎,因为在这种情况下,对于不存在的键会返回空数组,但该值实际上并不会插入到哈希表中,而且所有不存在的键都会返回同一个 `Array` 对象,对这个返回值的修改会影响其他所有值。
为了向动画中插入对象,还实现了 `add` 方法:
```ruby
class Animation
def add(obj)
@objects.push(obj)
end
end
```
#### 3. 动画渲染
当准备好渲染动画时,需要调用 `run` 方法,并传入输出目录和要输出的帧数。示例代码如下:
```ruby
animation = Animation.new(800, 800)
animation.run("myfirstanimation", 5)
```
如果没有添加任何对象,这将是一个非常无聊的动画,只会得到五帧空白画面。`run` 方法的实现如下:
```ruby
class Animation
def run(dir, frames)
Dir.mkdir(dir) rescue nil
digits = frames.to_s.size
frames.times do |n|
@frame = n
file = frame_id(n, digits)
filename = File.join(dir, file)
run_callbacks
render(filename)
end
end
end
```
该方法首先会创建一个目录来保存图像。如果之前已经运行过这个动画,目录可能已经存在,此时 `mkdir` 方法会抛出异常,使用 `rescue` 修饰符可以防止程序因异常而停止。
另外,还需要确定表示帧数所需的位数。例如,如果要渲染100帧,就需要三位数字,第一帧的文件名就是 `000.jpg`。这很重要,因为很多将单个帧转换为电影的工具要求文件名按字母顺序排序。通过始终用所需的位数进行填充,可以确保 `"100"` 不会排在 `"11"` 之前,从而在后续处理中更加方便。`frame_id` 方法使用 `sprintf` 方法将数字和位数转换为适当填充的字符串:
```ruby
class Animation
def frame_id(frame, digits)
sprintf("%.#{digits}d", frame)
end
end
```
#### 4. 回调的注册与运行
为了控制动画,需要能够在特定时间运行用户指定的代码。可以通过注册块作为回调来实现这一点,这些块可以操作、添加或删除动画中的对象。提供了两种注册回调的方式:
- **特定时间触发的回调**:
```ruby
animation.at(4) { puts animation.frame }
```
这个回调只会在构建第4帧时被调用,它使用 `frame` 方法获取当前帧并打印出来。
- **每帧变化时触发的回调**:
```ruby
animation.step { puts animation.frame}
```
注册方法的代码如下:
```ruby
class Animation
def at(frame, &callback)
@at_callbacks[frame].push(callback)
end
def step(&callback)
@step_callbacks.push(callback)
end
end
```
`step` 方法只是将回调存储在列表中,而 `at` 方法使用哈希表为每一帧存储一个回调列表,以支持同一帧上附加多个回调。触发回调的代码如下:
```ruby
class Animation
def run_callbacks
@step_callbacks.each{|cb| cb.call }
@at_callbacks[frame].each {|cb| cb.call }
end
end
```
可以看到,每次注册的 `step` 回调都会被调用,但只有当前帧的 `at` 回调会被触发。
#### 5. 嵌入式Ruby模板(ERB)
为了输出每一帧的SVG文件,使用了嵌入式Ruby模板(ERB)。ERB是一种优雅的Ruby模板语言,随标准发行版一起提供。虽然有些纯粹主义者认为它给模板系统提供了太多自由(因为可以包含任意Ruby代码),但它是组合和排列文本片段的绝佳工具,主要用于HTML模板,模板标签
0
0
复制全文
相关推荐










