[English]

Scroll(滚动)

Overview(概述)

显示原文

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

Any object can be scrollable including lv_obj, lv_image, lv_button, lv_meter, etc

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


在LVGL中,滚动的工作方式非常直观:如果一个对象超出了其父对象内容区域(不包括内边距的大小),那么其父对象就会变成可滚动的,并且会出现滚动条。就是这样。

任何对象都可以是可滚动的,包括 lv_obj, lv_image, lv_button, lv_meter 等。

对象可以在一个操作中水平或垂直滚动;无法进行对角线滚动。

Scrollbar(滚动条)

Mode(模式)

显示原文

Scrollbars are displayed according to a configured mode. The following mode(s) exist:

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


滚动条的显示方式根据配置的 mode 来确定。存在以下几种 mode

通过 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 turn to red like this:

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);

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

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

...

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

If the base direction of the LV_PART_SCROLLBAR is RTL (LV_BASE_DIR_RTL) the vertical scrollbar will be placed on the left. Note that, the base_dir style property is inherited. Therefore, it can be set directly on the LV_PART_SCROLLBAR part of an object or on the object's or any parent's main part to make a scrollbar inherit the base direction.

pad_left/right/top/bottom sets the spacing around the scrollbars and width sets the scrollbar's width.


滚动条有其专用的部分,称为 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);

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

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

...

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

如果 LV_PART_SCROLLBAR 的基本方向是RTL(LV_BASE_DIR_RTL) ,垂直滚动条将被放置在左侧。请注意, base_dir 样式属性是继承的。 因此,它可以直接在对象的 LV_PART_SCROLLBAR 部分上设置,或者在对象的或任何父级的主要部分上设置,以使滚动条继承基本方向。

pad_left/right/top/bottom 设置滚动条周围的间距, width 设置滚动条的宽度。

Events(事件)

显示原文

The following events are related to scrolling:

  • LV_EVENT_SCROLL_BEGIN: Scrolling begins. The event parameter is NULL or an lv_anim_t * with a scroll animation descriptor that can be modified if required.

  • LV_EVENT_SCROLL_END: Scrolling ends.

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


以下事件与滚动相关:

Basic example(基本示例)

TODO

Features of scrolling(滚动的特点)

显示原文

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

此外,除了管理“正常”滚动外,还有许多有趣和实用的额外功能。


Scrollable(滚动效果)

显示原文

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

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

The direction in which scrolling happens can be controlled by lv_obj_set_scroll_dir(obj, LV_DIR_...).

The following values are possible for the direction:

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


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

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

滚动的方向可以通过 lv_obj_set_scroll_dir(obj, LV_DIR_...) 来控制。

以下值可以用于指定方向:

也可以组合多个方向。比如:LV_DIR_TOP | LV_DIR_LEFT

Scroll chain(滚动条)

显示原文

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

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


如果一个对象无法进一步滚动(例如,其内容已经到达最底部位置),额外的滚动将传递给其父对象。如果在该方向上可以滚动父对象,则父对象将被滚动。它会继续传递给祖父和曾祖父对象。

滚动传播被称为“滚动链”,可以使用 LV_OBJ_FLAG_SCROLL_CHAIN_HOR/VER 标志来启用/禁用它。如果禁用了链式滚动,传播将在该对象处停止,并且父对象(们)将不会滚动。

Scroll momentum(滚动惯性效果)

显示原文

When the user scrolls an object and releases it, LVGL can emulate inertial 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 an object can't be scrolled past the extremities of its content. 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 is 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.

An object can align snapped children in four ways:

Snap alignment is 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_FLAG_SNAPPABLE 标志单独进行对齐。

一个对象可以以四种方式对齐被捕获的子项:

通过 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. This requires making the children snappable and setting a scroll snap alignment different from LV_SCROLL_SNAP_NONE.

This feature can be enabled by the LV_OBJ_FLAG_SCROLL_ONE flag.


“滚动一个”功能告诉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 a scrollable object. Pressing the "Tab" button focuses the next object but it might be outside the visible area of the scrollable object. If the "scroll on focus" feature is enabled LVGL will automatically scroll objects to bring their children into view. The scrolling happens recursively therefore even nested scrollable objects are handled properly. The object will be scrolled into view even if it's on a different page of a tabview.


想象一下,有很多物体在一个可滚动的对象上。按下“Tab”按钮会将焦点转移到下一个物体,但它可能超出了可滚动对象的可见区域。 如果启用了“焦点时滚动”功能,LVGL会自动滚动物体以使它们的子对象出现在视图中。滚动是递归进行的,因此即使是嵌套的可滚动对象也会被正确处理。 即使对象在选项卡视图的不同页面上,它也会被滚动到视图中。

Scroll manually(手动滚动)

显示原文

The following API functions allow manual scrolling of 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 top side

From time to time you may need to retrieve the scroll position of an element, either to restore it later, or to display dynamically some elements according to the current scroll. Here is an example to see how to combine scroll event and store the scroll top position.

static int scroll_value = 0;

static void store_scroll_value_event_cb(lv_event_t* e) {
  lv_obj_t* screen = lv_event_get_target(e);
  scroll_value = lv_obj_get_scroll_top(screen);
  printf("%d pixels are scrolled out on the top\n", scroll_value);
}

lv_obj_t* container = lv_obj_create(NULL);
lv_obj_add_event_cb(container, store_scroll_value_event_cb, LV_EVENT_SCROLL, NULL);

Scroll coordinates can be retrieved from different axes with these functions:

  • lv_obj_get_scroll_x(obj) Get the x coordinate of object

  • lv_obj_get_scroll_y(obj) Get the y coordinate of object

  • lv_obj_get_scroll_top(obj) Get the scroll coordinate from the top

  • lv_obj_get_scroll_bottom(obj) Get the scroll coordinate from the bottom

  • lv_obj_get_scroll_left(obj) Get the scroll coordinate from the left

  • lv_obj_get_scroll_right(obj) Get the scroll coordinate from the right


以下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) 滚动以将给定坐标带到顶部

有时您可能需要检索元素的滚动位置,以便稍后恢复它,或者根据当前滚动动态显示一些元素。以下是一个示例,介绍如何结合滚动事件并存储滚动顶部位置。

static int scroll_value = 0;

static void store_scroll_value_event_cb(lv_event_t* e) {
  lv_obj_t* screen = lv_event_get_target(e);
  scroll_value = lv_obj_get_scroll_top(screen);
  printf("向上滚动了%d像素\n", scroll_value);
}

lv_obj_t* container = lv_obj_create(NULL);
lv_obj_add_event_cb(container, store_scroll_value_event_cb, LV_EVENT_SCROLL, NULL);

使用这些函数可以从不同轴检索滚动坐标:

  • lv_obj_get_scroll_x(obj) 获取对象的 x 坐标

  • lv_obj_get_scroll_y(obj) 获取对象的 y 坐标

  • lv_obj_get_scroll_top(obj) 从顶部获取滚动坐标

  • lv_obj_get_scroll_bottom(obj) 从底部获取滚动坐标

  • lv_obj_get_scroll_left(obj) 从左侧获取滚动坐标

  • lv_obj_get_scroll_right(obj) 从右侧获取滚动坐标

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 establishes the size of an object's 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.

This means not only the children can make an object scrollable but a larger self size will 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:

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 need 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
  }
}

自身大小是一个对象的属性。通常情况下,用户不应该使用这个参数,但如果创建了一个自定义控件,它可能会很有用。

简而言之,自身大小确定了对象内容的大小。为了更好地理解,以表格为例。假设它有10行,每行高度为50像素。 因此,内容的总高度是500像素。换句话说,“自身高度”为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);

  //如果x或y < 0,则不需要立即计算
  if(p->x >= 0) {
    p->x = 200; //设置或计算自身宽度
  }

  if(p->y >= 0) {
    p->y = 50;  //设置或计算自身高度
  }
}

Examples

[English]

Nested scrolling

#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_screen_active());
    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_button_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

Snapping

#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_remove_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_screen_active());
    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_button_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_remove_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_screen_active());
    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_screen_active());
    lv_label_set_text(label, "One scroll");
    lv_obj_align_to(label, sw, LV_ALIGN_OUT_BOTTOM_MID, 0, 5);
#endif
}

#endif

Floating button

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

static uint32_t btn_cnt = 1;

static void float_button_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_button(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 with a floating button
 */
void lv_example_scroll_3(void)
{
    lv_obj_t * list = lv_list_create(lv_screen_active());
    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_button(list, LV_SYMBOL_AUDIO, buf);
    }

    lv_obj_t * float_btn = lv_button_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_button_event_cb, LV_EVENT_ALL, list);
    lv_obj_set_style_radius(float_btn, LV_RADIUS_CIRCLE, 0);
    lv_obj_set_style_bg_image_src(float_btn, LV_SYMBOL_PLUS, 0);
    lv_obj_set_style_text_font(float_btn, lv_theme_get_font_large(float_btn), 0);
}

#endif

Styling the scrollbars

#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_screen_active());
    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

Right to left scrolling

#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_screen_active());
    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

Translate on scroll

#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);
    int32_t cont_y_center = cont_a.y1 + lv_area_get_height(&cont_a) / 2;

    int32_t r = lv_obj_get_height(cont) * 7 / 10;
    uint32_t i;
    uint32_t child_cnt = lv_obj_get_child_count(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);

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

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

        /*Get the x of diff_y on a circle.*/
        int32_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_screen_active());
    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_button_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_obj_send_event(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

API

lv_obj_scroll.h