derivative_demonstration.py
1from manim import *2 3# 配置中文环境4class DerivativeDefinition(Scene):5 def construct(self):6 # 使用黑色背景7 self.camera.background_color = BLACK8 9 # 创建坐标系 - 不显示数值10 axes = Axes(11 x_range=[-1, 4, 1],12 y_range=[-1.5, 1.5, 0.5],13 axis_config={"color": BLUE, "include_numbers": False}, # 移除数值14 x_length=6,15 y_length=616 )17 18 # 创建坐标轴标签,并手动调整y标签位置19 x_label = axes.get_x_axis_label("x")20 y_label = axes.get_y_axis_label("y").shift(DOWN * 0.5) # 将y标签下移21 axes_labels = VGroup(x_label, y_label)22 23 # 创建新函数 f(x) = sin(x),但只用通用表达式显示24 func = lambda x: np.sin(x)25 graph = axes.plot(func, color=BLUE, stroke_width=3)26 27 # 修改为通用表达式 y = f(x)28 func_label = MathTex("y = f(x)").next_to(graph, UP).shift(RIGHT)29 30 # 选择点 x₀ = π/4 (原来的a)31 x0 = np.pi/432 point_x0 = axes.coords_to_point(x0, func(x0))33 dot_x0 = Dot(point_x0, color=YELLOW, radius=0.08) # 稍微增大点的半径34 35 # 添加点x₀的标签36 x0_label = MathTex("x_0").next_to(dot_x0, DOWN+RIGHT, buff=0.1).scale(0.9)37 38 # 创建切线 - 提前创建,一直显示39 exact_derivative = np.cos(x0) # sin(x)的导数是cos(x)40 tangent_slope = exact_derivative41 tangent_line = Line(42 start=axes.coords_to_point(x0 - 2, func(x0) - 2*tangent_slope), # 延长切线43 end=axes.coords_to_point(x0 + 2, func(x0) + 2*tangent_slope), # 延长切线44 color=RED,45 stroke_width=2.546 )47 48 # 创建切线标签49 tangent_label = Text("切线", font="SimSun", color=RED).scale(0.7)50 # 将标签定位到切线的左下角51 tangent_label.next_to(52 axes.coords_to_point(x0 - 1.5, func(x0) - 1.5*tangent_slope), # 切线左下方的点53 DOWN + LEFT, # 放在这个点的左下方54 buff=0.255 )56 57 # 最终结论: 切线斜率 = 计算公式 = f'(x0)58 final_conclusion_1 = Text("切线斜率 =", font="SimSun").scale(0.7).to_corner(UL, buff=0.3)59 final_conclusion_2 = MathTex("\\lim_{\\Delta x \\to 0} \\frac{f(x_0+\\Delta x) - f(x_0)}{\\Delta x} = f'(x_0)").scale(0.7).next_to(final_conclusion_1, RIGHT, buff=0.1)60 61 # 在右侧显示割线斜率计算公式 - 调整为两行显示,并向右移动62 secant_slope_text = Text("割线斜率 =", font="SimSun").scale(0.7).to_edge(RIGHT, buff=2.0).shift(RIGHT * 1.0) # 减小右边距,并额外右移63 secant_slope_formula_1 = MathTex("\\frac{f(x_0+\\Delta x) - f(x_0)}{\\Delta x}").scale(0.7).next_to(secant_slope_text, DOWN, buff=0.3).align_to(secant_slope_text, RIGHT) # 对齐右侧64 65 # 初始设置66 self.play(67 Create(axes),68 Write(axes_labels),69 Create(graph),70 Write(func_label)71 )72 self.play(Create(dot_x0), Write(x0_label))73 self.wait()74 75 # 先显示切线,然后显示切线标签76 self.play(Create(tangent_line))77 self.play(Write(tangent_label)) # 添加切线标签78 self.add(tangent_line) # 确保切线始终可见79 80 # 使用ValueTracker实现连续动画 - 变量h改为delta_x81 delta_x_tracker = ValueTracker(2.0) # 起始Δx值更大,更明显82 83 # 创建依赖于delta_x_tracker的动态对象84 def get_secant_point():85 delta_x = delta_x_tracker.get_value()86 return axes.coords_to_point(x0 + delta_x, func(x0 + delta_x))87 88 dot_x0_plus_delta = Dot(color=GREEN, radius=0.08)89 dot_x0_plus_delta.add_updater(lambda d: d.move_to(get_secant_point()))90 91 # 添加x₀+Δx标签92 x0_plus_delta_label = MathTex("x_0 + \\Delta x").scale(0.8)93 x0_plus_delta_label.add_updater(94 lambda l: l.next_to(dot_x0_plus_delta, UP+RIGHT, buff=0.1)95 )96 97 # 创建延长的割线98 secant_line = Line(color=GREEN, stroke_width=4.0)99 100 # 更新函数,使割线延长101 def update_secant_line(line):102 delta_x = delta_x_tracker.get_value()103 end_point = axes.coords_to_point(x0 + delta_x, func(x0 + delta_x))104 105 # 计算延长线的向量方向106 direction = end_point - point_x0107 direction = direction / np.linalg.norm(direction)108 109 # 延长线的起点和终点110 extended_start = point_x0 - direction * 3 # 向后延长111 extended_end = end_point + direction * 3 # 向前延长112 113 line.put_start_and_end_on(extended_start, extended_end)114 return line115 116 secant_line.add_updater(update_secant_line)117 118 # 动态更新Δx值显示119 delta_x_label = Text("", font="SimSun")120 delta_x_label.add_updater(121 lambda t: t.become(122 Text(f"Δx = {delta_x_tracker.get_value():.2f}", font="SimSun")123 ).next_to(secant_slope_formula_1, DOWN, buff=0.3)124 )125 126 # 1. 首先显示割线和点 - 修改显示顺序127 self.play(Create(dot_x0_plus_delta), Write(x0_plus_delta_label), Create(secant_line))128 self.wait()129 130 # 2. 然后显示割线斜率计算公式 - 修改为两行显示131 self.play(Write(secant_slope_text), Write(secant_slope_formula_1), Write(delta_x_label))132 133 # 突出显示当前公式,表示极限过程134 limit_process_text = Text("极限过程", font="SimSun", color=YELLOW).scale(0.7).next_to(delta_x_label, DOWN, buff=0.5)135 limit_arrow = Arrow(limit_process_text.get_top(), delta_x_label.get_bottom(), buff=0.1, color=YELLOW)136 self.play(Write(limit_process_text), Create(limit_arrow))137 138 # 3. 执行Δx从大到小变化的动画,展示割线趋近于切线139 self.play(delta_x_tracker.animate.set_value(0.05), rate_func=lambda t: 1 - (1-t)**4, run_time=8)140 self.wait()141 142 # 4. 显示结论:割线趋近于切线143 conclusion = Text("当 Δx → 0 时,割线趋近于切线", font="SimSun").to_edge(DOWN)144 self.play(Write(conclusion))145 self.wait()146 147 # 5. 最后显示总结性公式:切线斜率 = 计算公式 = f'(x0)148 self.play(Write(final_conclusion_1), Write(final_conclusion_2))149 self.wait(2) 讲解
这个视频围绕「导数的几何意义」展开。画面把问题、图像和公式放在同一条理解路径中,让抽象关系先被看见。
开头先建立问题背景和主要对象,让观察从标题、坐标或第一组关系进入。
画面中出现的文字「y = f(x)」给出视觉入口。随后画面通过对象创建、移动、淡入淡出和高亮,把抽象命题拆成可以跟随的步骤。
随后出现更具体的图像或公式提示,动画会把局部对象和整体结论联系起来。这里可以重点观察变量、曲线、区域或向量如何随镜头推进而变化。
核心公式可以写成:
这类公式可以和画面中的符号一一对应。
观察路径
观察路径可以分三步:先锁定「导数的几何意义」中的核心对象,尤其留意导数、切线、割线之间的联系;再跟随画面中变量、图像或向量的变化;最后回到公式或结论,判断局部变化如何支撑整体关系。
本页可以从导数、切线、割线、函数图像这些概念进入,继续沿相邻问题观察同一类数学结构。