Files
xianyan/lib/editor/models/spritesheet_models.dart
Developer 847ebc8501 feat: 新增精灵图贴纸支持及多项功能优化
新增精灵图贴纸类型及内置资源
优化分类图标使用SVG替代emoji
实现分页预加载功能
修复API基础地址与客户端一致
新增健康生活、国学经典服务模块
扩展Feed频道至44种并整合互动统计
修正多处UI显示问题及逻辑错误
2026-04-29 09:23:59 +08:00

217 lines
5.8 KiB
Dart
Raw Permalink 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.
// ============================================================
// 闲言APP — 精灵图动画贴纸数据模型
// 创建时间: 2026-04-29
// 更新时间: 2026-04-29
// 作用: 定义精灵图贴纸和表情包的数据结构
// 上次更新: 初始创建
// ============================================================
/// 精灵图贴纸来源
enum SpritesheetSource {
builtin('builtin', '内置'),
local('local', '本地导入'),
remote('remote', '远程下载');
const SpritesheetSource(this.key, this.label);
final String key;
final String label;
}
/// 精灵图贴纸 — 单个动画贴纸
class SpritesheetSticker {
const SpritesheetSticker({
required this.id,
required this.name,
required this.emoji,
required this.source,
required this.imageUri,
required this.frameWidth,
required this.frameHeight,
required this.totalFrames,
this.fps = 12,
this.loop = true,
this.packId,
this.thumbnailUri,
this.columns,
this.rows,
});
final String id;
final String name;
final String emoji;
final SpritesheetSource source;
final String imageUri;
final int frameWidth;
final int frameHeight;
final int totalFrames;
final int fps;
final bool loop;
final String? packId;
final String? thumbnailUri;
final int? columns;
final int? rows;
/// 推算列数:优先用 columns否则假设单行排列
int get effectiveColumns => columns ?? totalFrames;
/// 推算行数:优先用 rows否则为1
int get effectiveRows => rows ?? 1;
factory SpritesheetSticker.fromJson(Map<String, dynamic> json) {
return SpritesheetSticker(
id: json['id'] as String,
name: json['name'] as String,
emoji: json['emoji'] as String? ?? '🎭',
source: SpritesheetSource.values.firstWhere(
(s) => s.key == json['source'],
orElse: () => SpritesheetSource.builtin,
),
imageUri: json['imageUri'] as String,
frameWidth: json['frameWidth'] as int,
frameHeight: json['frameHeight'] as int,
totalFrames: json['totalFrames'] as int,
fps: json['fps'] as int? ?? 12,
loop: json['loop'] as bool? ?? true,
packId: json['packId'] as String?,
thumbnailUri: json['thumbnailUri'] as String?,
columns: json['columns'] as int?,
rows: json['rows'] as int?,
);
}
Map<String, dynamic> toJson() => {
'id': id,
'name': name,
'emoji': emoji,
'source': source.key,
'imageUri': imageUri,
'frameWidth': frameWidth,
'frameHeight': frameHeight,
'totalFrames': totalFrames,
'fps': fps,
'loop': loop,
'packId': packId,
'thumbnailUri': thumbnailUri,
'columns': columns,
'rows': rows,
};
SpritesheetSticker copyWith({
String? id,
String? name,
String? emoji,
SpritesheetSource? source,
String? imageUri,
int? frameWidth,
int? frameHeight,
int? totalFrames,
int? fps,
bool? loop,
String? packId,
String? thumbnailUri,
int? columns,
int? rows,
}) {
return SpritesheetSticker(
id: id ?? this.id,
name: name ?? this.name,
emoji: emoji ?? this.emoji,
source: source ?? this.source,
imageUri: imageUri ?? this.imageUri,
frameWidth: frameWidth ?? this.frameWidth,
frameHeight: frameHeight ?? this.frameHeight,
totalFrames: totalFrames ?? this.totalFrames,
fps: fps ?? this.fps,
loop: loop ?? this.loop,
packId: packId ?? this.packId,
thumbnailUri: thumbnailUri ?? this.thumbnailUri,
columns: columns ?? this.columns,
rows: rows ?? this.rows,
);
}
}
/// 表情包 — 一组精灵图贴纸
class StickerPack {
const StickerPack({
required this.id,
required this.name,
required this.icon,
required this.source,
required this.stickers,
this.coverUri,
this.downloadUrl,
this.fileSize,
this.isDownloaded = false,
});
final String id;
final String name;
final String icon;
final SpritesheetSource source;
final List<SpritesheetSticker> stickers;
final String? coverUri;
final String? downloadUrl;
final int? fileSize;
final bool isDownloaded;
factory StickerPack.fromJson(Map<String, dynamic> json) {
return StickerPack(
id: json['id'] as String,
name: json['name'] as String,
icon: json['icon'] as String? ?? '🎭',
source: SpritesheetSource.values.firstWhere(
(s) => s.key == json['source'],
orElse: () => SpritesheetSource.builtin,
),
stickers:
(json['stickers'] as List?)
?.map(
(s) => SpritesheetSticker.fromJson(s as Map<String, dynamic>),
)
.toList() ??
[],
coverUri: json['coverUri'] as String?,
downloadUrl: json['downloadUrl'] as String?,
fileSize: json['fileSize'] as int?,
isDownloaded: json['isDownloaded'] as bool? ?? false,
);
}
Map<String, dynamic> toJson() => {
'id': id,
'name': name,
'icon': icon,
'source': source.key,
'stickers': stickers.map((s) => s.toJson()).toList(),
'coverUri': coverUri,
'downloadUrl': downloadUrl,
'fileSize': fileSize,
'isDownloaded': isDownloaded,
};
StickerPack copyWith({
String? id,
String? name,
String? icon,
SpritesheetSource? source,
List<SpritesheetSticker>? stickers,
String? coverUri,
String? downloadUrl,
int? fileSize,
bool? isDownloaded,
}) {
return StickerPack(
id: id ?? this.id,
name: name ?? this.name,
icon: icon ?? this.icon,
source: source ?? this.source,
stickers: stickers ?? this.stickers,
coverUri: coverUri ?? this.coverUri,
downloadUrl: downloadUrl ?? this.downloadUrl,
fileSize: fileSize ?? this.fileSize,
isDownloaded: isDownloaded ?? this.isDownloaded,
);
}
}