铭溢鞋样 發表於 2025-11-23 14:29:00

让你的动画“活”过来:Manim 节奏控制指南 (Rate Functions)

<p>你在制作Manim动画时,是否遇到过这样的困境?</p>
<p>“代码写得天衣无缝,运行流畅,出来的动画却总觉得哪里不对劲?”</p>
<p>虽然物体确实从 A 移动到了 B,但看起来就像是老旧的工业机器人在干活——僵硬、死板,甚至有点无聊。</p>
<p>其实,你的动画离 <strong>“丝滑”</strong> 和 <strong>“专业”</strong>,往往只差这一个参数的距离:<code>rate_func</code> <strong>(速率函数)</strong>。</p>
<p>今天,我们就来聊聊 Manim 中这个不起眼但至关重要的参数,看看如何通过控制 <strong>“时间的流速”</strong>,让你的数学动画不仅能动,而且动得有节奏、有灵魂。</p>
<h1 id="1-什么是-rate-function给时间的进度条">1. 什么是 Rate Function?(给时间的进度条)</h1>
<p>在 <code>Manim</code> 中,当你写下 <code>.animate.shift(RIGHT)</code> 时,默认发生了什么?</p>
<p>如果你觉得动画只是简单的“在 <code>Run Time</code> 时间内移动距离 <code>RIGHT</code>”,那只对了一半。<code>Rate Function</code> 本质上是<strong>动画完成度与时间的关系</strong>。</p>
<p>想象一下你在看视频时的进度条:</p>
<ul>
<li><strong>输入 (</strong>$ t $<strong>)</strong>:当前时间过去了多少(从 0 到 1,代表 0% 到 100% 的时间)。</li>
<li><strong>输出 (</strong>$ f(t) $<strong>)</strong>:动画实际上完成了多少(从 0 到 1,代表 0% 到 100% 的进度)。</li>
</ul>
<p><strong>默认的魔法</strong>:<code>Smooth</code></p>
<p>Manim 的默认 <code>rate_func</code> 是 <code>smooth</code>。<br>
这符合物理世界的惯性定律:<strong>起步时慢(加速),中间快,快结束时慢(减速)</strong>。</p>
<p>这就是为什么默认的动画看起来比较自然。</p>
<p>如果我们把它换成 <code>linear</code>(线性),物体就会瞬间以最大速度启动,最后瞬间急停,看起来就会很像 <strong>“PPT 动画”</strong>。</p>
<h1 id="2-常用函数图鉴选对调味料">2. 常用函数图鉴:选对“调味料”</h1>
<p><code>Manim</code>内置了一大堆写好的函数,位于 <code>manim.utils.rate_functions</code>。</p>
<p>我们可以把它们看作是给动画调味的香料。</p>
<p>为了方便演示,我们假设我们要移动一个小球。</p>
<h2 id="21-基础三剑客">2.1. 基础三剑客</h2>
<ul>
<li><code>linear</code> <strong>(匀速)</strong>
<ul>
<li><strong>效果</strong>:机械感强,速度恒定。</li>
<li><strong>适用场景</strong>:旋转的齿轮、循环滚动的背景、匀速扫描的雷达。</li>
</ul>
</li>
<li><code>smooth</code> <strong>(默认)</strong>
<ul>
<li><strong>效果</strong>:两头慢,中间快。</li>
<li><strong>适用场景</strong>:绝大多数物体的移动、缩放。</li>
</ul>
</li>
<li><code>rush_into</code> / <code>rush_from</code>
<ul>
<li><strong>效果</strong>:
<ul>
<li><code>rush_into</code>: 越走越快,最后“砰”地撞线(只有加速)。</li>
<li><code>rush_from</code>: 一开始很快,慢慢停下来(只有减速)。</li>
</ul>
</li>
<li><strong>适用场景</strong>:连续动作的衔接。比如小球飞入画面停下(<code>rush_from</code>),或者发射出去(<code>rush_into</code>)。</li>
</ul>
</li>
</ul>
<h2 id="22-动感特效组">2.2. 动感特效组</h2>
<ul>
<li><code>there_and_back</code> <strong>(往返)</strong>
<ul>
<li><strong>效果</strong>:走到终点,又原路返回起点。</li>
<li><strong>适用场景</strong>:强调某个东西。比如把公式放大一下再缩回去,告诉观众“看这里!”。</li>
</ul>
</li>
<li><code>wiggle</code> <strong>(摆动)</strong>
<ul>
<li><strong>效果</strong>:像果冻一样左右晃动一下。</li>
<li><strong>适用场景</strong>:表示“错误”、“拒绝”或者引起注意。</li>
</ul>
</li>
<li><code>running_start</code> <strong>(助跑)</strong>
<ul>
<li><strong>效果</strong>:先向后退一点点,然后猛地向前冲。</li>
<li><strong>适用场景</strong>:想要表现物体很有力量感,或者像卡通片里的冲刺效果。</li>
</ul>
</li>
</ul>
<h2 id="23-物理模拟组">2.3. 物理模拟组</h2>
<ul>
<li><code>ease_out_bounce</code><strong>(落地反弹)</strong>
<ul>
<li><strong>效果</strong>:像篮球落地一样,到底部后弹跳几次再停下。</li>
<li><strong>适用场景</strong>:文字掉落、物体自由落体。</li>
</ul>
</li>
</ul>
<h1 id="3-动手写个-demo">3. 动手写个 Demo</h1>
<p>光说不练假把式。下面的示例代码可以直观感受不同函数的区别:</p>
<pre><code class="language-python">from manim import *


class RateFuncComparison(Scene):
    def construct(self):
      # 定义我们想对比的函数
      funcs = [
            linear,
            smooth,
            rush_from,
            rush_into,
            there_and_back,
            rate_functions.ease_out_bounce,
      ]
      labels = [
            "Linear",
            "Smooth",
            "Rush Into",
            "Rush From",
            "There &amp; Back",
            "Bounce",
      ]

      # 创建圆点和文字
      group = VGroup()
      for i, (func, label_text) in enumerate(zip(funcs, labels)):
            dot = Dot(color=TEAL)
            label = Text(label_text, font_size=20).next_to(dot, LEFT)
            row = VGroup(label, dot)
            group.add(row)

      # 竖直排列
      group.arrange(DOWN, buff=0.5).to_edge(LEFT)
      self.add(group)

      # 制作动画:让所有点同时向右移动
      # 注意:我们在这里分别指定了不同的 rate_func
      anims = []
      for item, func in zip(group, funcs):
            dot = item# 获取组里的 Dot
            anims.append(dot.animate(rate_func=func, run_time=3).shift(RIGHT * 4))

      self.play(*anims)
</code></pre>
<p>运行后你会发现,虽然大家的 <code>run_time</code> 都是3秒,移动距离一样,但“性格”截然不同。</p>
<p><img src="https://img2024.cnblogs.com/blog/83005/202511/83005-20251123142756773-71039727.gif" alt="" loading="lazy"></p>
<hr>
<h1 id="4-进阶自定义与时间扭曲">4. 进阶:自定义与时间扭曲</h1>
<p>作为会 <code>Python</code> 的老手,如果内置函数满足不了你怎么办?</p>
<h2 id="41-自定义函数-lambda大法">4.1. 自定义函数 (Lambda大法)</h2>
<p><code>rate_func</code> 接受任何一个 <code>Python</code> 函数。</p>
<p>比如,你想做一个简单的“先慢后快”的加速效果,可以直接用 <code>Lambda</code>:</p>
<pre><code class="language-python"># y = x^2,典型的加速曲线
class CustomRateFuncDemo(Scene):
    def construct(self):
      # 创建一个圆
      circle = Circle(radius=0.5, color=BLUE).shift(LEFT * 2)
      self.add(circle)

      # 使用自定义 rate_func(t**2)让圆向右移动
      self.play(circle.animate(rate_func=lambda t: t**2).shift(RIGHT * 4), run_time=3)
      self.wait()
</code></pre>
<p><img src="https://img2024.cnblogs.com/blog/83005/202511/83005-20251123142756803-2079360807.gif" alt="" loading="lazy"></p>
<h2 id="42-时间挤压-squish-rate-func">4.2. 时间挤压 (Squish Rate Func)</h2>
<p>这是 <code>Manim</code> 中最强大的黑科技之一:<code>squish_rate_func</code>。</p>
<p>假设你写了一个 <code>run_time=6</code> 的动画,但你希望某个特定的变换(比如变色),</p>
<p>在第 <code>1.2</code> 秒到第 <code>3</code> 秒之间(即整个进度的 <code>0.2</code> 到 <code>0.5</code>)由<strong>白色</strong>变成<strong>红色</strong>;</p>
<p>在第 <code>3</code> 秒到第 <code>4.8</code> 秒之间(即整个进度的 <code>0.5</code> 到 <code>0.8</code>)由<strong>红色</strong>变成<strong>绿色</strong>。</p>
<p>你不需要把动画拆成多段写,只需要:</p>
<pre><code class="language-python">class SquishRateFuncDemo(Scene):
    def construct(self):
      # 创建一个圆点
      dot = Dot(color=WHITE).shift(LEFT * 2)
      self.add(dot)

      # 使用UpdateFromAlphaFunc来同时控制位置和颜色变化
      def update_dot(obj, alpha):
            # 位置变化 - 使用默认的linear速率
            obj.move_to(LEFT * 2 + RIGHT * 5 * alpha)

            # 颜色变化 - 使用squish_rate_func控制变色的时间段
            squished_alpha = squish_rate_func(smooth, 0.2, 0.5)(alpha)
            squished_alpha2 = squish_rate_func(smooth, 0.5, 0.8)(alpha)

            if alpha &lt; 0.5:
                obj.set_color(interpolate_color(WHITE, RED, squished_alpha))
            else:
                obj.set_color(interpolate_color(RED, GREEN, squished_alpha2))

      self.play(
            UpdateFromAlphaFunc(dot, update_dot),
            run_time=6,
      )
      self.wait()
</code></pre>
<p>这个技巧在制作复杂的多重同步动画时非常有效!</p>
<p><img src="https://img2024.cnblogs.com/blog/83005/202511/83005-20251123142756674-1043443111.gif" alt="" loading="lazy"></p>
<h1 id="5-总结">5. 总结</h1>
<p><code>Manim</code> 不仅仅是动画工具,它更像是一个导演工具。</p>
<ul>
<li><code>linear</code> 是为了表现机械、循环。</li>
<li><code>smooth</code> 是为了表现自然、物理。</li>
<li><code>there_and_back</code> / <code>wiggle</code> 是为了引导观众的注意力。</li>
<li><code>squish_rate_func</code> 是为了精准控制时间轴。</li>
</ul>
<p>下次当你的动画看起来略显生硬时,不妨停下来想一想:<em>“这个动作的节奏对吗?是不是该换个 rate function 了?”</em></p><br><br>
来源:https://www.cnblogs.com/wang_yb/p/19260567
頁: [1]
查看完整版本: 让你的动画“活”过来:Manim 节奏控制指南 (Rate Functions)