本动画通过二次函数 在点 处的实例,直观展示切线的几何意义,并通过割线簇和局部放大演示“局部线性化”的核心思想。
核心思想
动画围绕三个核心概念层层递进:
- 切线的唯一性:在给定点处,无数条割线中只有一条是切线
- 局部线性化:在足够小的邻域内,曲线可以用切线近似代替
动画结构
第一部分:基础建立
第二部分:切线展示
第三部分:割线簇对比
- 围绕该点绘制一系列旋转的白色线段,模拟不同方向的割线
- 通过对比凸显切线的唯一性——只有一条线与曲线在该点“相切”
第四部分:局部放大观察
- 相机缩放至该区域,展示曲线与切线在局部范围内几乎重合
视觉设计亮点
- 相机控制:通过缩放动画,直观展示“局部线性化”概念
完整代码
from utils import *
config.tex_template = myTemplate
classv7(MovingCameraScene):
defconstruct(self):
# ============ 1. 坐标轴和函数图形 ============
ax, ax_x_label, ax_y_label = axes_func(x_range=(-5, 5), x_length=8,
y_range=(-1, 9), y_length=6)
f = f_func2(0.5) # 定义函数 f(x) = 0.5x²
gr = ax.plot(f, x_range=[-4.1, 4.1], stroke_width=4, color=BLUE)
gr_label = MathTex("y = x^2", color=BLUE).to_edge(UP, buff=1.2).shift(LEFT * 4.2)
self.play(LaggedStart(FadeIn(ax), FadeIn(ax_x_label, ax_y_label),
Create(gr), FadeIn(gr_label), lag_ratio=0.2))
self.wait(2)
# ============ 2. 抛物线上的一点及其切线 ============
# 点 dot_1 和标签
dot_1 = dot_func(ax, f, 2).set_z_index(6)
dot_1_label_aux = MathTex(r"(x,\ x^2)").scale(0.8).next_to(dot_1, UL, buff=0.05)
dot_1_label = MathTex(r"(2,\ 2^2)").scale(0.8).move_to(dot_1_label_aux)
self.play(GrowFromCenter(dot_1))
self.wait(2)
self.play(FadeIn(dot_1_label_aux))
self.wait(2)
self.play(ReplacementTransform(dot_1_label_aux, dot_1_label))
self.wait(2)
tangent_line = tangent_line_func(ax, f, 2, length=5, color=GREEN)
tangent_line_label = MathTex("y = ax + b", color=GREEN).next_to(
tangent_line, RIGHT, buff=0.2).shift(UP * 2.35)
indicative_arrow_1 = Arrow(ORIGIN, UP * 1.5).next_to(
tangent_line_label, DOWN, buff=0.1).shift(RIGHT * 0.15
).set_color(GRAY).rotate(30 * DEGREES)
self.play(FadeIn(VGroup(tangent_line, tangent_line_label), shift=UL), run_time=1.5)
self.wait(2)
# 高亮切线公式中的斜率项
self.play(FadeIn(indicative_arrow_1),
tangent_line_label[0][2].animate.set_color(YELLOW))
self.wait(2)
self.play(FadeOut(indicative_arrow_1),
tangent_line_label[0][2].animate.set_color(GREEN))
self.wait(2)
# ============ 3. 一系列割线围绕切线旋转 ============
# 创建一系列旋转的白色线段,模拟割线簇
base_line = Line(stroke_width=2, color=WHITE).scale(2.7).set_opacity(0.5).move_to(dot_1)
not_tangent_lines = VGroup()
for i in range(12):
line = base_line.copy().rotate((i * 15) * DEGREES)
not_tangent_lines.add(line)
self.play(LaggedStart(*[GrowFromCenter(line) for line in not_tangent_lines],
lag_ratio=0.05))
self.wait(2)
self.play(FadeOut(not_tangent_lines))
self.wait(2)
# ============ 4. 放大展示切线附近区域 ============
t1 = Tex(r"切线", color=GREEN).scale(0.8).move_to(tangent_line_label).shift(DOWN + LEFT)
sq_aux_1 = Rectangle(width=4, height=3, stroke_width=2).scale(0.5).move_to(
dot_1).set_stroke(opacity=0.5)
self.play(FadeIn(t1))
self.wait(2)
self.play(Create(sq_aux_1))
self.wait(2)
# 保存相机状态,放大显示切线区域
self.camera.frame.save_state()
self.play(self.camera.frame.animate.scale(0.2).move_to(dot_1),
gr.animate.set_stroke(width=2),
tangent_line.animate.set_stroke(width=2).shift(DR * 0.01),
sq_aux_1.animate.scale(0.5), run_time=10)
self.wait(2)
# 恢复相机状态
self.play(self.camera.frame.animate.restore(),
gr.animate.set_stroke(width=5),
tangent_line.animate.set_stroke(width=5).shift(UL * 0.01), run_time=2)
self.wait(2)
self.play(FadeOut(*self.mobjects))
self.wait(2)
%manim -ql -v WARNING v7
文章首发:微信公众号:数与理 | 发布日期: 2026 年 3 月 28 日