Scroll(滚动)

Overview(概述)

查看原文

In LVGL scrolling works very intuitively: if an object is out of its parent content area (the size without paddings), the parent becomes scrollable and scrollbar(s) will appear. That's it.

Any object can be scrollable including lv_obj_t, lv_img, lv_btn, lv_meter, etc

The object can either be scrolled either horizontally or vertically in one stroke; diagonal scrolling is not possible.

在 LVGL 中,滚动的工作非常直观:如果对象超出其父内容区域(没有填充的大小),则父对象的空间可滚动并且会出现滚动条。仅此而已。

任何对象都可以滚动,包括 lv_obj_tlv_imglv_btnlv_meter

对象可以一次水平或垂直滚动​​;对角滚动是不可能的。

Scrollbar(滚动条)

Mode(模式)

查看原文

The scrollbars are displayed according to the set mode. The following modes exist:

  • LV_SCROLLBAR_MODE_OFF Never show the scrollbars

  • LV_SCROLLBAR_MODE_ON Always show the scrollbars

  • LV_SCROLLBAR_MODE_ACTIVE Show scroll bars while object is being scrolled

  • LV_SCROLLBAR_MODE_AUTO Show scroll bars when the content is large enough to be scrolled

lv_obj_set_scrollbar_mode(obj, LV_SCROLLBAR_MODE_...) set the scrollbar mode on an object.

滚动条根据设置的 模式 显示。有下面这几种 模式 可以选择:

  • LV_SCROLLBAR_​​MODE_OFF 从不显示滚动条

  • LV_SCROLLBAR_​​MODE_ON 始终显示滚动条

  • LV_SCROLLBAR_​​MODE_ACTIVE 在对象滚动时显示滚动条

  • LV_SCROLLBAR_​​MODE_AUTO 当内容足够大可以滚动时显示滚动条

lv_obj_set_scrollbar_mode(obj, LV_SCROLLBAR_​​MODE_...) 在对象上设置滚动条模式。

Styling(样式)

查看原文

The scrollbars have their own dedicated part, called LV_PART_SCROLLBAR. For example a scrollbar can turned to red like this:

滚动条有自己的专用部分,称为 LV_PART_SCROLLBAR 。例如,滚动条可以像这样变成红色:

static lv_style_t style_red;
lv_style_init(&style_red);
lv_style_set_bg_color(&style_red, lv_color_red());

...

lv_obj_add_style(obj, &style_red, LV_PART_SCROLLBAR);
查看原文

The object goes to LV_STATE_SCROLLED state while it's being scrolled. It allows adding different style to the scrollbar or the object itself when scrolled. This code makes the scrollbar blue when the object is scrolled:

对象在滚动时进入 LV_STATE_SCROLLED 状态。它允许在滚动时向滚动条或对象本身添加不同的样式。 当对象滚动时,此代码使滚动条变为蓝色:

static lv_style_t style_blue;
lv_style_init(&style_blue);
lv_style_set_bg_color(&style_red, lv_color_blue());

...

lv_obj_add_style(obj, &style_blue, LV_STATE_SCROLLED | LV_PART_SCROLLBAR);

Events(事件)

查看原文

The following events are related to scrolling:

  • LV_EVENT_SCROLL_BEGIN Scrolling begins

  • LV_EVENT_SCROLL_END Scrolling ends

  • LV_EVENT_SCROLL Scroll happened. Triggered on every position change. Scroll events

以下事件与滚动相关:

  • LV_EVENT_SCROLL_BEGIN 开始滚动

  • LV_EVENT_SCROLL_END 滚动结束

  • LV_EVENT_SCROLL 滚动发生。每次位置变化时触发。

滚动事件

Basic example(基本示例)

TODO

Features of scrolling(滚动的特点)

查看原文

Besides managing "normal" scrolling there are many interesting and useful additional features too.

除了管理 “正常” 滚动之外,还有许多有趣且有用的附加功能。

Scrollable(滚动效果)

查看原文

It's possible to make an object non-scrollable with lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE).

Non-scrollable object can still propagate the scrolling (chain) to the parents.

The direction in which scrolling can happen can be controlled by lv_obj_set_scroll_dir(obj, LV_DIR_...). The following values are possible for the direction:

  • LV_DIR_TOP only scroll up

  • LV_DIR_LEFT only scroll left

  • LV_DIR_BOTTOM only scroll down

  • LV_DIR_RIGHT only scroll right

  • LV_DIR_HOR only scroll horizontally

  • LV_DIR_VER only scroll vertically

  • LV_DIR_ALL scroll any directions

OR-ed values are also possible. E.g. LV_DIR_TOP | LV_DIR_LEFT.

可以使用 lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE) 使对象不可滚动。

不可滚动对象仍然可以将滚动(链)传播到父对象。

滚动发生的方向可以由lv_obj_set_scroll_dir(obj, LV_DIR_...)控制。 方向可能有以下值:

  • LV_DIR_TOP 只向上滚动

  • LV_DIR_LEFT 只向左滚动

  • LV_DIR_BOTTOM 只向下滚动

  • LV_DIR_RIGHT 只向右滚动

  • LV_DIR_HOR 只能水平滚动

  • LV_DIR_VER 只能垂直滚动

  • LV_DIR_ALL 滚动任何方向

可以用同时设置使用多个值(OR-ed)。例如。 LV_DIR_TOP | LV_DIR_LEFT

Scroll chain(滚动条)

查看原文

If an object can't be scrolled further (e.g. it's content has reached the bottom most position) the scrolling is propagated to it's parent. If the parent an be scrolled in that direction than it will be scrolled instead. It propagets to the grandparent and grand-grandparents too.

The propagation on scrolling is called "scroll chaining" and it can be enabled/disabled with the LV_OBJ_FLAG_SCROLL_CHAIN flag. If chaining is disabled the propagation stops on the object and the parent(s) won't be scrolled.

如果对象无法进一步滚动(例如,它的内容已到达最底部的位置),则滚动将传播到其父级。如果父级在该方向上滚动,则它将改为滚动。 它也传播给祖父母和祖父母。

滚动传播称为 “滚动链接” ,可以使用 LV_OBJ_FLAG_SCROLL_CHAIN 标志启用/禁用。 如果禁用链接,则传播将停止在对象上,并且不会滚动父对象。

Scroll momentum(滚动惯性效果)

查看原文

When the user scrolls an object and releases it, LVGL can emulate a momentum for the scrolling. It's like the object was thrown and scrolling slows down smoothly.

The scroll momentum can be enabled/disabled with the LV_OBJ_FLAG_SCROLL_MOMENTUM flag.

当用户滚动对象并释放它时,LVGL 可以模拟滚动的动量。就好像对象被抛出并且滚动平稳地减慢了速度。

可以使用 “LV_OBJ_FLAG_SCROLL_MOMENTUM” 标志启用/禁用滚动动量。

Elastic scroll(弹性卷轴效果)

查看原文

Normally the content can't be scrolled inside the object. That is the top side of the content can't be below the top side of the object.

However, with LV_OBJ_FLAG_SCROLL_ELASTIC a fancy effect can be added when the user "over-scrolls" the content. The scrolling slows down, and the content can be scrolled inside the object. When the object is released the content scrolled in it will be animated back to the valid position.

通常内容不能在对象内滚动。即内容的顶部不能低于对象的顶部。

但是,使用LV_OBJ_FLAG_SCROLL_ELASTIC 可以在用户“滚动”内容时添加奇特的效果。滚动变慢,内容可以在对象内部滚动。 当对象被释放时,滚动到其中的内容将动画回到有效位置。

Snapping(捕捉)

查看原文

The children of an object can be snapped according to specific rules when scrolling ends. Children can be made snappable individually with the LV_OBJ_FLAG_SNAPPABLE flag.

The object can align the snapped children in 4 ways:

  • LV_SCROLL_SNAP_NONE Snapping is disabled. (default)

  • LV_SCROLL_SNAP_START Align the children to the left/top side of the scrolled object

  • LV_SCROLL_SNAP_END Align the children to the right/bottom side of the scrolled object

  • LV_SCROLL_SNAP_CENTER Align the children to the center of the scrolled object

滚动结束时,可以根据特定规则捕捉对象的子项。可以使用 LV_OBJ_FLAG_SNAPPABLE 标志将子对象单独设置为可捕捉。

该对象可以通过 4 种方式对齐对齐的子项:

  • LV_SCROLL_SNAP_NONE 捕捉被禁用。 (默认)

  • LV_SCROLL_SNAP_START 将子对象与滚动对象的左侧/顶部对齐

  • LV_SCROLL_SNAP_END 将子对象与滚动对象的右侧/底部对齐

  • LV_SCROLL_SNAP_CENTER 将子对象与滚动对象的中心对齐

查看原文

The alignment can be set with lv_obj_set_scroll_snap_x/y(obj, LV_SCROLL_SNAP_...):

Under the hood the following happens:

  1. User scrolls an object and releases the screen

  2. LVGL calculates where the scroll would end considering scroll momentum

  3. LVGL finds the nearest scroll point

  4. LVGL scrolls to the snap point with an animation

对齐可以用lv_obj_set_scroll_snap_x/y(obj, LV_SCROLL_SNAP_...)设置:

底层代码会发生以下情况:

  1. 用户滚动对象并释放屏幕

  2. LVGL 考虑滚动动量计算滚动结束的位置

  3. LVGL 寻找最近的滚动点

  4. LVGL 滚动到带动画的捕捉点

Scroll one(只滚动一个)

查看原文

The "scroll one" feature tells LVGL to allow scrolling only one snappable child at a time. So this requires to make the children snappable and set a scroll snap alignment different from LV_SCROLL_SNAP_NONE.

This feature can be enabled by the LV_OBJ_FLAG_SCROLL_ONE flag.

“只滚动一个(Scroll one)”功能告诉 LVGL 一次只允许滚动一个可捕捉的孩子。

因此,这需要使子项可捕捉并设置与 LV_SCROLL_SNAP_NONE 不同的滚动对齐对齐方式。 此功能可以通过 LV_OBJ_FLAG_SCROLL_ONE 标志启用。

Scroll on focus(滚动焦点)

查看原文

Imagine that there a lot of objects in a group that are on scrollable object. Pressing the "Tab" button focuses the next object but it might be out of the visible area of the scrollable object. If the "scroll on focus" features is enabled LVGL will automatically scroll to the objects to bring the children into the view. The scrolling happens recursively therefore even nested scrollable object are handled properly. The object will be scrolled to the view even if it's on a different page of a tabview.

想象一下,一个组中有很多对象位于可滚动对象上。按 “Tab” 按钮聚焦下一个对象,但它可能超出可滚动对象的可见区域。 如果启用了“焦点滚动”功能,LVGL 将自动滚动到对象以将子项带入视图。

滚动以递归方式发生,因此即使嵌套的可滚​​动对象也能得到正确处理。 即使对象位于 tabview 的不同页面上,它也会滚动到视图。

Scroll manually

查看原文

The following API functions allow to manually scroll objects:

  • lv_obj_scroll_by(obj, x, y, LV_ANIM_ON/OFF) scroll by x and y values

  • lv_obj_scroll_to(obj, x, y, LV_ANIM_ON/OFF) scroll to bring the given coordinate to the top left corner

  • lv_obj_scroll_to_x(obj, x, LV_ANIM_ON/OFF) scroll to bring the given coordinate to the left side

  • lv_obj_scroll_to_y(obj, y, LV_ANIM_ON/OFF) scroll to bring the given coordinate to the left side

以下 API 函数允许手动滚动对象:

  • lv_obj_scroll_by(obj, x, y, LV_ANIM_ON/OFF)xy 值滚动

  • lv_obj_scroll_to(obj, x, y, LV_ANIM_ON/OFF) 滚动以将给定的坐标带到左上角

  • lv_obj_scroll_to_x(obj, x, LV_ANIM_ON/OFF) 滚动以将给定的坐标带到左侧

  • lv_obj_scroll_to_y(obj, y, LV_ANIM_ON/OFF) 滚动以将给定的坐标带到左侧

Self size(自身尺寸)

查看原文

Self size is a property of an object. Normally, the user shouldn't use this parameter but if a custom widget is created it might be useful.

In short, self size tell the size of the content. To understand it better take the example of a table. Let's say it has 10 rows each with 50 px height. So the total height of the content is 500 px. In other words the "self height" is 500 px. If the user sets only 200 px height for the table LVGL will see that the self size is larger and make the table scrollable.

It means not only the children can make an object scrollable but a larger self size too.

LVGL uses the LV_EVENT_GET_SELF_SIZE event to get the self size of an object. Here is an example to see how to handle the event

自身大小是对象的属性。通常,用户不应使用此参数,但如果创建了自定义小、部件,它可能会很有用。

简而言之,自我大小告诉内容的大小。为了更好地理解它,举一个表格的例子。 假设它有 10 行,每行 50 像素高度。所以内容的总高度是 500 px。换句话说,“自身高度”是 500 像素。 如果用户只为表格设置 200 像素高度,LVGL 将看到自身尺寸更大并使表格可滚动。

这意味着不仅孩子们可以使对象可滚动,而且还可以使自身尺寸更大。

LVGL 使用 LV_EVENT_GET_SELF_SIZE 事件来获取对象的自身大小。下面是一个例子,看看如何处理事件

if(event_code == LV_EVENT_GET_SELF_SIZE) {
	lv_point_t * p = lv_event_get_param(e);
  
  //If x or y < 0 then it doesn't neesd to be calculated now
  if(p->x >= 0) {
    p->x = 200;	//Set or calculate the self width
  }
  
  if(p->y >= 0) {
    p->y = 50;	//Set or calculate the self height
  }
}

Examples

Nested scrolling

C code  

 GitHub
#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES

/**
 * Demonstrate how scrolling appears automatically
 */
void lv_example_scroll_1(void)
{
    /*Create an object with the new style*/
    lv_obj_t * panel = lv_obj_create(lv_scr_act());
    lv_obj_set_size(panel, 200, 200);
    lv_obj_center(panel);

    lv_obj_t * child;
    lv_obj_t * label;

    child = lv_obj_create(panel);
    lv_obj_set_pos(child, 0, 0);
    lv_obj_set_size(child, 70, 70);
    label = lv_label_create(child);
    lv_label_set_text(label, "Zero");
    lv_obj_center(label);

    child = lv_obj_create(panel);
    lv_obj_set_pos(child, 160, 80);
    lv_obj_set_size(child, 80, 80);

    lv_obj_t * child2 = lv_btn_create(child);
    lv_obj_set_size(child2, 100, 50);

    label = lv_label_create(child2);
    lv_label_set_text(label, "Right");
    lv_obj_center(label);

    child = lv_obj_create(panel);
    lv_obj_set_pos(child, 40, 160);
    lv_obj_set_size(child, 100, 70);
    label = lv_label_create(child);
    lv_label_set_text(label, "Bottom");
    lv_obj_center(label);
}

#endif

MicroPython code  

 GitHub Simulator
#
# Demonstrate how scrolling appears automatically
#
# Create an object with the new style
panel = lv.obj(lv.scr_act())
panel.set_size(200, 200)
panel.center()

child = lv.obj(panel)
child.set_pos(0, 0)
label = lv.label(child)
label.set_text("Zero")
label.center()

child = lv.obj(panel)
child.set_pos(-40, 100)
label = lv.label(child)
label.set_text("Left")
label.center()

child = lv.obj(panel)
child.set_pos(90, -30)
label = lv.label(child)
label.set_text("Top")
label.center()

child = lv.obj(panel)
child.set_pos(150, 80)
label = lv.label(child)
label.set_text("Right")
label.center()

child = lv.obj(panel)
child.set_pos(60, 170)
label = lv.label(child)
label.set_text("Bottom")
label.center()


Snapping

C code  

 GitHub
#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_FLEX

static void sw_event_cb(lv_event_t * e)
{
    lv_event_code_t code = lv_event_get_code(e);
    lv_obj_t * sw = lv_event_get_target(e);

    if(code == LV_EVENT_VALUE_CHANGED) {
        lv_obj_t * list = lv_event_get_user_data(e);

        if(lv_obj_has_state(sw, LV_STATE_CHECKED)) lv_obj_add_flag(list, LV_OBJ_FLAG_SCROLL_ONE);
        else lv_obj_clear_flag(list, LV_OBJ_FLAG_SCROLL_ONE);
    }
}

/**
 * Show an example to scroll snap
 */
void lv_example_scroll_2(void)
{
    lv_obj_t * panel = lv_obj_create(lv_scr_act());
    lv_obj_set_size(panel, 280, 120);
    lv_obj_set_scroll_snap_x(panel, LV_SCROLL_SNAP_CENTER);
    lv_obj_set_flex_flow(panel, LV_FLEX_FLOW_ROW);
    lv_obj_align(panel, LV_ALIGN_CENTER, 0, 20);

    uint32_t i;
    for(i = 0; i < 10; i++) {
        lv_obj_t * btn = lv_btn_create(panel);
        lv_obj_set_size(btn, 150, lv_pct(100));

        lv_obj_t * label = lv_label_create(btn);
        if(i == 3) {
            lv_label_set_text_fmt(label, "Panel %"LV_PRIu32"\nno snap", i);
            lv_obj_clear_flag(btn, LV_OBJ_FLAG_SNAPPABLE);
        } else {
            lv_label_set_text_fmt(label, "Panel %"LV_PRIu32, i);
        }

        lv_obj_center(label);
    }
    lv_obj_update_snap(panel, LV_ANIM_ON);

#if LV_USE_SWITCH
    /*Switch between "One scroll" and "Normal scroll" mode*/
    lv_obj_t * sw = lv_switch_create(lv_scr_act());
    lv_obj_align(sw, LV_ALIGN_TOP_RIGHT, -20, 10);
    lv_obj_add_event_cb(sw, sw_event_cb, LV_EVENT_ALL, panel);
    lv_obj_t * label = lv_label_create(lv_scr_act());
    lv_label_set_text(label, "One scroll");
    lv_obj_align_to(label, sw, LV_ALIGN_OUT_BOTTOM_MID, 0, 5);
#endif
}

#endif

MicroPython code  

 GitHub Simulator
def sw_event_cb(e,panel):

    code = e.get_code()
    sw = e.get_target()

    if code == lv.EVENT.VALUE_CHANGED:

        if sw.has_state(lv.STATE.CHECKED):
            panel.add_flag(lv.obj.FLAG.SCROLL_ONE)
        else:
            panel.clear_flag(lv.obj.FLAG.SCROLL_ONE)


#
# Show an example to scroll snap
#

panel = lv.obj(lv.scr_act())
panel.set_size(280, 150)
panel.set_scroll_snap_x(lv.SCROLL_SNAP.CENTER)
panel.set_flex_flow(lv.FLEX_FLOW.ROW)
panel.center()

for i in range(10): 
    btn = lv.btn(panel)
    btn.set_size(150, 100)

    label = lv.label(btn)
    if i == 3:
        label.set_text("Panel {:d}\nno snap".format(i))
        btn.clear_flag(lv.obj.FLAG.SNAPPABLE)
    else:
        label.set_text("Panel {:d}".format(i))
    label.center()

panel.update_snap(lv.ANIM.ON)


# Switch between "One scroll" and "Normal scroll" mode
sw = lv.switch(lv.scr_act())
sw.align(lv.ALIGN.TOP_RIGHT, -20, 10)
sw.add_event_cb(lambda evt:  sw_event_cb(evt,panel), lv.EVENT.ALL, None)
label = lv.label(lv.scr_act())
label.set_text("One scroll")
label.align_to(sw, lv.ALIGN.OUT_BOTTOM_MID, 0, 5)



Floating button

C code  

 GitHub
#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_LIST

static uint32_t btn_cnt = 1;

static void float_btn_event_cb(lv_event_t * e)
{
    lv_event_code_t code = lv_event_get_code(e);
    lv_obj_t * float_btn = lv_event_get_target(e);

    if(code == LV_EVENT_CLICKED) {
        lv_obj_t * list = lv_event_get_user_data(e);
        char buf[32];
        lv_snprintf(buf, sizeof(buf), "Track %d", (int)btn_cnt);
        lv_obj_t * list_btn = lv_list_add_btn(list, LV_SYMBOL_AUDIO, buf);
        btn_cnt++;

        lv_obj_move_foreground(float_btn);

        lv_obj_scroll_to_view(list_btn, LV_ANIM_ON);
    }
}

/**
 * Create a list a with a floating button
 */
void lv_example_scroll_3(void)
{
    lv_obj_t * list = lv_list_create(lv_scr_act());
    lv_obj_set_size(list, 280, 220);
    lv_obj_center(list);

    for(btn_cnt = 1; btn_cnt <= 2; btn_cnt++) {
        char buf[32];
        lv_snprintf(buf, sizeof(buf), "Track %d", (int)btn_cnt);
        lv_list_add_btn(list, LV_SYMBOL_AUDIO, buf);
    }

    lv_obj_t * float_btn = lv_btn_create(list);
    lv_obj_set_size(float_btn, 50, 50);
    lv_obj_add_flag(float_btn, LV_OBJ_FLAG_FLOATING);
    lv_obj_align(float_btn, LV_ALIGN_BOTTOM_RIGHT, 0, -lv_obj_get_style_pad_right(list, LV_PART_MAIN));
    lv_obj_add_event_cb(float_btn, float_btn_event_cb, LV_EVENT_ALL, list);
    lv_obj_set_style_radius(float_btn, LV_RADIUS_CIRCLE, 0);
    lv_obj_set_style_bg_img_src(float_btn, LV_SYMBOL_PLUS, 0);
    lv_obj_set_style_text_font(float_btn, lv_theme_get_font_large(float_btn), 0);
}

#endif

MicroPython code  

 GitHub Simulator
class ScrollExample_3():
    def __init__(self):
        self.btn_cnt = 1
        #
        # Create a list a with a floating button
        #

        list = lv.list(lv.scr_act())
        list.set_size(280, 220)
        list.center()

        for btn_cnt in range(2):
            list.add_btn(lv.SYMBOL.AUDIO,"Track {:d}".format(btn_cnt))

        float_btn = lv.btn(list)
        float_btn.set_size(50, 50)
        float_btn.add_flag(lv.obj.FLAG.FLOATING)
        float_btn.align(lv.ALIGN.BOTTOM_RIGHT, 0, -list.get_style_pad_right(lv.PART.MAIN))
        float_btn.add_event_cb(lambda evt: self.float_btn_event_cb(evt,list), lv.EVENT.ALL, None)
        float_btn.set_style_radius(lv.RADIUS.CIRCLE, 0)
        float_btn.set_style_bg_img_src(lv.SYMBOL.PLUS, 0)
        float_btn.set_style_text_font(lv.theme_get_font_large(float_btn), 0)
            
    def float_btn_event_cb(self,e,list):
        code = e.get_code()
        float_btn = e.get_target()

        if code == lv.EVENT.CLICKED:
            list_btn = list.add_btn(lv.SYMBOL.AUDIO, "Track {:d}".format(self.btn_cnt))
            self.btn_cnt += 1

            float_btn.move_foreground()

            list_btn.scroll_to_view(lv.ANIM.ON)
            
scroll_example_3 = ScrollExample_3()



Styling the scrollbars

C code  

 GitHub
#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_LIST


/**
 * Styling the scrollbars
 */
void lv_example_scroll_4(void)
{
    lv_obj_t * obj = lv_obj_create(lv_scr_act());
    lv_obj_set_size(obj, 200, 100);
    lv_obj_center(obj);

    lv_obj_t * label = lv_label_create(obj);
    lv_label_set_text(label,
            "Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n"
            "Etiam dictum, tortor vestibulum lacinia laoreet, mi neque consectetur neque, vel mattis odio dolor egestas ligula. \n"
            "Sed vestibulum sapien nulla, id convallis ex porttitor nec. \n"
            "Duis et massa eu libero accumsan faucibus a in arcu. \n"
            "Ut pulvinar odio lorem, vel tempus turpis condimentum quis. Nam consectetur condimentum sem in auctor. \n"
            "Sed nisl augue, venenatis in blandit et, gravida ac tortor. \n"
            "Etiam dapibus elementum suscipit. \n"
            "Proin mollis sollicitudin convallis. \n"
            "Integer dapibus tempus arcu nec viverra. \n"
            "Donec molestie nulla enim, eu interdum velit placerat quis. \n"
            "Donec id efficitur risus, at molestie turpis. \n"
            "Suspendisse vestibulum consectetur nunc ut commodo. \n"
            "Fusce molestie rhoncus nisi sit amet tincidunt. \n"
            "Suspendisse a nunc ut magna ornare volutpat.");


    /*Remove the style of scrollbar to have clean start*/
    lv_obj_remove_style(obj, NULL, LV_PART_SCROLLBAR | LV_STATE_ANY);

    /*Create a transition the animate the some properties on state change*/
    static const lv_style_prop_t props[] = {LV_STYLE_BG_OPA, LV_STYLE_WIDTH, 0};
    static lv_style_transition_dsc_t trans;
    lv_style_transition_dsc_init(&trans, props, lv_anim_path_linear, 200, 0, NULL);

    /*Create a style for the scrollbars*/
    static lv_style_t style;
    lv_style_init(&style);
    lv_style_set_width(&style, 4);      /*Width of the scrollbar*/
    lv_style_set_pad_right(&style, 5);  /*Space from the parallel side*/
    lv_style_set_pad_top(&style, 5);    /*Space from the perpendicular side*/

    lv_style_set_radius(&style, 2);
    lv_style_set_bg_opa(&style, LV_OPA_70);
    lv_style_set_bg_color(&style, lv_palette_main(LV_PALETTE_BLUE));
    lv_style_set_border_color(&style, lv_palette_darken(LV_PALETTE_BLUE, 3));
    lv_style_set_border_width(&style, 2);
    lv_style_set_shadow_width(&style, 8);
    lv_style_set_shadow_spread(&style, 2);
    lv_style_set_shadow_color(&style, lv_palette_darken(LV_PALETTE_BLUE, 1));

    lv_style_set_transition(&style, &trans);

    /*Make the scrollbars wider and use 100% opacity when scrolled*/
    static lv_style_t style_scrolled;
    lv_style_init(&style_scrolled);
    lv_style_set_width(&style_scrolled, 8);
    lv_style_set_bg_opa(&style_scrolled, LV_OPA_COVER);

    lv_obj_add_style(obj, &style, LV_PART_SCROLLBAR);
    lv_obj_add_style(obj, &style_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED);
}

#endif

MicroPython code  

 GitHub Simulator
#
# Styling the scrollbars
#
obj = lv.obj(lv.scr_act())
obj.set_size(200, 100)
obj.center()

label = lv.label(obj)
label.set_text(
"""
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Etiam dictum, tortor vestibulum lacinia laoreet, mi neque consectetur neque, vel mattis odio dolor egestas ligula.
Sed vestibulum sapien nulla, id convallis ex porttitor nec.
Duis et massa eu libero accumsan faucibus a in arcu.
Ut pulvinar odio lorem, vel tempus turpis condimentum quis. Nam consectetur condimentum sem in auctor.
Sed nisl augue, venenatis in blandit et, gravida ac tortor.
Etiam dapibus elementum suscipit.
Proin mollis sollicitudin convallis.
Integer dapibus tempus arcu nec viverra.
Donec molestie nulla enim, eu interdum velit placerat quis.
Donec id efficitur risus, at molestie turpis.
Suspendisse vestibulum consectetur nunc ut commodo.
Fusce molestie rhoncus nisi sit amet tincidunt.
Suspendisse a nunc ut magna ornare volutpat.
""")


# Remove the style of scrollbar to have clean start
obj.remove_style(None, lv.PART.SCROLLBAR | lv.STATE.ANY)

# Create a transition the animate the some properties on state change
props = [lv.STYLE.BG_OPA, lv.STYLE.WIDTH, 0]
trans = lv.style_transition_dsc_t()
trans.init(props, lv.anim_t.path_linear, 200, 0, None)

# Create a style for the scrollbars
style = lv.style_t()
style.init()
style.set_width(4)               # Width of the scrollbar
style.set_pad_right(5)           # Space from the parallel side
style.set_pad_top(5)             # Space from the perpendicular side

style.set_radius(2)
style.set_bg_opa(lv.OPA._70)
style.set_bg_color(lv.palette_main(lv.PALETTE.BLUE))
style.set_border_color(lv.palette_darken(lv.PALETTE.BLUE, 3))
style.set_border_width(2)
style.set_shadow_width(8)
style.set_shadow_spread(2)
style.set_shadow_color(lv.palette_darken(lv.PALETTE.BLUE, 1))

style.set_transition(trans)

# Make the scrollbars wider and use 100% opacity when scrolled
style_scrolled = lv.style_t()
style_scrolled.init()
style_scrolled.set_width(8)
style_scrolled.set_bg_opa(lv.OPA.COVER)

obj.add_style(style, lv.PART.SCROLLBAR)
obj.add_style(style_scrolled, lv.PART.SCROLLBAR | lv.STATE.SCROLLED)


Right to left scrolling

C code  

 GitHub
#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_FONT_DEJAVU_16_PERSIAN_HEBREW


/**
 * Scrolling with Right To Left base direction
 */
void lv_example_scroll_5(void)
{
    lv_obj_t * obj = lv_obj_create(lv_scr_act());
    lv_obj_set_style_base_dir(obj, LV_BASE_DIR_RTL, 0);
    lv_obj_set_size(obj, 200, 100);
    lv_obj_center(obj);

    lv_obj_t * label = lv_label_create(obj);
    lv_label_set_text(label,"میکروکُنترولر (به انگلیسی: Microcontroller) گونه‌ای ریزپردازنده است که دارای حافظهٔ دسترسی تصادفی (RAM) و حافظهٔ فقط‌خواندنی (ROM)، تایمر، پورت‌های ورودی و خروجی (I/O) و درگاه ترتیبی (Serial Port پورت سریال)، درون خود تراشه است، و می‌تواند به تنهایی ابزارهای دیگر را کنترل کند. به عبارت دیگر یک میکروکنترلر، مدار مجتمع کوچکی است که از یک CPU کوچک و اجزای دیگری مانند تایمر، درگاه‌های ورودی و خروجی آنالوگ و دیجیتال و حافظه تشکیل شده‌است.");
    lv_obj_set_width(label, 400);
    lv_obj_set_style_text_font(label, &lv_font_dejavu_16_persian_hebrew, 0);

}

#endif

MicroPython code  

 GitHub Simulator
#
# Scrolling with Right To Left base direction
#
obj = lv.obj(lv.scr_act())
obj.set_style_base_dir(lv.BASE_DIR.RTL, 0)
obj.set_size(200, 100)
obj.center()

label = lv.label(obj)
label.set_text("میکروکُنترولر (به انگلیسی: Microcontroller) گونه‌ای ریزپردازنده است که دارای حافظهٔ دسترسی تصادفی (RAM) و حافظهٔ فقط‌خواندنی (ROM)، تایمر، پورت‌های ورودی و خروجی (I/O) و درگاه ترتیبی (Serial Port پورت سریال)، درون خود تراشه است، و می‌تواند به تنهایی ابزارهای دیگر را کنترل کند. به عبارت دیگر یک میکروکنترلر، مدار مجتمع کوچکی است که از یک CPU کوچک و اجزای دیگری مانند تایمر، درگاه‌های ورودی و خروجی آنالوگ و دیجیتال و حافظه تشکیل شده‌است.")
label.set_width(400)
label.set_style_text_font(lv.font_dejavu_16_persian_hebrew, 0)


Translate on scroll

C code  

 GitHub
#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_FLEX

static void scroll_event_cb(lv_event_t * e)
{
    lv_obj_t * cont = lv_event_get_target(e);

    lv_area_t cont_a;
    lv_obj_get_coords(cont, &cont_a);
    lv_coord_t cont_y_center = cont_a.y1 + lv_area_get_height(&cont_a) / 2;

    lv_coord_t r = lv_obj_get_height(cont) * 7 / 10;
    uint32_t i;
    uint32_t child_cnt = lv_obj_get_child_cnt(cont);
    for(i = 0; i < child_cnt; i++) {
        lv_obj_t * child = lv_obj_get_child(cont, i);
        lv_area_t child_a;
        lv_obj_get_coords(child, &child_a);

        lv_coord_t child_y_center = child_a.y1 + lv_area_get_height(&child_a) / 2;

        lv_coord_t diff_y = child_y_center - cont_y_center;
        diff_y = LV_ABS(diff_y);

        /*Get the x of diff_y on a circle.*/
        lv_coord_t x;
        /*If diff_y is out of the circle use the last point of the circle (the radius)*/
        if(diff_y >= r) {
            x = r;
        } else {
            /*Use Pythagoras theorem to get x from radius and y*/
            uint32_t x_sqr = r * r - diff_y * diff_y;
            lv_sqrt_res_t res;
            lv_sqrt(x_sqr, &res, 0x8000);   /*Use lvgl's built in sqrt root function*/
            x = r - res.i;
        }

        /*Translate the item by the calculated X coordinate*/
        lv_obj_set_style_translate_x(child, x, 0);

        /*Use some opacity with larger translations*/
        lv_opa_t opa = lv_map(x, 0, r, LV_OPA_TRANSP, LV_OPA_COVER);
        lv_obj_set_style_opa(child, LV_OPA_COVER - opa, 0);
    }
}

/**
 * Translate the object as they scroll
 */
void lv_example_scroll_6(void)
{
    lv_obj_t * cont = lv_obj_create(lv_scr_act());
    lv_obj_set_size(cont, 200, 200);
    lv_obj_center(cont);
    lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN);
    lv_obj_add_event_cb(cont, scroll_event_cb, LV_EVENT_SCROLL, NULL);
    lv_obj_set_style_radius(cont, LV_RADIUS_CIRCLE, 0);
    lv_obj_set_style_clip_corner(cont, true, 0);
    lv_obj_set_scroll_dir(cont, LV_DIR_VER);
    lv_obj_set_scroll_snap_y(cont, LV_SCROLL_SNAP_CENTER);
    lv_obj_set_scrollbar_mode(cont, LV_SCROLLBAR_MODE_OFF);

    uint32_t i;
    for(i = 0; i < 20; i++) {
        lv_obj_t * btn = lv_btn_create(cont);
        lv_obj_set_width(btn, lv_pct(100));

        lv_obj_t * label = lv_label_create(btn);
        lv_label_set_text_fmt(label, "Button %"LV_PRIu32, i);
    }

    /*Update the buttons position manually for first*/
    lv_event_send(cont, LV_EVENT_SCROLL, NULL);

    /*Be sure the fist button is in the middle*/
    lv_obj_scroll_to_view(lv_obj_get_child(cont, 0), LV_ANIM_OFF);
}

#endif

MicroPython code  

 GitHub Simulator
def scroll_event_cb(e):

    cont = e.get_target()

    cont_a = lv.area_t()
    cont.get_coords(cont_a)
    cont_y_center = cont_a.y1 + cont_a.get_height() // 2

    r = cont.get_height() * 7 // 10
    
    child_cnt = cont.get_child_cnt()
    for i in range(child_cnt):
        child = cont.get_child(i)
        child_a = lv.area_t()
        child.get_coords(child_a)

        child_y_center = child_a.y1 + child_a.get_height() // 2

        diff_y = child_y_center - cont_y_center
        diff_y = abs(diff_y)

        # Get the x of diff_y on a circle.
        
        # If diff_y is out of the circle use the last point of the circle (the radius)
        if diff_y >= r:
            x = r
        else:
            # Use Pythagoras theorem to get x from radius and y
            x_sqr = r * r - diff_y * diff_y
            res = lv.sqrt_res_t()
            lv.sqrt(x_sqr, res, 0x8000)   # Use lvgl's built in sqrt root function
            x = r - res.i

        # Translate the item by the calculated X coordinate
        child.set_style_translate_x(x, 0)

        # Use some opacity with larger translations
        opa = lv.map(x, 0, r, lv.OPA.TRANSP, lv.OPA.COVER)
        child.set_style_opa(lv.OPA.COVER - opa, 0)

#
# Translate the object as they scroll
#

cont = lv.obj(lv.scr_act())
cont.set_size(200, 200)
cont.center()
cont.set_flex_flow(lv.FLEX_FLOW.COLUMN)
cont.add_event_cb(scroll_event_cb, lv.EVENT.SCROLL, None)
cont.set_style_radius(lv.RADIUS.CIRCLE, 0)
cont.set_style_clip_corner(True, 0)
cont.set_scroll_dir(lv.DIR.VER)
cont.set_scroll_snap_y(lv.SCROLL_SNAP.CENTER)
cont.set_scrollbar_mode(lv.SCROLLBAR_MODE.OFF)

for i in range(20):
    btn = lv.btn(cont)
    btn.set_width(lv.pct(100))
    
    label = lv.label(btn)
    label.set_text("Button " + str(i))

    # Update the buttons position manually for first*
    lv.event_send(cont, lv.EVENT.SCROLL, None)

    # Be sure the fist button is in the middle
    #lv.obj.scroll_to_view(cont.get_child(0), lv.ANIM.OFF)
    cont.get_child(0).scroll_to_view(lv.ANIM.OFF)