Search code examples
kivyinfinite-scrollpython-3.9

Kivy Seamless/Infinite scrolling effect


I wrote the below code in attempts to make a seamlessly flowing image, which is on infinite scroll. (Perhaps the term infinite scroll is misleading in this context, but it should seamlessly scroll, where when a certain part of image disappears from bottom, it should already be appearing on top as soon as it disappears, which is seamless scrolling)

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.image import Image
from kivy.clock import Clock

class ScrollingImageApp(App):
    def build(self):
        self.root = BoxLayout(orientation='vertical')

        # Create a non-interfering layer for scrolling image
        self.scroll_layer = Image(source='pexels-tessa-k-896673.jpg', allow_stretch=True, keep_ratio=False)
        self.root.add_widget(self.scroll_layer)

        # Schedule the scroll function to be called every frame
        Clock.schedule_interval(self.scroll_image, 1 / 60.0)

        return self.root

    def scroll_image(self, dt):
        # Scroll the image vertically
        self.scroll_layer.y -= 1
        if self.scroll_layer.y < -self.scroll_layer.height:
            self.scroll_layer.y = self.root.height

if __name__ == '__main__':
    ScrollingImageApp().run()


# Image source: https://www.pexels.com/photo/timelapse-photo-of-gray-clouds-896673/
# The above image was made seamless using Gimp software (photoshop alternative)

There are a few problems with it.

  1. When it scrolls, until the first image completely disappears, the ones above it do not appear, so the entire width of the picture has to completed, which does not make the scrolling seamless. That does not give a continuous scrolling effect as it leaves the screen blank until the first picture is completely gone.

{I thought I could use 2 image instances of the same image, and then reset it when the first one disappears, but I am unable to achieve any code for that as well. But honestly, I know we dont need 2 images for seamless scroll effect but just one instance which should continuously scroll.} How do I achieve the seamless scroll?

  1. When I stretch the app window like maximizing it, the picture does not stretch in width fitting the screen(as it scrolls vertically, we do not need to fit vertically but horizontally). While maintaining the aspect ratio of the image, is it possible for it to be stretched as per the size of the window, no matter how I resize it?

{Problem is, if I suddenly resize it, the entire scroll of the image gets reset and it again begins from the start, which should not happen.}

How do I get around this problem?

Kindly help on how I can workout these 2 issues? Do you think there is a way to fix them, so that the seamless(/infinite) scroll happens along with the picture fitting the width, and not the height?


Solution

  • You can do something like that by using two Image objects. Here is a modification of your App class that uses this approach:

    class ScrollingImageApp(App):
        def build(self):
            self.root = BoxLayout(orientation='vertical')
    
            # Add a FloatLayout to contain the scrolling Images
            fl = FloatLayout()
            self.upper_image = Image(source='pexels-tessa-k-896673.jpg', allow_stretch=True, keep_ratio=False)
            self.scroll_layer = Image(source='pexels-tessa-k-896673.jpg', allow_stretch=True, keep_ratio=False)
            fl.add_widget(self.upper_image)
            fl.add_widget(self.scroll_layer)
            self.root.add_widget(fl)
    
            # # Schedule the scroll function to be called every frame
            Clock.schedule_interval(self.scroll_image, 1 / 60.0)
    
            return self.root
    
        def scroll_image(self, dt):
            # Scroll the image vertically
            self.scroll_layer.y -= 1
            if self.scroll_layer.y < -self.scroll_layer.height:
                self.scroll_layer.y = 0  # reset to starting position
    
            # adjust the position of the upper Image
            self.upper_image.y = self.scroll_layer.y + self.scroll_layer.height
    

    Note that the scroll_image() method resets the y value scroll_layer (lower image) to 0 when the lower image gets scrolled off the screen. And it also positions the upper image to be right above the lower image.