feat(core): ignoreParentAlpha — render a node at its own alpha while ancestors fade#100
Merged
Conversation
…ancestors fade Adds an ignoreParentAlpha boolean prop to CoreNode. When enabled, the node's world alpha is computed from its own alpha only instead of parent.worldAlpha * alpha, so the node stays visible while its parent (and the rest of the subtree) fades — including at parent alpha 0. The node's own alpha still applies and its descendants inherit its world alpha as usual. The render list builder culls whole subtrees at worldAlpha === 0, which would make an ignoring child disappear exactly when a fade-out completes. To keep that cull correct, each node now maintains an ignoreParentAlphaCount (nodes in its subtree, itself included, with the prop enabled), updated on the cold paths — prop setter, addChild/ removeChild, and construction — so the hot-path cull only gains one comparison that short-circuits unless worldAlpha is already 0. Counts err toward traversal (conservative skip). No effect inside RTT subtrees (the composited quad still fades as one unit); documented on the prop. Includes unit tests for the alpha math, renderability under an alpha-0 parent, premultiplied colors, toggling, and counter propagation through attach/toggle/reparent, plus a visual regression test with certified snapshot covering rects and text nodes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…worldAlpha 0 Removes the subtree counter that let the render-list builder traverse into fully transparent subtrees containing ignoreParentAlpha nodes. The worldAlpha === 0 cull now applies unconditionally: ignoreParentAlpha only has an effect while every ancestor's alpha is above 0, and the node disappears with the subtree once an ancestor reaches exactly 0. This trades the alpha-0 edge case for zero bookkeeping on the prop setter, attach/detach, and construction paths, and restores the original single-comparison cull. Documented on the prop's TSDoc; unit and visual tests updated to pin the new semantics. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds an
ignoreParentAlphaboolean prop to nodes. When enabled, the node's world alpha is computed from its ownalphaonly — instead ofparent.worldAlpha * alpha— so the node keeps rendering while its parent (and the rest of the subtree) fades.alphastill applies, and can still be animated.INode/INodeProps.Why
A child's effective alpha is always multiplied by its ancestors', so there was no way to keep an overlay (scrim gradient, badge, focus ring) at full strength while its container fades. The earlier workaround — restructuring the overlay as a sibling of the fading container — forces awkward scene-graph layouts and breaks encapsulation in component frameworks.
How
A 3-line branch in the
WorldAlphaupdate plus a getter/setter raising the same update flags asalpha. Render-list invalidation needed no new triggers: toggling the prop flips the node'sisRenderable, which already requests a rebuild.Reviewer notes
Stage.buildRenderListculls whole subtrees atworldAlpha === 0, unconditionally. So the prop only has an effect while every ancestor's alpha is above 0 — once an ancestor reaches exactly 0, the ignoring node is culled with the subtree. This keeps the fully-transparent cull free of bookkeeping (an earlier revision tracked a subtree counter to render through alpha-0 ancestors; it was dropped in favor of the simpler cull). Documented in the prop's TSDoc and pinned by the visual test.worldAlpha+ premultiplied colors), so WebGL and Canvas2D agree.Testing
CoreNode.test.ts: default multiply, ignore behavior, own world alpha while the parent fades toward 0, premultiplied colors from own alpha, toggle recompute, no-op set short-circuit, and descendant inheritance. Full suite 225/225 green; lint clean.examples/tests/alpha-ignore-parent.tswith certified CI snapshot (captured via the Docker runner, manually inspected): faded parent vs ignoring child, near-zero parent (0.05) with a solid ignoring child, own-alpha + descendant inheritance, text nodes, and toggle-back-off.🤖 Generated with Claude Code