- meow_box(include/meow_ui_box.h + meow_ui_box.c):竖直/水平容器;子项=固定|weight|wrap; margin/padding/gap;主轴对齐 start/center/end/space-between/around/evenly;交叉轴 start/center/end/stretch; 嵌套(深度≤16 静态栈,单遍求解,无递归爆炸)。可作 meow_view 布局提供者 (meow_view_set_layout/relayout,opt-in;未设布局的 view 保持现有绝对坐标行为)。 新增 meow_widget_set_rect(只写字段不标脏)作为 box 写坐标的通道。 - meow_anim(include/meow_ui_anim.h + meow_ui_anim.c):时间驱动 tween(from/to/duration/easing: linear/ease-out/ease-in-out/overshoot)+ update/done 回调;静态池≤16、取消、并行多动画; meow_anim_set_region 让每帧只 flush 动画影响区(不整屏)。meow_anim_tick 挂主循环 on_tick, 无动画零开销(只查计数即返回)。 - meow_scroll(include/meow_ui_scroll.h + meow_ui_scroll.c):速度跟踪(最近 N 采样加权)+ 松手 fling (减速度曲线,用 anim 基座驱动)+ 边界过冲 clamp + 简单回弹(不做 spring)+ 可选 snap; 跟手拖动行为不变(track 内部调既有 set_scroll);每帧只 redraw 列表视口区;不引 libm。 - 追加式:既有 meow_row/column/box 与全部 meow_*_create/hit/scroll 签名一行未改;旧绝对坐标永远有效; 既有 App 免改源码。 - 验证:宿主新测试 box(42)/anim(32)/scroll(17) 全 PASS;既存 9 回归(widgets/sdk_input_keys/ime_fix/ pinyin/overlay_suppress/notif_gesture/sdk_fixes/sb_collapse/p1p2p3_repro)全 PASS; 离屏 demo(demo_box_scroll.c)渲染 box 布局 + 列表 fling 正常;zig-mipsel -Wall -Wextra 全 TU 0 warning; meow_ui.a md5 83dd6a2d0d24cc748a8659c29f1de2b5(未重链/部署 hello/files/ncm/reader,由主会话统一处理)。
129 lines
4.2 KiB
C
129 lines
4.2 KiB
C
/*
|
||
* meow_ui_anim.c — 动画基座 + UI 节拍器(§12.2)。
|
||
*
|
||
* 时间驱动 tween:start 记录起始时刻(meow_ui_time_ms,单调),tick 按经过时间求 t,
|
||
* 代入 ease 得 v = from + (to-from)*ease(t),回调 update;t 到 1 → 回调 done(canceled=0) 并回收。
|
||
* 取消 → 回调 done(canceled=1) 并回收。静态池(≤MEOW_ANIM_MAX),热路径零 malloc。
|
||
* 无动画时 meow_anim_tick() 仅查活动计数即返回(零开销)。
|
||
*
|
||
* 节拍挂接:由 meow_ui_run 主循环(on_tick 之后)与 meow_ui_feed_tick(阻塞 IME 循环)调用,
|
||
* 不引入常驻高频轮询。modal/dialog 阻塞循环不喂 tick(动画暂停,符合「模态期不做动画」惯例)。
|
||
*/
|
||
#include "meow_ui.h"
|
||
#include "include/meow_ui_anim.h"
|
||
#include <string.h>
|
||
|
||
struct meow_anim {
|
||
int active;
|
||
float from, to;
|
||
uint32_t duration_ms;
|
||
meow_ease ease;
|
||
uint64_t start_ms;
|
||
float value;
|
||
int region_set;
|
||
int rx, ry, rw, rh;
|
||
meow_anim_update_fn update;
|
||
meow_anim_done_fn done;
|
||
void *ctx;
|
||
};
|
||
|
||
static struct meow_anim g_pool[MEOW_ANIM_MAX];
|
||
static unsigned g_active = 0; /* 活动动画计数(0 = 空闲,tick 直退) */
|
||
|
||
/* ---- 缓动 ---- */
|
||
float meow_ease_apply(meow_ease e, float t)
|
||
{
|
||
if (e == MEOW_EASE_OUT) {
|
||
/* ease-out quad */
|
||
float u = 1.0f - t;
|
||
return 1.0f - u * u;
|
||
}
|
||
if (e == MEOW_EASE_IN_OUT) {
|
||
/* ease-in-out quad */
|
||
if (t < 0.5f) return 2.0f * t * t;
|
||
float u = -2.0f * t + 2.0f;
|
||
return 1.0f - u * u * 0.5f;
|
||
}
|
||
if (e == MEOW_EASE_OVERSHOOT) {
|
||
/* ease-out-back(越过目标再回稳);t=1 → 恰为 1 */
|
||
const float c1 = 1.70158f;
|
||
const float c3 = c1 + 1.0f;
|
||
float u = t - 1.0f;
|
||
return 1.0f + c3 * u * u * u + c1 * u * u;
|
||
}
|
||
/* MEOW_EASE_LINEAR */
|
||
return t;
|
||
}
|
||
|
||
/* ---- 生命周期 ---- */
|
||
meow_anim *meow_anim_start(const meow_anim_desc *d)
|
||
{
|
||
if (!d) return NULL;
|
||
for (int i = 0; i < MEOW_ANIM_MAX; i++) {
|
||
struct meow_anim *a = &g_pool[i];
|
||
if (a->active) continue;
|
||
a->active = 1;
|
||
a->from = d->from;
|
||
a->to = d->to;
|
||
a->duration_ms = d->duration_ms ? d->duration_ms : 1; /* 0 → 瞬发(下一 tick 完成) */
|
||
a->ease = d->ease;
|
||
a->start_ms = meow_ui_time_ms();
|
||
a->update = d->update;
|
||
a->done = d->done;
|
||
a->ctx = d->ctx;
|
||
a->region_set = 0;
|
||
a->value = d->from;
|
||
g_active++;
|
||
if (a->update) a->update(a, a->value, a->ctx); /* 起始帧回调(t=0) */
|
||
return a;
|
||
}
|
||
return NULL; /* 池满 */
|
||
}
|
||
|
||
void meow_anim_cancel(meow_anim *a)
|
||
{
|
||
if (!a || !a->active) return;
|
||
a->active = 0;
|
||
if (g_active > 0) g_active--;
|
||
if (a->done) a->done(a, 1, a->ctx);
|
||
}
|
||
|
||
int meow_anim_active(meow_anim *a) { return (a && a->active) ? 1 : 0; }
|
||
float meow_anim_value(meow_anim *a) { return a ? a->value : 0.0f; }
|
||
|
||
void meow_anim_set_region(meow_anim *a, int x, int y, int w, int h)
|
||
{
|
||
if (!a) return;
|
||
if (x < 0) { a->region_set = 0; return; }
|
||
a->region_set = 1;
|
||
a->rx = x; a->ry = y; a->rw = w > 0 ? w : 0; a->rh = h > 0 ? h : 0;
|
||
}
|
||
|
||
/* ---- 节拍器 ---- */
|
||
unsigned meow_anim_count(void) { return (unsigned)g_active; }
|
||
|
||
void meow_anim_tick(void)
|
||
{
|
||
if (g_active == 0) return; /* 无动画:零开销直接返回 */
|
||
uint64_t now = meow_ui_time_ms();
|
||
for (int i = 0; i < MEOW_ANIM_MAX; i++) {
|
||
struct meow_anim *a = &g_pool[i];
|
||
if (!a->active) continue;
|
||
uint64_t elapsed = now - a->start_ms;
|
||
float t = (float)(elapsed) / (float)a->duration_ms;
|
||
int finished = (t >= 1.0f);
|
||
if (t > 1.0f) t = 1.0f;
|
||
if (t < 0.0f) t = 0.0f;
|
||
float v = a->from + (a->to - a->from) * meow_ease_apply(a->ease, t);
|
||
a->value = v;
|
||
if (a->update) a->update(a, v, a->ctx);
|
||
if (a->region_set && a->rw > 0 && a->rh > 0)
|
||
meow_ui_flush_region(a->rx, a->ry, a->rw, a->rh); /* 只刷动画影响区 */
|
||
if (finished) {
|
||
a->active = 0;
|
||
if (g_active > 0) g_active--;
|
||
if (a->done) a->done(a, 0, a->ctx);
|
||
}
|
||
}
|
||
}
|