# databar.py (仅列出与原文件不同或新增的部分)
from manimimport *
import pandasas pd
import math
font1 ="Bodoni MT"
font2 ="KaiTi"
MAX_BAR_LENGTH =12 # 你要求的“最大只能为12”
# 前面的读取 Excel、配置画布大小等保持不变
# ──────────────────────────────────────────────────────────────
excel_path ="data.xlsx"
df0 = pd.read_excel(excel_path)# 仅做尺寸计算
data_cols = df0.shape[1] -1
if data_cols <=10:
config.frame_width =16
config.frame_height =10
else:
config.frame_width =16
config.frame_height =10 + (data_cols -10)
config.pixel_width = config.frame_width *60 *1
config.pixel_height = config.frame_height *60 *1
class DataBar(ThreeDScene):
# ───── 创建单根柱子─────
# 现在返回(len_tracker, value_tracker)
def make_bar(self, top_buff, init_len, init_val,
label_text="", bar_color=RED):
len_tracker = ValueTracker(init_len)# 显示长度
value_tracker = ValueTracker(init_val)# 真实数值
# 1. 柱体
bar = always_redraw(
lambda: Cube(side_length=1,
fill_color = bar_color,
fill_opacity=1,
stroke_width=1,
stroke_color=WHITE
)
.scale([len_tracker.get_value(),0.4,0.4])# 只用长度tracker
.to_edge(LEFT,buff=2)
.to_edge(UP,buff=top_buff)
)
# 2. 数字(用 value_tracker)
number = always_redraw(
lambda: DecimalNumber(value_tracker.get_value(),
num_decimal_places=2,
color=WHITE,
font_size=28
).next_to(bar, RIGHT,buff=0.2)
)
# 3. 标签
label = Text(label_text,color=WHITE,font_size=32,font=font2)
label.add_updater(lambda m: m.next_to(bar, LEFT,buff=0.2)
.align_to(bar, UP))
self.add(bar, number, label)
return len_tracker, value_tracker
# ───── construct ─────
def construct(self):
# 1) 背景(可选)
bg = ImageMobject("image/bg.png")
bg.set_z_index(-10)
self.add(bg)
# 2) 读取真正用于动画的Excel
df = pd.read_excel(excel_path)
date_list = df.iloc[:,0].astype(str).tolist()
numeric_part = df.iloc[:,1:]
bar_names = numeric_part.columns.tolist()
nums_sets = [numeric_part[c].tolist()for cin bar_names]
n_bars =len(bar_names)
n_frames =len(df)
palette = [RED, GOLD, GREEN, BLUE, PINK, PURPLE,
ORANGE, MAROON_B, TEAL, YELLOW]
palette = (palette * math.ceil(n_bars /len(palette)))[:n_bars]
# 0. 先算第 0 帧的缩放
frame0_max =max(s[0]for sin nums_sets)
scale0 = MAX_BAR_LENGTH / frame0_maxif frame0_max !=0 else 1
# 1. 创建柱子
len_trs, val_trs = [], []
for iin range(n_bars):
top_buff =1.5 +0.8 * i
len0 = nums_sets[i][0] * scale0
init_value = nums_sets[i][0]
lt, vt =self.make_bar(top_buff, len0, init_value,
label_text=bar_names[i],
bar_color=palette[i])
len_trs.append(lt)
val_trs.append(vt)
# 2. 右上角日期
date_mobj = Text(date_list[0],color=YELLOW,
font_size=40,font=font1).to_edge(UR,buff=0.5)
self.add(date_mobj)
# 3. 逐帧播放
for stepin range(1, n_frames):
frame_max =max(nums_sets[k][step]for kin range(n_bars))
scale = MAX_BAR_LENGTH / frame_maxif frame_max !=0 else 1
bar_len_anims = [
len_trs[k].animate.set_value(nums_sets[k][step] * scale)
for kin range(n_bars)
]
bar_val_anims = [
val_trs[k].animate.set_value(nums_sets[k][step])
for kin range(n_bars)
]
# 日期瞬时替换
new_date = Text(date_list[step],color=YELLOW,
font_size=40,font=font1).to_edge(UR,buff=0.5)
date_mobj.become(new_date)
self.play(*(bar_len_anims + bar_val_anims),
run_time=2,rate_func=linear)
self.wait(0.5)
# manim -pql caogao.py DataBar