Search code examples
pythonbackgroundkivyparticleskivymd

particle animation background for kivy


I want to put a particle animation in the background screen of my software, something like the link below, but for Python and kivymd

codepen.io/JulianLaval/pen/KpLXOO

I know this may be difficult or impossible for kivymd right now but if anyone has an idea please let me know

enter image description here


Solution

  • Yes! This is absolutely possible (everything is possible in Kivy). Check out the code below:

    from kivy.app import App
    from kivy.uix.widget import Widget
    from kivy.graphics import Line, Color
    from random import randint
    from kivy.clock import Clock
    from kivy.lang import Builder
    from kivy.properties import ListProperty
    from math import sin, cos
    
    
    class ParticleMesh(Widget):
        points = ListProperty()
    
        def __init__(self, **kwargs):
            super().__init__(**kwargs)
            self.direction = []
            self.point_number = 50
            Clock.schedule_once(lambda dt: self.plot_points(), 2)
    
        def plot_points(self):
            for _ in range(self.point_number):
                x = randint(0, self.width)
                y = randint(0, self.height)
                self.points.extend([x, y])
                self.direction.append(randint(0, 359))
            Clock.schedule_interval(self.update_positions, 0)
    
        def draw_lines(self):
            self.canvas.after.clear()
            with self.canvas.after:
                for i in range(0, len(self.points), 2):
                    for j in range(i + 2, len(self.points), 2):
    
                        d = self.distance_between_points(self.points[i], self.points[i + 1], self.points[j],
                                                         self.points[j + 1])
                        if d > 120:
                            continue
                        color = d / 120
                        Color(rgba=[color, color, color, 1])
                        Line(points=[self.points[i], self.points[i + 1], self.points[j], self.points[j + 1]])
    
        def update_positions(self, *args):
            step = 1
            for i, j in zip(range(0, len(self.points), 2), range(len(self.direction))):
                theta = self.direction[j]
                self.points[i] += step * cos(theta)
                self.points[i + 1] += step * sin(theta)
    
                if self.off_screen(self.points[i], self.points[i + 1]):
                    self.direction[j] = 90 + self.direction[j]
    
            self.draw_lines()
    
        @staticmethod
        def distance_between_points(x1, y1, x2, y2):
            return ((x1 - x2) ** 2 + (y1 - y2) ** 2) ** 0.5
    
        def off_screen(self, x, y):
            return x < -5 or x > self.width + 5 or y < -5 or y > self.height + 5
    
    
    kv = """
    FloatLayout:
    
        canvas.before:
            Color:
                rgba: 1, 1, 1, 1
            Rectangle:
                size: self.size
                pos: self.pos
    
        ParticleMesh:
    
            canvas:
                Color:
                    rgba: 0, 0, 0, 1
                Point:
                    points: self.points
                    pointsize: 2
    """
    
    
    class MeshApp(App):
    
        def build(self):
            return Builder.load_string(kv)
    
    
    if __name__ == '__main__':
        MeshApp().run()
    

    This code will create the following (this is just a screenshot - if you run the app, the points move about):

    enter image description here

    First plot_points is called, which creates an array of points randomly placed on the screen. A random direction for each point is also created. This direction is represented by an angle between 0 and 359. On the completion of this function, a Clock object is instantiated and calls update_positions every frame.

    The update_positions moves the particles by one pixel in the angle specified by directions. If the position of the particle is greater than the screen the direction is reversed.

    Finally draw_lines is called. This function first clears all existing lines then draws new ones. If the points are at a distance greater than 120 pixels no line is created. However, if they are closer than 120 pixels a line is drawn such that the closer the two points are, the darker the line will be.

    You can always increase or decrease the number of points on the screen by changing the self.point_number property.

    I will leave it up to you to change the colour of the points and the background - I don't think that should be too hard.