☰
LVGL页面切换动画lv_scr_load_anim实战:从参数配置到内存优化
2026/9/27 6:26:16 网站建设 项目流程

1. 为什么页面切换要单独做动画处理

先说一个很多朋友问过的问题:在STM32这类MCU上跑LVGL,页面切换不就是调用lv_scr_load()换屏吗?为什么要用lv_scr_load_anim搞动画过渡?

我刚开始做嵌入式GUI的时候也是这么想的,直接lv_scr_load()一切干净利落。但你实际跑一遍就会发现,这个“干净利落”在视觉上特别生硬,新页面瞬间顶上来,没有任何过渡,用户感觉就像在看PPT翻页。在带触摸屏的项目里,这种体验非常掉价,客户拿到手第一句话可能就是“这界面怎么这么愣”。

另一个更隐蔽的问题是,lv_scr_load()切换是瞬间完成的,旧页面直接被替换掉,没有任何中间态。如果这时候你在旧页面上还挂着定时器、动画、或者正在播放的进度条,全部会被瞬间打断,状态来不及保存。而用lv_scr_load_anim做渐变过渡,新旧页面会有一段共存时间,视觉上是连续变化的,交互上也更平滑。

还有一点,LVGL的屏幕切换本质上不是删除和新建,而是把display对象上挂载的顶层screen对象换掉。底层机制并不复杂,但正因为这个“换”的过程比较粗暴,才需要动画模块来做一个中间过渡层。lv_scr_load_anim的原理是在指定时间内,驱动动画引擎逐帧改变新旧屏幕的坐标、透明度或裁剪区域,LVGL的刷新机制再把每一帧的差异区域刷新到显存,最终呈现在屏幕上的是连续的过渡画面。

所以这篇内容我会围绕lv_scr_load_anim的完整用法来展开,包括参数怎么调、动画类型怎么选、和内存的相爱相杀,以及我在实际项目里踩过的一些坑。适合正在用LVGL做界面开发、尤其是跑在STM32和FreeRTOS平台上的朋友参考。

2. lv_scr_load_anim核心参数解析与选型逻辑

2.1 函数签名和各版本差异

先看清楚API长什么样。在LVGL 8.x中,函数原型是这样的:

void lv_scr_load_anim(lv_obj_t * scr, lv_scr_load_anim_t anim_type, uint32_t time, uint32_t delay, bool auto_del);

在LVGL 9.x中,参数基本一致,但部分版本对动画类型枚举有调整,而且9.x更推荐配合lv_scr_load_anim_start()一起使用,分两步走:先设置动画参数,再显式启动。我建议你不管用什么版本,动手之前先打开lv_obj.h或者lv_screen.h看一下当前版本的函数声明,不要凭记忆写。我见过太多人从8.x代码往9.x上搬,编译报错还不知道是参数顺序变了。

参数含义拆开说:

  • scr:目标screen对象,也就是你要切换过去的新页面对象。这个对象需要提前创建好。
  • anim_type:动画类型,下一节详细拆。
  • time:动画持续时间,单位毫秒。这是整个函数最关键的参数,直接决定过渡的“质感”。
  • delay:延迟启动时间,单位毫秒。这个参数很多人忽略,但在做页面联动时特别好用。
  • auto_del:动画结束后是否自动删除旧screen。设置为true时LVGL会在动画完成后把旧的screen对象删掉,省得你手动释放。

2.2 动画类型怎么选

LVGL内置了几种预定义动画类型,我整理了一个表:

动画类型效果推荐场景内存开销
LV_SCR_LOAD_ANIM_NONE无动画,等同于直接切换调试、低内存场合最低
LV_SCR_LOAD_ANIM_OVER_LEFT新页面从右侧滑入覆盖旧页面列表进入详情中
LV_SCR_LOAD_ANIM_OVER_RIGHT新页面从左侧滑入覆盖旧页面返回上一级中
LV_SCR_LOAD_ANIM_MOVE_LEFT新页面推着旧页面一起向左移动同级页面左右切换较高
LV_SCR_LOAD_ANIM_MOVE_RIGHT新页面推着旧页面一起向右移动同级页面右切较高
LV_SCR_LOAD_ANIM_FADE_IN新页面淡入,旧页面淡出弹窗、全屏浮层最低
LV_SCR_LOAD_ANIM_FADE_ON新页面淡入后在旧页面上方停留需要保留旧页面背景的场合中

从内存角度来看,MOVE_LEFT/RIGHT这类动画在一段时间内新旧两个页面都必须完整存在于内存中,因为两者都会被渲染。FADE_IN相对好一些,透明度渐变过程中底层旧页面虽然还在,但新页面的渲染内容只占实际显示区域。在内存吃紧的板子上,我个人建议优先考虑FADE_IN或OVER_LEFT,减少同时渲染的开销。

从体验角度来看,MOVE_LEFT最适合做“平级切换”,比如从左到右滑动菜单项;OVER_LEFT最适合做“深一层”的进入操作,比如点击列表项进入详情页,新页面覆盖上来,符合人的空间认知;返回操作则用OVER_RIGHT反向覆盖。方向搞反了会非常反直觉。

2.3 time和delay的调参心得

time参数的设置,说多了都是泪。我刚开始做的时候,觉得动画越丝滑越好,直接把时间设成800ms,一跑起来发现卡成PPT。后来才明白,MCU不是手机CPU,LVGL动画期间每帧都要做重绘计算和刷新,时间越长意味着动画中间帧越多,CPU占用率越高,内存占用峰值时间也越长。

实测下来,在STM32F429这类主频180MHz的芯片上,320x240分辨率RGB565屏幕,动画时间建议控制在200ms到400ms之间。低于150ms会感觉没有过渡,高于500ms会明显感觉“拖沓”,而且动画期间如果还要处理触摸事件,帧率会掉得很厉害。在更高端的平台比如Cortex-A系列或者双核MCU上,可以适当放宽到500ms左右。

delay参数我常用的一个场景是多级联动的页面切换。比如点击菜单后,先让当前页面的某个图标做一个缩小动画,等这个动画快结束时再启动页面切换,这时候就在lv_scr_load_anim里传入一个100~200ms的delay,让两个动画首尾衔接,视觉节奏很舒服。另外,如果新页面的内容创建比较耗时,也可以加一点delay,给LVGL一点缓冲时间,避免启动瞬间CPU抢不过资源。

3. 实操:搭建一个带丝滑切换的LVGL多页面应用

3.1 基础环境准备

先说下我的测试环境,方便你对照。我用的是一块STM32F429IIT6,主频180MHz,屏幕是320x240的TFT,RGB565色深,LVGL配置了单缓冲+局部刷新。系统层面跑的是FreeRTOS,LVGL跑在一个独立任务里,任务栈大小2048字节。

不管你是 STM32 还是其他平台,以下几个基础配置先确认无误,动画才能跑得起来:

  • lv_conf.h里的LV_USE_ANIMATION一定要开,默认是开的,但有些裁剪配置会把动画整个裁掉。
  • LV_TICK_CUSTOM要正确配置。LVGL动画的时钟基准全靠它驱动,如果你的 tick 没配好,动画要么不走,要么一走就飞。
  • 系统里要周期性调用lv_timer_handler()。在FreeRTOS里就是在LVGL任务里用一个 while 循环反复调,周期通常5ms。这个调用周期不稳定,动画就会一顿一顿的,所以别让它被其他高优先级任务长期抢占。

3.2 创建两个页面对象

下面我用一个双页面切换的完整示例来说明,这个例子在调试LVGL页面切换时特别常用,建议直接当模板抄。

/* 两个全局screen对象 */ static lv_obj_t *screen_main = NULL; static lv_obj_t *screen_setting = NULL; /* 创建主页面 */ static void create_screen_main(void) { screen_main = lv_obj_create(NULL); /* NULL表示创建独立screen */ lv_obj_set_style_bg_color(screen_main, lv_color_hex(0x2F3B4C), LV_STATE_DEFAULT); lv_obj_t *btn = lv_btn_create(screen_main); lv_obj_set_size(btn, 120, 48); lv_obj_center(btn); lv_obj_t *label = lv_label_create(btn); lv_label_set_text(label, "进入设置"); lv_obj_center(label); lv_obj_add_event_cb(btn, btn_main_cb, LV_EVENT_CLICKED, NULL); } /* 创建设置页面 */ static void create_screen_setting(void) { screen_setting = lv_obj_create(NULL); lv_obj_set_style_bg_color(screen_setting, lv_color_hex(0x1B6B5A), LV_STATE_DEFAULT); lv_obj_t *btn = lv_btn_create(screen_setting); lv_obj_set_size(btn, 120, 48); lv_obj_center(btn); lv_obj_t *label = lv_label_create(btn); lv_label_set_text(label, "返回主页"); lv_obj_center(label); lv_obj_add_event_cb(btn, btn_setting_cb, LV_EVENT_CLICKED, NULL); } /* 主页面按钮回调:切换到设置页 */ static void btn_main_cb(lv_event_t *e) { lv_scr_load_anim(screen_setting, LV_SCR_LOAD_ANIM_MOVE_LEFT, 300, 0, true); } /* 设置页按钮回调:返回主页面 */ static void btn_setting_cb(lv_event_t *e) { lv_scr_load_anim(screen_main, LV_SCR_LOAD_ANIM_MOVE_RIGHT, 300, 0, true); }

3.3 初始化顺序和生命周期管理

初始化顺序有个细节容易踩坑。lv_obj_create(NULL)创建出来的对象在LVGL里会被视为一个独立screen,但这个screen并不会自动显示,必须等lv_scr_load()或lv_scr_load_anim()被调用才会挂到display上去。

推荐的做法是,在系统初始化时就把所有screen都创建好,用lv_scr_load(screen_main)显示第一个页面。后续切换时,目标页面对象已经存在,直接用lv_scr_load_anim做过渡。如果你在回调里临时创建新screen再立刻做动画,也不是不行,但会有一段创建对象的耗时,动画启动瞬间会卡一下,不够丝滑。

关于生命周期,特别注意auto_del = true的行为。它会在动画结束后调用lv_obj_del()删除旧screen,并且把旧screen上的所有子对象一并删掉。这意味着:

  • 如果其他代码还持有旧screen里某个子对象的指针,动画结束后这个指针就变成了悬空指针,再去访问会直接硬错误。
  • 如果旧screen上挂了定时器,会随父对象一起被删除,但定时器的回调函数里如果引用了其他对象,回调触发时机要小心。
  • 如果新页面里有控件需要引用上一个页面的数据,不要直接存对象指针,应该把需要的值提前拷贝出来。

我在一个项目里就吃过亏:设置页有个滑块,滑块的user_data里存了主页面某个label的指针,结果点击“返回”后主页面被删了,设置页再点滑块,回调里访问那个label指针,直接崩了。后来改成所有跨页面引用都通过全局变量或者静态变量传递,才彻底解决。

3.4 和FreeRTOS任务搭配的推荐写法

在FreeRTOS里跑LVGL,任务优先级和调度策略直接影响动画的流畅度。我目前的推荐配置是:

void lvgl_task(void *param) { while(1) { uint32_t start = xTaskGetTickCount(); lv_timer_handler(); /* 统计本次耗时,动态调整delay */ uint32_t elapsed = xTaskGetTickCount() - start; if (elapsed < 5) { vTaskDelay(5 - elapsed); } else { vTaskDelay(1); /* 处理耗时超过5ms,立即让出CPU */ } } }

任务优先级我建议设为中等偏低,比如tskIDLE_PRIORITY + 2。为什么不能太高?因为LVGL的lv_timer_handler()里面有刷新和事件处理,如果优先级太高,会抢占其他任务的执行,包括触摸扫描、传感器读取这些实时性要求高的任务。但如果太低,又有可能被其他任务长期抢占,导致动画掉帧。实测下来,触摸扫描任务优先级略高于LVGL任务,通信任务优先级略低或持平,整体最稳定。

另外,LVGL任务栈要足够大。动画期间LVGL会递归遍历对象树、执行回调,栈深度不可控,如果任务栈太小,会出现莫名其妙的现象:某些页面切换正常,某些一进去就HardFault。我一般给LVGL任务分配4096字节以上,如果开了比较大的图片解码、字体抗锯齿,甚至要8192字节。Debug时打开configCHECK_FOR_STACK_OVERFLOW,看任务栈还剩多少,别靠猜。

4. 内存优化:让动画在资源受限的MCU上跑起来

4.1 动画期间内存开销怎么算

提到动画就绕不开内存,因为动画期间,尤其是MOVE_LEFT这种位移型动画,新旧两个页面在过渡阶段可能同时处于“活跃”状态。虽然LVGL在刷新时是按dirty area逐块刷新的,但两个screen的坐标、透明度、裁剪区域在动画期间都会被实时计算和维护,这对内存的消耗是实打实的。

具体算笔账。假如你的屏幕是320x240,RGB565色深,一帧完整画面的像素数据大小是3202402 = 153600字节,也就是150KB。如果你配置了双缓冲,显存区就是300KB。如果在动画过程中,LVGL维护的渲染缓存不足以覆盖整个界面,它会把画面拆成多个区域逐块渲染,每一块都需要从屏幕缓冲区读取、混算、再写回,这个过程虽然没有把所有像素都复制一份,但计算量和内存带宽消耗非常高。

那么如何估算你自己项目的动画内存上限?有个经验公式:动画所需内存峰值 ≈ (旧screen对象树大小 + 新screen对象树大小)+ 显示缓冲区大小 + 渲染过程中的临时缓冲区大小。对象树大小取决于你页面里有多少控件、每个控件又有多少样式属性。LVGL的对象结构体并不小,一个基础对象大概占几百字节,带样式的更多。如果两个页面各有20个控件,光对象内存就有几十KB。

在RAM只有64KB的芯片上,做一个页面切换动画,如果其他功能(通信协议栈、传感器数据缓存、业务逻辑变量)已经占了40KB,留给LVGL的就只剩20KB左右,这时候一个页面对象树加显存缓冲就已经很紧张了。所以内存优化不是可选项,是必须项。

4.2 从lv_conf.h入手的三项关键配置

lv_conf.h是LVGL配置的总入口,动画和内存相关的三个配置项,我建议每个项目都认真调一遍,别用默认值糊弄过去。

第一个是LV_MEM_SIZE。这个是LVGL内部内存池的大小,LVGL所有对象、控件、样式属性的动态内存分配都从这里出。如果这个值太小,动画期间频繁创建临时对象就会分配失败,轻则动画中断,重则直接断言。我的判断标准是:打开LV_USE_PERF_MONITOR和LV_USE_MEM_MONITOR这两个工具,跑一遍页面对切并来回切换多次,观察LVGL空闲内存的最小值,然后确保这个最小值不低于LV_MEM_SIZE的20%。如果低于20%,就需要调大LV_MEM_SIZE。实测中,一个包含三四个页面、每页十几二十个控件的工程,LV_MEM_SIZE至少要64 * 1024才比较宽裕。

第二个是LV_MEM_CUSTOM。如果芯片支持,我建议直接挂到FreeRTOS的heap管理上,把LV_MEM_CUSTOM = 1,并指定LV_MEM_CUSTOM_INCLUDE <FreeRTOS.h>和LV_MEM_CUSTOM_ALLOC pvPortMalloc。这样做的好处是,LVGL和系统共用一套内存分配机制,方便你用FreeRTOS的xPortGetFreeHeapSize()统一查看系统剩余内存。而且FreeRTOS的heap_4方案有内存碎片合并机制,对反复创建删除页面对象这种场景更友好。

第三个是显示缓冲区的配置方式。如果你在lv_disp_draw_buf_init()里只配置了一个缓冲,动画期间刷新性能会明显不够,因为单缓冲意味着LVGL必须等屏幕控制器完成一次完整刷新后,才能开始渲染下一帧,中间存在空白等待。建议至少配置双缓冲,一个用于LVGL渲染,一个用于DMA传输,可以做到边渲染边传输。如果RAM实在不够,退而求其次,用单缓冲但把LV_HOR_RES_MAX和实际屏幕高度配合,采用逐行刷新的做法,也能在一定程度上缓解。

4.3 图片和字体的内存消耗是隐藏杀手

很多朋友只盯着对象树和缓冲,却忽略了图片和字体。一张全屏背景图,如果以RGB565格式直接存成C数组,320x240的图就是150KB,直接能把小内存芯片撑爆。更别提如果你用lv_img_set_src加载一张PNG或JPEG,LVGL还需要解码,解码过程需要额外缓冲区,开LV_USE_PNG和LV_USE_SJPG后,解出来的位图数据照样要占内存。

我的建议是:

  • 能用纯色背景就用纯色或简单样式,别放整图背景。
  • 非用不可的图片,优先转成C数组,防止文件系统读取带来的延迟。
  • 图片色深尽量和屏幕一致,避免转换损耗。
  • 图片大小按实际显示尺寸裁剪,不要明明显示一个80x80的图标,却放一张320x240的大图。

字体的套路更深。LVGL默认字体是8位宽的位图字体,中文字体每个字都要占几个字节到几十个字节不等。如果你页面切换动画期间要渲染动态文本,每次文本变化LVGL都会重新分配字形缓存。开LV_USE_FONT_PLACEHOLDER可以减少一些无谓的分配,但更实际的做法是,把常用字符做成静态字体,只加载有限的字符集,避免整个中文全字库直接载入RAM。字号能少用就少用,每多一种字号,字形数据就是一份完整的内存占用。

4.4 样式对象和控件的内存复用

还有一块很多人不注意的内存浪费在样式上。LVGL允许你对每个控件单独设置样式,但如果每个控件都复制一份样式对象,内存开销会让你怀疑人生。比如你有10个按钮,每个按钮都有独立的lv_obj_set_style_bg_color(),LVGL在内部会为每种样式分配一个样式条目,这种重复分配非常浪费。

正确做法是,对通用控件定义一个“样式类”,用lv_style_set_bg_color()和lv_obj_add_style()把样式挂在控件上。LVGL的样式系统本身支持共享,多个控件可以指向同一个样式对象,只用LV_STATE_DEFAULT描述默认态,有交互的控件再单独添加LV_STATE_PRESSED样式即可。这样10个按钮只要一两份样式内存,而不是10份。

另外,页面切换后如果旧页面被auto_del删除了,那么旧页面上的样式对象如果没有显式lv_style_reset(),有可能留在内存池里。这个问题的根源是LVGL的样式对象生命周期由用户负责。所以我对样式对象的统一管理策略是:所有全局样式用static声明、初始化一次、结束后不手动删除,随程序生命周期存续。只有页面内临时创建的对象样式才跟着页面对象走。

4.5 善用懒加载和预创建

还有一种内存优化思路和页面切换时机相关,叫做“懒加载”,实现代价很小,收益很实在。就是在程序启动时只创建首页对象,其他页面不创建,等到用户需要切换到某个页面时才调用创建函数,然后用lv_scr_load_anim切过去。

这样做的好处是,启动阶段内存只被首页占用,更短的时间内可以进入主界面,用户体验更好。但缺点是,点击按钮触发页面创建的瞬间,lv_obj_create频繁分配内存会有一点耗时,动画启动可能有轻微卡顿。为了平衡,我一般把“创建目标页面”这一步放在按钮回调里,并且在调用lv_scr_load_anim之前加一个lv_obj_invalidate强制刷新一帧,人工给用户一种“页面已经在加载”的反馈感,掩盖掉创建耗时。

反过来,如果内存充足但页面对切非常频繁,也可以全部预创建,切换只做动画不建对象,这样对切最快、最顺滑。取舍的核心指标就是LV_MEM_SIZE的余量,用内存监控工具实测后再定。

5. 常见问题与排查技巧实录

5.1 切换后系统崩溃或HardFault

这个是最常见的故障,排查思路基本指向三个方向。

第一个是auto_del导致悬空指针。前面已经详细分析过,动画结束后旧screen被自动删除,如果还有代码引用了旧screen或它的子对象,必然崩。排查方法是在崩溃HardFault中断服务函数里查看PC寄存器的值,在IDE内存窗口里看对象结构体是否变成了一串0xDEADBEEF或无效地址。如果是,基本就是悬空指针。

第二个是任务栈溢出。动画递归调用比较深,如果LVGL任务栈设置不够,切换到某一特定页面时栈炸了。排查方法是用FreeRTOS的任务栈高水位线统计uxTaskGetStackHighWaterMark(),在每次动画结束后打印一次,观察水位线趋势。如果某个页面创建后水位线急剧下降,说明栈不够,直接改大任务栈。

第三个是LVGL内部断言。比如创建新页面时内存不足,LVGL会进入LV_ASSERT,此时如果你的LV_ASSERT_HANDLER没有定义,就会直接死循环。排查方法是打开LV_USE_LOG(Level设为LV_LOG_LEVEL_INFO),串口会打印内存分配失败的具体位置,跟着日志哪里崩了改哪里。

5.2 动画卡顿、掉帧严重

动画掉帧要从三个方面查。

先查lv_timer_handler的调用周期是否稳定。如果系统里其他任务耗时太长,LVGL任务得不到及时调度,动画就会一卡一卡的。在动画期间用逻辑分析仪或GPIO翻转测试,看lv_timer_handler的实际调用间隔是否稳定在5ms左右,如果忽高忽低,优先优化任务调度。

再查缓冲区配置。双缓冲是否真正确认过?有的朋友配置了lv_disp_draw_buf_init分配了两块缓冲,但后续又改成了单缓冲刷新模式,等于白配。用LV_USE_PERF_MONITOR观察FPS,动画期间FPS如果能稳定在30以上,基本还行;如果20都不到,优先检查缓冲数量。

最后查动画类型和页面的对象复杂度。对象越多、每个对象的样式层级越深,渲染耗时越长。一个页面几十个控件还带圆角阴影,跑动画必然吃力。优化手段一是减少动画期间页面上的复杂控件数量,二是把动画时间适当延长以降低每帧刷新压力,三是用LV_OBJ_FLAG_HIDDEN把动画期间不需要显示的控件隐藏掉,减少渲染范围。

5.3 页面切换后触摸事件错乱

这个问题我调试了很久才找到原因。现象是切换动画完成后,点按钮没反应,或者点A按钮触发了B按钮的回调。

核心原因是LVGL的事件系统基于对象的点击区域和父对象坐标。如果切换后的screen坐标偏移了,事件就会算错。排查时先在动画完成回调里打印lv_obj_get_x(screen_setting)和lv_obj_get_y(...),确认屏幕对象坐标是否异常。另外,交叉检查动画期间触摸到了旧页面坐标区域导致事件锁定,可以在lv_scr_load_anim之后加一个lv_group_focus_obj()或者手动清掉输入设备的group焦点,防止旧对象的焦点还锁着触摸输入。

还有一个容易被忽视的点:如果你在动画期间有触摸事件正在处理(比如用户在动画启动的瞬间按下了按钮),LVGL的事件状态机可能被卡住,表现为后续所有按钮都点不动。这类问题我建议在按钮回调最开始加一个lv_obj_set_state(btn, LV_STATE_DEFAULT)重置控件状态,再处理业务逻辑,能避免大部分状态残留问题。

5.4 反复切换后内存泄漏

内存泄漏在长时间运行的嵌入式设备上最致命,页面切着切着系统就跑死了。

每次切换都创建新页面但没释放旧页面,是最常见原因。检查你的回调里是否都在用lv_scr_load_anim且auto_del = true,如果设成了false,旧页面对象就一直残留在内存里,切一次漏一份。如果业务上确实需要保留页面,建议同一个页面只创建一次,反复复用,不要每次进入都新建。

定时器泄漏也很隐蔽。如果页面里创建了lv_timer_create并且设置了周期回调,页面切换时auto_del会把定时器连带删除。但如果你在页面销毁前没有lv_timer_del()或者没有把定时器停止,某些版本的LVGL可能出现定时器仍在队列里、回调不断触发的情况。排查方式是在页面切换回调里通过lv_timer_get_next遍历所有正在运行的定时器,看看是否有不属于当前页面的残留条目。

5.5 内存监控和压力测试技巧

最后分享一套我自己维护项目时用的“内存压力体检”流程。写完页面切切换功能后,不要直接交付,先在开发板上写一个自动化测试脚本:每500ms执行一次正向切换,再500ms反向切换,持续跑20分钟,同时在串口周期打印:

  • lv_mem_monitor_t结构体里的free_size、total_size和max_used。
  • FreeRTOS的xPortGetFreeHeapSize()剩余内存。
  • LV_USE_PERF_MONITOR的FPS和动画耗时。

观察这些数值是否有持续下降的趋势。如果 free_size 反复波动但稳定在一个区间,说明内存分配和释放是平衡的,没有泄漏;如果整体趋势一路向下,哪怕很慢,也说明某个路径上对象或样式没有被释放。

我把压力测试集成到了一个test_mem_stress.c文件里,测试完直接删掉,不影响正式代码。这套流程虽然简单,但能提前暴露绝大多数和页面切换相关的内存问题,远比产品用着用着死机了再去现场调试靠谱。

另外,针对标题里提到的“丝滑”二字,再补一个实操经验:如果MCU性能实在不够,与其勉强跑位移动画,不如降级成模板透明度渐变,效果一样自然,而且对CPU和内存都友好得多。我现在的项目里默认开启FADE_IN,只有特定页面跳转才用位移动画,整体体验好了,系统也稳定了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询