You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kivy中两个Label沿贝塞尔曲线动画异常问题求助

问题分析

你的代码核心问题在于循环中复用了同一个实例变量self.sb:在build()方法的循环里,每次创建SimpleBezier都会覆盖self.sb,最终两个标签的动画回调update_pos都只会使用最后一次赋值的SimpleBezier实例,也就是第二条曲线。

另外,BezierPath类里的self.bezier也存在同样的覆盖问题——画布上只会显示最后一条贝塞尔曲线,不过这只是显示问题,不影响标签运动逻辑。

修复方案

给每个Label对象绑定对应的SimpleBezier实例,而不是用全局的self.sb存储。具体做法是给Label添加一个自定义属性,比如bezier_curve,把对应的曲线实例赋值给它,然后在update_pos中直接调用这个属性的get_point方法。

修正后的完整代码
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.widget import Widget
from kivy.graphics import Color, Bezier
from kivy.animation import Animation
from math import pow, comb
from random import randint, random

class BezierPath(Widget):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.points = [[100, 100, 200, 100, 300, 200, 400, 1400], [620, 100, 520, 100, 420, 200, 400, 1400]]
        self.colors = [(1, 0, 0, 1), (1, 0.5, 0, 1)]
        self.starting_points = [(100, 100), (620, 100)]

        # 修复画布曲线覆盖问题:用列表存储所有Bezier实例
        self.beziers = []
        for i in range(len(self.points)):
            with self.canvas:
                Color(*self.colors[i])
                bezier = Bezier(points=self.points[i])
                self.beziers.append(bezier)

class SimpleBezier:
    def __init__(self, **kwargs):
        self.points = kwargs.pop('points', [])

    def get_point(self, t):
        num_points = int(len(self.points)/2)
        x = 0
        y = 0

        for i in range(num_points):
            px = self.points[i * 2]
            py = self.points[i * 2 + 1]
        
            coeff = comb(num_points-1, i) * pow(t, i) * pow((1.0 - t), (num_points - 1 - i))
            x += coeff * px
            y += coeff * py

        return x, y


class BezierLabelApp(App):
    def build(self):
        bezier_path = BezierPath()

        for i in range(len(bezier_path.points)):
            label = Label(
                pos=bezier_path.starting_points[i],
                font_size=30,
                color=bezier_path.colors[i]
            )

            bezier_path.add_widget(label)

            # 给当前标签绑定对应的贝塞尔曲线实例
            label.bezier_curve = SimpleBezier(points=bezier_path.points[i])

            animation = Animation(duration=randint(10, 20))
            animation.bind(on_progress=self.update_pos)
            animation.start(label)

        return bezier_path

    def update_pos(self, anim, label, t):
        # 使用标签自身绑定的曲线计算位置
        label.center = label.bezier_curve.get_point(t)
        # verbose
        num = str(t)[str(t).index('.')+1:][:2].lstrip('0')
        label.text = num

if __name__ == '__main__':
    BezierLabelApp().run()
关键修改点
  1. 给Label绑定专属曲线:在循环创建Label时,添加label.bezier_curve = SimpleBezier(...),让每个标签持有自己的曲线实例。
  2. 修正画布曲线显示:把self.bezier改成列表self.beziers存储所有曲线,避免覆盖,现在画布上会显示两条不同颜色的贝塞尔曲线。
  3. 回调方法修改:update_pos中不再使用self.sb,而是直接调用label.bezier_curve.get_point(t),确保每个标签用自己的曲线计算位置。

内容的提问来源于stack exchange,提问作者Timson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 18:54:58