Files
MeowOS-For-R3P2/patch_v2/sdk/meow_ui_anim.c
T
MeowOS 5a5e995ebd SDK 冲刺批①+②+⑤: meow_box 线性布局 + meow_anim 动画基座 + meow_scroll 滚动物理
- 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,由主会话统一处理)。
2026-09-01 23:49:24 +08:00

129 lines
4.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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);
}
}
}