新增功能:Material 涟漪效果
tkdeft 本身不带组件,这次也没有加组件——加的是一个可以接到任何控件上的效果:
tkdeft.extra.ripple。你的按钮照旧自己画,只要在点击处记一笔、在重绘里贴一层。
from tkdeft.extra.ripple import Ripple
self.ripple = Ripple(self, on_frame=self._paint) # 建控制器,每帧回调重绘
self.ripple.start(event.x, event.y) # 点击处起涟漪
self.ripple.paint(color="#000000", opacity=0.12, # 在合适层级贴一层
corner_radius=8)
tkdeft.extra 是新开的子包:核心包继续只提供零件,可选功能单独放、按需加载
(import tkdeft 不会顺带把 Pillow 的 ImageTk 拉起来)。
examples/ripple_button.py 里有一个完整的按钮示范:圆角底色、悬停 / 按压 /
焦点 / 禁用、空格触发、T 换主题都由示例自己画,涟漪只借这三行。也带自检:
python examples/ripple_button.py # 默认自动挑栅格引擎
python examples/ripple_button.py --engine tksvg # 换引擎看每帧开销
python examples/ripple_button.py --check # 自检,不开窗口
值得单独说的几点
- 半透明涟漪没有用
create_oval:Tk 的 canvas item 没有逐像素 alpha。 这里在 Pillow 里算「圆 ∩ 圆角矩形」的遮罩,超采样 3 倍再缩回,得到一张 与控件等大的 RGBA 图层,create_image贴上去。底色画在它下面、文字画在上面, 层级由宿主决定。 - 动画进度按
time.monotonic()算,不是每帧累加 16 ms—— 主线程卡一下,下一帧直接跳到正确进度,整段动画的时长不会被拖长。 - 每帧全量重绘在这个尺寸下完全够用:一个 200×48 的按钮,
中位耗时
skia0.42 ms、pillow0.38 ms、cairo0.41 ms, 默认的tksvg也只要 4.66 ms(60 fps 的预算是 16.7 ms)。 - 控制器不碰 canvas item、也不存颜色:
paint()每次现取颜色与圆角, 所以同一份状态能跟着主题换色,不需要重建对象。
顺手记下三个坑
写这个效果时踩到的、文档里原来没写清楚的:
class MyButton(DCanvas, DDrawWidget)会直接 MRO 报错——DDrawWidget已经继承DCanvas,只写后者就行;draw_roundrect(..., tags=...)在SVG 回退路径上会抛Invalid attribute 'tags' for svg-element <rect>,打标签要用addtag_withtag(返回的 item id);- 先
pack()再mainloop()时,<Configure>往往在映射之前就发完了, 而DDrawWidget._draw()在未映射时直接返回——控件会一直空白, 直到鼠标划过。补一个<Map>绑定即可(示例里已经这么做)。
另外还发现:PrintWindow 抓不到 canvas 上的半透明叠加层(窗口里看得见、
截图里没有),所以文档插图改成"引擎渲染的底色 + ripple_layer() 逐帧合成"。
这一条已经写进 常见问题与排查 第 13 条。
文档
新增 涟漪效果:给任意控件加一层扩散动画,
先讲怎么接(三步、控制器与纯函数的 API),再按"为什么不用
create_oval → 遮罩怎么算 → 浓度与半径两条曲线 → 计时 → 图片保活 → 三个坑 →
性能"拆开讲。
插图 ripple-frames.png 由 docs/gen_figures.py --write --only ripple-frames 生成,
可重新跑。