I'm relatively new to code and I'm currently learning Kivy. And I'm desperately stuck. I'm trying to create a label (which I have), and a Bezier curve. I want to animate the Label to move in the pat of the Bezier curve, not the control points.
This is the code I have so far:
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
class BezierPath(Widget):
def __init__(self, **kwargs):
super().__init__(**kwargs)
with self.canvas:
Color(1, 0, 0)
self.bezier = Bezier(points=[100, 100, # Control point 1
200, 300, # Control point 2
300, 100, # Control point 3
400, 400]) # End point
class BezierLabelApp(App):
def build(self):
bezier_path = BezierPath()
label = Label(text="Follow me!")
label.font_size = 20
bezier_path.add_widget(label)
animation = Animation(points=bezier_path.bezier.points, duration=5)
animation.repeat = True
animation.start(label)
return bezier_path
if __name__ == "__main__":
BezierLabelApp().run()
What am I doing wrong?`
The default Animation
animates a property of the Widget
along a straight line from its starting position to its ending position. There is no built-in way to animate along a path.
You can animate along a path by providing your own calculation of the path and using that in an on_progress
binding for the Animation
. First, you need something to calculate the path:
class SimpleBezier:
def __init__(self, **kwargs):
self.points = kwargs.pop('points', [])
def get_point(self, t):
# get the x, y point at parameter value t (0->1)
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) * math.pow(t, i) * math.pow((1.0 - t), (num_points - 1 - i))
x += coeff * px
y += coeff * py
return x, y
Then, you can use this for the on_progress
binding in the App
class:
class BezierLabelApp(App):
def build(self):
bezier_path = BezierPath()
label = Label(text="Follow me!")
label.font_size = 20
bezier_path.add_widget(label)
animation = Animation(duration=5)
animation.bind(on_progress=self.update_pos) # setup the binding
# animation.repeat = True
animation.start(label)
self.sb = SimpleBezier(points=bezier_path.bezier.points) # creat an instance of SimpleBezier
return bezier_path
def update_pos(self, anim, label, t):
label.center = self.sb.get_point(t) # use the SimpleBezier instance to calculate the position
I have commented out the animation.repeat = True
as I was not sure what your purpose was.