166 lines
5.4 KiB
Python
166 lines
5.4 KiB
Python
from manim import *
|
||
import math
|
||
|
||
class FadeInExample(MovingCameraScene):
|
||
def construct(self):
|
||
# —— 1. 文本淡入淡出(略) —— #
|
||
self.wait(0.5)
|
||
tex = Tex("Inverse ", "Proportion ", "Function").scale(1)
|
||
self.play(Write(tex), run_time=2)
|
||
self.wait(1)
|
||
self.play(
|
||
AnimationGroup(
|
||
FadeOut(tex[0], shift=DOWN),
|
||
FadeOut(tex[1], shift=DOWN),
|
||
lag_ratio=0.09
|
||
)
|
||
)
|
||
self.play(tex[2].animate.to_corner(UL))
|
||
self.wait(0.3)
|
||
|
||
# —— 2. 定义坐标系 & 函数 —— #
|
||
axes = Axes(
|
||
x_range=[-6, 8, 1],
|
||
y_range=[-3, 5, 1],
|
||
x_length=13,
|
||
y_length=6,
|
||
axis_config={
|
||
"include_tip": False,
|
||
"numbers_to_include": [],
|
||
"color": WHITE
|
||
}
|
||
).add_coordinates()
|
||
labels = axes.get_axis_labels()
|
||
func1 = axes.plot(lambda x: 1/x, x_range=[0.1, 11], color=YELLOW)
|
||
func2 = axes.plot(lambda x: 1/x, x_range=[-25, -0.1], color=YELLOW)
|
||
credits = Tex("Made with Manim by David", font_size=24)
|
||
|
||
# —— 3. 播放坐标系 & 函数动画 —— #
|
||
self.add(credits.to_corner(DR))
|
||
self.play(
|
||
Create(axes, run_time=3, lag_ratio=0.1),
|
||
Write(credits, run_time=1),
|
||
)
|
||
self.add(labels)
|
||
self.play(Write(labels))
|
||
self.play(Create(func1, run_time=3), Create(func2, run_time=1))
|
||
|
||
# —— 4. 相机缩放前的公式高亮(略) —— #
|
||
equation = MathTex("y", "=", r"\frac{1}{x}")
|
||
equation.move_to(UR)
|
||
self.play(Write(equation), run_time=1)
|
||
self.wait(0.5)
|
||
rect = SurroundingRectangle(equation[0], color=YELLOW)
|
||
self.play(Create(rect), run_time=0.6)
|
||
self.wait(0.5)
|
||
new_equation = MathTex(r"f(x)", "=", r"\frac{1}{x}")
|
||
new_equation.move_to(equation)
|
||
new_rect = SurroundingRectangle(new_equation[0], color=YELLOW)
|
||
self.play(
|
||
Transform(equation, new_equation),
|
||
Transform(rect, new_rect),
|
||
run_time=1
|
||
)
|
||
self.play(FadeOut(rect), run_time=0.5)
|
||
|
||
# —— 5. 相机移动与缩放 —— #
|
||
self.camera.frame.save_state()
|
||
center = axes.c2p(
|
||
(axes.x_range[0] + axes.x_range[1]) / 2,
|
||
(axes.y_range[0] + axes.y_range[1]) / 2
|
||
)
|
||
shift_vec = RIGHT * 1.6 + UP * 0.7
|
||
scale_factor = 0.6
|
||
self.play(
|
||
self.camera.frame.animate
|
||
.move_to(center)
|
||
.shift(shift_vec)
|
||
.scale(scale_factor),
|
||
run_time=1.4
|
||
|
||
)
|
||
self.remove(credits)
|
||
self.add(credits.to_corner(DR))
|
||
self.play(Write(credits), run_time=1)
|
||
self.wait(0.3)
|
||
|
||
# —— 6. 新增积分区域动画 —— #
|
||
# ValueTracker 跟踪 x(初始值设为 e)
|
||
t = ValueTracker(math.e)
|
||
|
||
# 点 updater:始终在 (t, 1/t) 上
|
||
dot = Dot().set_z_index(10)
|
||
dot.add_updater(lambda d: d.move_to(
|
||
axes.c2p(t.get_value(), 1 / t.get_value())
|
||
))
|
||
|
||
# 动态多边形:积分区域从 x=1 到 current_x
|
||
def make_polygon():
|
||
x = t.get_value()
|
||
y = 1 / x
|
||
# 顶点为 (1,0), (x,0), (x, y), (1, 1)
|
||
p0 = axes.c2p(1, 0)
|
||
p1 = axes.c2p(x, 0)
|
||
p2 = axes.c2p(x, y)
|
||
p3 = axes.c2p(1, 1)
|
||
poly = Polygon(p0, p1, p2, p3)
|
||
poly.set_fill(BLUE, opacity=0.5)
|
||
poly.set_stroke(YELLOW, width=2)
|
||
return poly
|
||
polygon = always_redraw(make_polygon)
|
||
|
||
# 实时显示面积(积分面积)
|
||
area_text = always_redraw(lambda: MathTex(
|
||
rf"x = {t.get_value():.2f}",
|
||
rf"\text{{Area}} = \ln(x) = {math.log(t.get_value()):.2f}"
|
||
).arrange(DOWN).scale(0.6).next_to(dot, RIGHT).shift(UP*1.5))
|
||
|
||
# 添加元素到场景
|
||
self.add(dot, polygon)
|
||
self.play(
|
||
Create(polygon),
|
||
run_time = 1.5
|
||
)
|
||
|
||
self.add(area_text)
|
||
self.play(
|
||
Write(area_text),
|
||
run_time = 3
|
||
)
|
||
|
||
# 动画:x 在 e±0.5 之间滑动,最后停在 e
|
||
self.play(t.animate.set_value(math.e + 0.5), run_time=1)
|
||
self.play(t.animate.set_value(math.e - 0.5), run_time=1)
|
||
self.play(t.animate.set_value(math.e), run_time=1)
|
||
self.wait(1)
|
||
|
||
# —— 7. 转换为积分形式并高亮 x —— #
|
||
self.play(
|
||
Transform(equation, MathTex(
|
||
r"\int_{1}^{x} \frac{1}{t}\,dt = \ln(x)"
|
||
).move_to(RIGHT * 1.3 + UP)),
|
||
run_time=1.5
|
||
)
|
||
|
||
# 高亮 x
|
||
highlight_rect = SurroundingRectangle(equation[-2][1], color=YELLOW)
|
||
self.play(Create(highlight_rect), run_time=0.6)
|
||
self.wait(0.5)
|
||
|
||
# 将 x 替换为 e
|
||
self.play(
|
||
Transform(equation, MathTex(
|
||
r"\int_{1}^{e} \frac{1}{t}\,dt = 1"
|
||
).move_to(equation)),
|
||
run_time=1.5
|
||
)
|
||
self.wait(0.5)
|
||
|
||
# 清除并添加 x = e
|
||
self.play(
|
||
Transform(equation, MathTex("x = e").move_to(equation)),
|
||
FadeOut(highlight_rect, shift=DOWN),
|
||
FadeOut(area_text, shift=DOWN), # 同时移除面积文本
|
||
run_time=1
|
||
)
|
||
self.wait(1) |