Jianying Text Animation Reference
Verified 2026-08-16 against the 23-lib PrivateRuntimes closure
(~/Library/Application Support/qcut/PrivateRuntimes/JianyingTransition/current/Frameworks)
and the local effect cache. Treat a Jianying text animation as four layers:
catalog card (rp.db http_cache, panel ruchang/chuchang/xunhuan)
-> Cache/effect/<resource_id>/<md5>/ (studioAnim.lsanim package)
-> AE-style text animator model (selectors + effectAnimators + cubic keyframes)
-> Swing sticker/text segment runtime (libcccreator AETextAnimator)
1. Catalog layer
Text animations live in the panel with category keys ruchang(入场, id 2066) /
chuchang(出场, 2067) / xunhuan(循环, 2133) plus caption_animation(字幕动画,
39829). Full counts (beta 2026-08-15, has_more=false, exact): 入场 200,
出场 136, 循环 127, 字幕动画 156; ~65% VIP. The complete lists sit in
get_panel_info responses in Cache/ressdk_db/*/rp.db http_cache —
preloaded with the full effect_item_list per category, so unlike sound
effects there is no pagination to chase. A neighbouring panel with keys
in/out/group (3340/3341/3342: 200/200/155) is the video/sticker animation
panel, not text.
2. Package anatomy
Cache/effect/<resource_id>/<md5>/ for a text animation contains:
| File | Meaning |
|---|---|
config.json |
script type, animation document path, package version |
studioAnim.lsanim |
THE animation document (see §3). Some are plaintext JSON, some are encrypted — check whether the file starts with { |
textAnim.lsproj |
node graph wiring the animator into the text render pipeline; usually plaintext |
res/*.jsdat |
expression-selector script, encrypted under its own format |
Roughly half of a typical local cache is plaintext. Directory names are opaque resource ids; the human-readable name mapping lives in the catalog database.
Do not record specific resource ids, cached file inventories, or package name lists in this repo. Work from whatever the local cache happens to hold at the time, and keep only the structural conclusions here.
3. The animation model (plaintext studioAnim.lsanim)
Top-level studio_anim_params has two arrays — this is After Effects' text
animator model, executed by AERender/data/AETextAnimator.cpp:
{
"studio_anim_params": {
"selectors": [{
"class": "Text_BaseSelector",
"selector_attrs": { // AE range selector
"class": "...",
"start": {"value": 0, "motionKeyFrameInfo": [...]}, // animated range!
"end": {"value": 1, "motionKeyFrameInfo": [...]},
"offset": {...}, "shape": {...}, "basedOn": {...},
"randomSort": {...}, "randomSeed": {...},
"smooth": {...}, "constrained": {...}, "customShape": {...}
},
"base_attrs": { // properties applied per selected unit
"position": {"type", "distance", "x", "y"},
"color": {"motionKeyFrameInfo": [...]}, // per-char color IS a channel
"scale": {"x", "y", "separation"},
"alpha": {...}, "rotate": {...},
"anchor": {"mode", "based", "x", "y"},
"custom_script": {"path": "/res/<ts>.jsdat"} // expression selector
}
}],
"effectAnimators": [{
"effects": [{ "name": "SoftGlow", "caption": "柔光",
"path"/"prefabName", "params": {..motionKeyFrameInfo..} }],
"renderGroup_attrs": { "mode", "shape", "expandRatioX/Y", "isRDGMode",
"priority", "randomSeed", "randomSort", "duration" }
}]
}
}
- Keyframes:
motionKeyFrameInfo: [{t, v, vi, vo, vti, vto, it: "cubic"}]— cubic bezier with explicit in/out values and tangents. Times are seconds within the anim window (animDurationscales them at runtime). - Observed effect names across local packages: SoftGlow, DeepGlowSimple, RadianceGlow, GaussianBlur, DirectionalBlurs, RadialBlur, LinearWipe, Dust.
- Runtime error strings confirm selector fields:
based,rangeUnit,shapebounds-checked;unsupport expression selectorwhen jsdat class is unknown. - QCut mapping: selector ≈ our
phaseUnitProgress+ stagger;base_attrs≈ our effect kinds;motionKeyFrameInfo≈ our easing curves. Two things we lack: a per-unit color channel and the effectAnimators post-effect chain (glow/blur applied to the animated text as a render group).
4. Runtime invocation (libcccreator)
Same Swing API family as the video-effect recipe (see the
jianying-video-effect-reference skill and its memory notes for the manager
setup, EnableSwingSimplify, and the full-runtime-closure trap):
SwingSegmentType: 3 = Text, 2 = Sticker (text renders through the sticker pipeline:Sticker2DV3Filter,InfoStickerManager).- Content:
bef_swing_segment_sticker_set_rich_text/bef_swing_segment_model_control_set_rich_text;bef_swing_rich_text_xml_to_jsonconverts the draft's rich-text XML. - Animation attach:
bef_swing_segment_sticker_set_animation,..._set_animation_property,..._set_entrance_time,..._set_anim_absolute_update; equivalents for video:bef_swing_segment_video_set_animation(_with_type). - The param blob uses keys
animInPath/animInDuration/animInType,animOutPath/animOutDuration/animOutType,animLoopPath/animLoopDuration/animLoopType,animPropertyKey/animPropertyValue,animJsonParam,animSeq— i.e. one call carries all three phases as package paths + durations. - Runtime guards worth knowing:
animIn->startTime < 0! Will resetandanimOut->startTime < animsIn->endTime!! Will reset out anim startTime and duration— out-anim is clamped after in-anim, like ourfitEdgeFrames. - Sequence-frame anims are separate (
animSeq,bef_swing_manager_set_anim_seq_*).
5. Draft layer
Segments reference materials.material_animations[] entries through
extra_material_refs; one entry bundles all phases in its animations[].
The current draft format is encrypted, so exact field names are not verified
here.
Do not read the user's local draft files to work around that. If field names ever matter, confirm them against a draft this project itself produced, or a synthetic fixture — not against whatever happens to be in the user's project folder.
6. Traps
- Both the animation document and the expression scripts have encrypted variants — don't attempt decryption; use the plaintext ones as ground truth.
- rp.db must be snapshotted (
sqlite3 ".backup") while 剪映 runs; bun:sqlite cannot open any db in the sandboxed shell — pre-export JSON via the sqlite3 CLI (see sound-effects batch-03 notes in docs/task/sound-effects-lab/). http_cachetimestamps are UTC.- Applying an animation in the UI does not necessarily persist plaintext evidence anywhere — the whole current-draft tier is opaque.
- For native probes reuse the video-effect recipe verbatim (23-lib closure,
EnableSwingSimplify, segments spanning the whole clip, sweep timestamps — many anims are identity at a given t).
7. Reproduce in QCut — suggested order
- Read a plaintext package for an animation we already ported (波浪挤压,
粒子碎落) and diff its
motionKeyFrameInfocurves against our effect params — calibrates the reading of the format itself. - 变色弹跳's package is the reference for adding a per-unit color channel to
TextAnimationVisualState(the single biggest capability gap; unlocks ~30 剪映 effects). - Then the effectAnimators post-chain (glow/blur render group) for 文字泛光 / 闪色循环-class looks.
- Native render probe of a text segment (type 3) is the parity oracle when
package math is ambiguous — extend
research/jianying-runtime-probe.
Do not copy Jianying scripts, packages, or assets into QCut; reimplement observed behavior with original code. VIP/free flags are product access state, not a redistribution license.