带动画曲线插值的 UI 抽象工具集,无依赖
- 动画曲线插值抽象,定点数优化浮点计算,自定义曲线
- 选择菜单抽象,适用于普通选择菜单、启动器等
- 更多控件抽象 (TODO)
- 控件树 (TODO)
视频:https://www.bilibili.com/video/BV1TT4y1W7tz
模拟器:https://github.com/Forairaaaaa/smooth_ui_toolkit_simu
.
├── core
│ ├── easing_path // 动画曲线
│ ├── smooth_drag
│ ├── transition // 一维过渡抽象
│ ├── transition2d // 二维过渡抽象
│ └── types // 类型定义
├── select_menu // 选择菜单抽象
│ ├── base
│ ├── smooth_options
│ └── smooth_selector
├── smooth_widget // TODO
│ ├── base
│ └── button
└── utils
└── fpm // 定点数
动画曲线函数,参考:https://easings.net/, 示例:
using namespace SmoothUIToolKit;
// 避免浮点运算,映射到 0 ~ EasingPath::maxT
for (int t = 0; t < EasingPath::maxT; t++)
{
auto x = EasingPath::easeInQuad(t);
// auto x = EasingPath::easeOutQuad(t);
// auto x = EasingPath::easeInOutQuad(t);
// auto x = EasingPath::easeInCubic(t);
// ...
spdlog::info("x({}) = {}", t, x);
}
更多具体内容在头文件定义
一维过渡抽象,示例:
using namespace SmoothUIToolKit;
Transition t;
// 初始值
t.setStartValue(0);
// 结束值
t.setEndValue(100);
// 过渡时间
t.setDuration(400);
// 过渡曲线
t.setTransitionPath(EasingPath::easeOutBack);
// Update 回调函数
t.setUpdateCallback([](Transition* transition) {
spdlog::info("value: {}", transition->getValue());
});
// 开始过渡, 传入当前时间 (ms)
t.start(HAL::Millis());
while (1)
{
// 更新过渡,传入当前时间 (ms)
t.update(HAL::Millis());
// update方法会在最后调用回调函数,当然也可以在外部直接拿值
t.getValue();
}
更多具体内容在头文件定义
二维过渡抽象,最常用的一集 (xy 坐标,wh 形状...),示例:
using namespace SmoothUIToolKit;
Transition2D t2d;
// 配置方法和一维无异,略
...
// 区别是可以单独配置他们的过渡参数
t2d.getXTransition()...
t2d.getYTransition()...
// 一样可以注册回调
t2d.setUpdateCallback([](Transition2D* transition2d) {
spdlog::info("i'm at ({}, {})", transition2d->getValue().x, transition2d->getValue().y);
});
// 跳到指定坐标, 没有过渡, 默认构造函数会跳到(0,0)
t2d.jumpTo(100, 233);
// 移动到指定坐标,有过渡
t2d.moveTo(666, 777);
while (1)
{
// 更新过渡,传入当前时间 (ms)
t2d.update(HAL::Millis());
}
更多具体内容在头文件定义
选择菜单的抽象,可以用于实现:
- 带有一个飞来飞去的选择指示器的选择菜单
- 滚轮选择菜单
- 选项会变形的环形菜单等
三个概念:
- 选项关键帧列表,存着每一个选项的关键帧
x, y, w, h
- 选择器,根据输入,移动到选项关键帧的
x, y
,变形到选项关键帧的w, h
- 摄像机,自动移动到合适的
x, y
,以保持选择器在摄像机w, h
中
因此只有选择器和摄像机有运动过渡,选项可视为静态不动的
示例:
using namespace SmoothUIToolKit;
using namespace SmoothUIToolKit::SelectMenu;
class SmoothSelector_test : public SmoothSelector
{
// 重写读取输入回调
void onReadInput() override
{
// 按键Up clicked
if (...)
goLast();
// 按键Down clicked
else if (...)
goNext();
}
// 重写渲染回调
void onRender() override
{
// 清屏
...
// 渲染选项
for (auto& i : getOptionList())
{
...(i.keyframe.x,
i.keyframe.y,
i.keyframe.w,
i.keyframe.h);
}
// 渲染选择器
auto cf = getSelectorCurrentFrame();
...(cf.x,
cf.y,
cf.w,
cf.h);
// 推屏
...
}
};
SmoothSelector_test menu;
// 添加选项
menu.addOption({{0, 0, 100, 20}, nullptr});
menu.addOption({{0, 20, 100, 20}, nullptr});
menu.addOption({{0, 40, 100, 20}, nullptr});
while (1)
{
// 更新菜单,传入当前时间 (ms)
menu.update(HAL::Millis());
}
更多具体内容在头文件定义
两个概念:
-
选项,根据输入,移动到关键帧的
x, y
,变形到选项关键帧的w, h
-
关键帧列表,存着与选项数量相同的关键帧
x, y, w, h
因此每个选项都有运动过渡,对应数量的关键帧可视为静态不动的。选项们连续地、循环地绕着关键帧列表移动、变形。
示例:
using namespace SmoothUIToolKit;
using namespace SmoothUIToolKit::SelectMenu;
class SmoothOption_Test : public SmoothOptions
{
// 重写读取输入回调
void onReadInput() override
{
// 按键Up clicked
if (...)
goLast();
// 按键Down clicked
else if (...)
goNext();
}
// 重写渲染回调
void onRender() override
{
// 清屏
...
// 渲染选项
for (int i = 0; i < getOptionList().size(); i++)
{
auto ocf = getOptionCurrentFrame(i);
...(ocf.x,
ocf.y,
ocf.w,
ocf.h);
}
// 推屏
...
}
};
SmoothOption_Test menu;
// 添加选项
for (int i = 0; i < 5; i++)
{
menu.addOption();
menu.setLastKeyframe({10 * i, 20 * i, 100, 20});
}
while (1)
{
// 更新菜单,传入当前时间 (ms)
menu.update(HAL::Millis());
}
更多具体内容在头文件定义