Merge branch 'p7-w4-parallax-css-scroll' into dev
This commit is contained in:
@@ -78,7 +78,7 @@ public abstract class ParallaxImageBase : ComponentBase, IAsyncDisposable
|
|||||||
[Parameter] public string? Class { get; set; }
|
[Parameter] public string? Class { get; set; }
|
||||||
|
|
||||||
protected ElementReference WindowRef;
|
protected ElementReference WindowRef;
|
||||||
protected string WindowStyle { get; private set; } = "--window-height: 300px; --parallax-pos: 0%;";
|
protected string WindowStyle { get; private set; } = "--window-height: 300px; --parallax-from: 0%; --parallax-to: 50%;";
|
||||||
|
|
||||||
private bool AspectRatioMode =>
|
private bool AspectRatioMode =>
|
||||||
WindowHeight is null && NaturalWidth is > 0 && NaturalHeight is > 0;
|
WindowHeight is null && NaturalWidth is > 0 && NaturalHeight is > 0;
|
||||||
@@ -86,11 +86,27 @@ public abstract class ParallaxImageBase : ComponentBase, IAsyncDisposable
|
|||||||
private IJSObjectReference? _module;
|
private IJSObjectReference? _module;
|
||||||
private string? _handle;
|
private string? _handle;
|
||||||
|
|
||||||
|
// --parallax-from/--parallax-to are inherited custom properties read by the
|
||||||
|
// CSS @keyframes parallax-pan (scroll-driven animation on .parallax-window).
|
||||||
|
// They encode ParallaxSpeed and InvertDirection. --parallax-pos itself is
|
||||||
|
// never set inline — an inline value would beat the CSS animation in the
|
||||||
|
// cascade and defeat the pre-WASM scroll-driven parallax. JS sets it inline
|
||||||
|
// only after WASM boots, transparently taking over (inline > animation).
|
||||||
|
private string ParallaxVars()
|
||||||
|
{
|
||||||
|
var end = (int)Math.Round(ParallaxSpeed * 100);
|
||||||
|
end = Math.Max(1, end); // floor at 1% so the property is always valid
|
||||||
|
return InvertDirection
|
||||||
|
? $"--parallax-from: {end}%; --parallax-to: 0%;"
|
||||||
|
: $"--parallax-from: 0%; --parallax-to: {end}%;";
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
|
var pv = ParallaxVars();
|
||||||
if (WindowHeight != null)
|
if (WindowHeight != null)
|
||||||
{
|
{
|
||||||
WindowStyle = $"--window-height: {WindowHeight}; --parallax-pos: 0%;";
|
WindowStyle = $"--window-height: {WindowHeight}; {pv}";
|
||||||
}
|
}
|
||||||
else if (AspectRatioMode)
|
else if (AspectRatioMode)
|
||||||
{
|
{
|
||||||
@@ -99,11 +115,11 @@ public abstract class ParallaxImageBase : ComponentBase, IAsyncDisposable
|
|||||||
// aspect-ratio: W / (H * fraction) → window is WindowHeightFraction
|
// aspect-ratio: W / (H * fraction) → window is WindowHeightFraction
|
||||||
// of the image's full display height at container width.
|
// of the image's full display height at container width.
|
||||||
var h = Math.Max(1, (int)Math.Round(NaturalHeight!.Value * WindowHeightFraction));
|
var h = Math.Max(1, (int)Math.Round(NaturalHeight!.Value * WindowHeightFraction));
|
||||||
WindowStyle = $"height: auto; aspect-ratio: {NaturalWidth!.Value} / {h}; --parallax-pos: 0%;";
|
WindowStyle = $"height: auto; aspect-ratio: {NaturalWidth!.Value} / {h}; {pv}";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
WindowStyle = "--window-height: 300px; --parallax-pos: 0%;";
|
WindowStyle = $"--window-height: 300px; {pv}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
@property --parallax-pos {
|
||||||
|
syntax: '<percentage>';
|
||||||
|
inherits: true;
|
||||||
|
initial-value: 0%;
|
||||||
|
}
|
||||||
|
|
||||||
.parallax-window {
|
.parallax-window {
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -37,6 +43,39 @@
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Cascade interaction for --parallax-pos:
|
||||||
|
*
|
||||||
|
* Before WASM (SSR / hydration):
|
||||||
|
* @property initial-value: 0%
|
||||||
|
* → CSS animation (view timeline) overrides → correct position from scroll
|
||||||
|
*
|
||||||
|
* After WASM (JS running):
|
||||||
|
* JS element.style.setProperty('--parallax-pos', ...) [inline style]
|
||||||
|
* → inline style beats animation → JS takes over seamlessly
|
||||||
|
*
|
||||||
|
* prefers-reduced-motion:
|
||||||
|
* animation: none → @property initial-value 0% used → static image
|
||||||
|
* JS also skips scroll listener
|
||||||
|
*/
|
||||||
|
@supports (animation-timeline: view()) {
|
||||||
|
@keyframes parallax-pan {
|
||||||
|
from { --parallax-pos: var(--parallax-from, 0%); }
|
||||||
|
to { --parallax-pos: var(--parallax-to, 0%); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.parallax-window {
|
||||||
|
animation: parallax-pan linear both;
|
||||||
|
animation-timeline: view();
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.parallax-window {
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
.parallax-window {
|
.parallax-window {
|
||||||
--parallax-pos: 0%;
|
--parallax-pos: 0%;
|
||||||
|
|||||||
Reference in New Issue
Block a user