- Spring、Easing 动画插值,RGB 颜色过渡插值
- Lvgl C++ 封装,NumberFlow 风格控件
- 动画菜单抽象
- 颜色类型定义,颜色混合、转换方法
- Signal、RingBuffer 等常用模板类
- Vector 类型,clamp、map_range 等数学工具
基础动画插值类,抽象设计参考 Motion ~
Animate animation;
// 动画参数配置
animation.start = 200;
animation.end = 600;
animation.repeat = -1;
animation.repeatType = AnimateRepeatType::Reverse;
// spring 动画参数配置
// 这里调用 springOptions() ,动画类型自动适应为 spring
animation.springOptions().bounce = 0.4;
animation.springOptions().visualDuration = 0.6;
// 如想要 easing 动画,调用 easingOptions() 即可
// animation.easingOptions().easingFunction = ease::ease_out_quad;
// animation.easingOptions().duration = 0.3;
animation.init();
animation.play();
while (1) {
// 更新
animation.update();
// 取值
draw_ball(animation.value(), 233);
}Animate 的派生类,简化取值赋值操作,使用起来更接近于普通变量
AnimateValue x = 100;
AnimateValue y = 225;
while (1) {
// 直接赋值即可,动画会自动适应新目标
x = get_mouse_x();
y = get_mouse_y();
// 直接取值即可,动画会自动更新
draw_ball(x, y);
});配合 spring 参数 可以实现不同的动画效果:
for (int i = 0; i < cursors.size(); i++) {
// 弹簧刚度逐渐增加
cursors[i].x.springOptions().stiffness = 55 + i * 25;
// 阻尼系数逐渐减小
cursors[i].x.springOptions().damping = 13 - i;
}Lvgl 控件智能指针封装
指针管理参考:https://github.com/vpaeder/lvglpp
简化了控件 API,并添加了信号 Signal 来简化事件回调处理
#include <smooth_lvgl.hpp>
// lvgl cpp 封装为 header only
// 需要工程已满足 #include <lvgl.h> 依赖
// 当前支持 v9.3.0 以上的版本
// Basic lvgl object
auto obj = new Container(screen);
obj->setPos(50, 50);
obj->setSize(200, 100);
// Label
auto label = new Label(screen);
label->setTextFont(&lv_font_montserrat_24);
label->align(LV_ALIGN_CENTER, -180, 0);
label->setText("??");
// Button
int count = 0;
auto btn = new Button(screen);
btn->setPos(50, 200);
btn->label().setText("+1");
btn->onClick().connect([&]() {
label->setText(fmt::format("{}", count++));
});
// Switch
auto sw = new Switch(screen);
sw->setPos(50, 300);
sw->onValueChanged().connect([&](bool value) {
label->setText(value ? "ON" : "OFF");
});
// Slider
auto slider = new Slider(screen);
slider->setPos(50, 390);
slider->onValueChanged().connect([&](int value) {
label->setText(fmt::format("{}", value));
});
// Roller
auto roller = new Roller(screen);
roller->align(LV_ALIGN_CENTER, 0, 0);
roller->setOptions({"nihao", "wohao", "dajiahao"});
roller->onValueChanged().connect([&](uint32_t value) {
label->setText(fmt::format("{}", roller->getSelectedStr()));
});
// ...基于 Lvgl 实现的 NumberFlow 风格数字显示控件,支持正负、小数和前后缀显示
auto number_flow = new NumberFlow(lv_screen_active());
...
btn_random->onClick().connect([&]() {
// 设置数值
number_flow->setValue(randomNum);
});
while (1) {
// 更新
number_flow->update();
}// 替换对象类型即可
auto number_flow = new NumberFlowFloat(lv_screen_active());
...把控件的一些共性行为抽离出来作为基类,使用时可以根据实际需求派生
例如一个带动画的选项菜单抽象:
重写 onReadInput() 来读取按键或者是编码器输入,以控制选项的切换
重写 onRender() 来实现实际的画面渲染
非常底层~
基于选择器的菜单抽象,选择器会平滑移动和变形,来匹配选中选项的关键帧
摄像机会自动平滑移动,保持选择器在摄像机范围内
适合常见的横向或纵向列表菜单:
简单横向菜单例程 :
也可以通过坐标变换实现选择器固定不动,而是选项滚动
选择器固定的横向菜单例程 :
选择器固定的纵向带弧度菜单例程 :
可以看这个例程来了解具体的基类设计:
基于选项移动的菜单抽象,每个选项独立动画,循环轮换到关键帧位置
适合圆形旋转菜单、卡片轮播等菜单
可以看这个例程来了解具体的基类设计:
// 0xffffff -> rgb(255, 255, 255)
Rgb_t hex_to_rgb(const uint32_t& hex);
// "#ffffff" -> rgb(255, 255, 255)
Rgb_t hex_to_rgb(const std::string& hex);
// rgb(255, 255, 255) -> 0xffffff
uint32_t rgb_to_hex(const Rgb_t& rgb);
// rgb(255, 255, 255) -> "#ffffff"
std::string rgb_to_hex_string(const Rgb_t& rgb);
// 差值混合
Rgb_t blend_in_difference(Rgb_t bg, Rgb_t fg);
// 透明度混合
Rgb_t blend_in_opacity(Rgb_t bg, Rgb_t fg, float opacity);RGB 颜色的变换过渡插值
// 颜色列表
std::vector<uint32_t> color_list = {...}
AnimateRgb_t bg_color;
bg_color.duration = 0.3;
bg_color.begin();
// 按钮事件
btn_random.onClick().connect([&]() {
// 直接赋值即可
bg_color = color_list[random];
});
while (1) {
// 更新
bg_color.update();
// 取值
xxx.setBgColor(lv_color_hex(bg_color.toHex()));
}动画的更新以系统 Tick 为参考基准,所使用的相关函数来自内部定义:
namespace ui_hal {
/**
* @brief Get the number of milliseconds since running
*
* @return uint32_t
*/
uint32_t get_tick();
/**
* @brief Wait a specified number of milliseconds before returning
*
* @param ms
*/
void delay(uint32_t ms);
} // namespace ui_hal默认实现为 chrono 和 thread
cmake 里 OFF SMOOTH_UI_TOOLKIT_ENABLE_DEFAULT_HAL
或者添加全局宏 SMOOTH_UI_TOOLKIT_ENABLE_DEFAULT_HAL=0
可以关闭这个默认实现
自定义实现方式:
// 比如 Arduino,性能应该比 chrono 好
ui_hal::on_get_tick([]() {
return millis();
});
ui_hal::on_delay([](uint32_t ms) {
delay(ms);
});所有类和方法均定义在命名空间 smooth_ui_toolkit 下
smooth_ui_toolkit::clamp(...);
smooth_ui_toolkit::AnimateValue();
smooth_ui_toolkit::lvgl_cpp::Button(...);
...可以引用 <uitk/short_namespace.hpp> 来获得一个更简短的重命名 uitk
#include <smooth_ui_toolkit.hpp>
#include <uitk/short_namespace.hpp>
uitk::clamp(...);
uitk::AnimateValue();
uitk::lvgl_cpp::Button(...);
...例程用了 lvgl 和 raylib 作为图形库,所以要安装 SDL2
- macOS:
brew install sdl2 cmake make - Ubuntu:
sudo apt install build-essential cmake libsdl2-dev
git clone https://github.com/Forairaaaaa/smooth_ui_toolkit.gitcd smooth_ui_toolkitpython example/fetch_repos.pymkdir build && cd buildcmake .. && make -j8执行 ./build/example/ 下对应例程,如:
./build/example/basic_animations工程 CMakeLists.txt 里添加:
# 不编译例程
set(SMOOTH_UI_TOOLKIT_BUILD_EXAMPLE OFF)
# 引入库路径
add_subdirectory(path/to/smooth_ui_toolkit)
# link
target_link_libraries(your_project PUBLIC
smooth_ui_toolkit
)clone 仓库,直接丢到 components 目录里就行
clone 仓库,直接丢到 libs 目录里就行
clone 仓库,直接丢到 libraries 目录里就行
一些常用图形库的桌面端最简工程
lvgl: https://github.com/Forairaaaaa/lvgl_simulator_cpp
m5gfx: https://github.com/Forairaaaaa/m5gfx_simulator_cpp


















