fix: smooth persona hover transitions
Preserve the original reflection material while adding pointer inertia and a short exit hold. Retain the Rive pointer constraint through the fade and blend rapid re-entry to avoid abrupt changes. Cover timing, re-entry, cancellation, renderer disposal and native Rive frames. The fade regression fails with the previous resource and passes with the updated resource.
This commit is contained in:
@@ -12,6 +12,7 @@ const designUrl = new URL("../design/agent/obsidian-offline.json", import.meta.u
|
||||
const defaultDesign = JSON.parse(await readFile(designUrl, "utf8"));
|
||||
const materialUrl = new URL("../design/agent/obsidian-material.json", import.meta.url);
|
||||
const defaultMaterial = JSON.parse(await readFile(materialUrl, "utf8"));
|
||||
const hoverTiming = JSON.parse(await readFile(new URL("../src/shared/ai-elements/persona-hover-timing.json", import.meta.url), "utf8"));
|
||||
|
||||
function uint(value) {
|
||||
if (!Number.isSafeInteger(value) || value < 0) throw new Error(`Invalid Rive uint: ${value}`);
|
||||
@@ -172,14 +173,40 @@ export function buildOfflinePersona(source, offline = defaultDesign, material =
|
||||
const components = objects.slice(artboard, firstAnimation).filter((object) => object.type !== 447);
|
||||
|
||||
let animation;
|
||||
let machineLayer;
|
||||
for (const object of objects) {
|
||||
if (object.type === 31) animation = valueOf(object, 55);
|
||||
if (object.type === 31) {
|
||||
animation = valueOf(object, 55);
|
||||
if (animation === "hover_in" || animation === "hover_out") {
|
||||
setValue(object, 57, Math.round((animation === "hover_in" ? hoverTiming.enterMs : hoverTiming.exitMs) * 60 / 1000));
|
||||
}
|
||||
}
|
||||
// Change timing only, keeping the original reflection geometry and color.
|
||||
if ((animation === "hover_in" || animation === "hover_out") && object.type === 30) {
|
||||
const durationMs = animation === "hover_in" ? hoverTiming.enterMs : hoverTiming.exitMs;
|
||||
setValue(object, 67, Math.round(valueOf(object, 67, 0) * durationMs / 1000));
|
||||
setValue(object, 68, 2);
|
||||
setValue(object, 69, 261);
|
||||
}
|
||||
if (object.type === 57) machineLayer = valueOf(object, 138);
|
||||
if (machineLayer === "hover" && object.type === 65 && valueOf(object, 152, 0) === 12) {
|
||||
setValue(object, 158, hoverTiming.blendMs);
|
||||
}
|
||||
// These tracks only control opacity and scale. Keep the complete sphere.
|
||||
if ((animation === "asleep" || animation === "wake") && object.type === 30) {
|
||||
setValue(object, 70, 1);
|
||||
}
|
||||
}
|
||||
|
||||
const hoverOutEnd = objects.findIndex((object) => object.type === 31 && valueOf(object, 55) === "on_loop_1");
|
||||
// Keep the pointer constraint active until the outgoing reflection is fully
|
||||
// transparent. The original hover_off timeline releases it afterwards.
|
||||
objects.splice(hoverOutEnd, 0,
|
||||
{ type: 25, values: [[51, 129]] },
|
||||
{ type: 26, values: [[53, 172]] },
|
||||
{ type: 30, values: [[68, 1], [70, 1]] }
|
||||
);
|
||||
|
||||
const loops = [];
|
||||
for (let number = 1; number <= 3; number++) {
|
||||
const start = objects.findIndex((object) => object.type === 31 && valueOf(object, 55) === `on_loop_${number}`);
|
||||
|
||||
Reference in New Issue
Block a user