anim: remove hero signal

* BottomSheet: remove hero support
* use StackedRouter's hero support instead
This commit is contained in:
thislight 2024-11-19 17:49:49 +08:00
parent 8588a17bd0
commit 15974af792
No known key found for this signature in database
GPG key ID: FCFE5192241CCD4E
3 changed files with 3 additions and 144 deletions

View file

@ -9,12 +9,10 @@ import {
type ResolvedChildren, type ResolvedChildren,
} from "solid-js"; } from "solid-js";
import "./BottomSheet.css"; import "./BottomSheet.css";
import { useHeroSignal } from "../platform/anim";
import material from "./material.module.css"; import material from "./material.module.css";
import { import {
ANIM_CURVE_ACELERATION, ANIM_CURVE_ACELERATION,
ANIM_CURVE_DECELERATION, ANIM_CURVE_DECELERATION,
ANIM_CURVE_STD,
} from "./theme"; } from "./theme";
import { import {
animateSlideInFromRight, animateSlideInFromRight,
@ -28,32 +26,11 @@ export type BottomSheetProps = {
onClose?(reason: "backdrop"): void; onClose?(reason: "backdrop"): void;
}; };
export const HERO = Symbol("BottomSheet Hero Symbol");
function composeAnimationFrame(
{
top,
left,
height,
width,
}: Record<"top" | "left" | "height" | "width", number>,
x: Record<string, unknown>,
) {
return {
top: `${top}px`,
left: `${left}px`,
height: `${height}px`,
width: `${width}px`,
...x,
};
}
const MOVE_SPEED = 1600; const MOVE_SPEED = 1600;
const BottomSheet: ParentComponent<BottomSheetProps> = (props) => { const BottomSheet: ParentComponent<BottomSheetProps> = (props) => {
let element: HTMLDialogElement; let element: HTMLDialogElement;
let animation: Animation | undefined; let animation: Animation | undefined;
const [hero, setHero] = useHeroSignal(HERO);
const [cache, setCache] = createSignal<ResolvedChildren | undefined>(); const [cache, setCache] = createSignal<ResolvedChildren | undefined>();
const ochildren = children(() => props.children); const ochildren = children(() => props.children);
@ -74,24 +51,11 @@ const BottomSheet: ParentComponent<BottomSheetProps> = (props) => {
}); });
const onClose = () => { const onClose = () => {
const srcElement = hero();
if (srcElement) {
srcElement.style.visibility = "unset";
}
element.close(); element.close();
setHero();
}; };
const animatedClose = () => { const animatedClose = () => {
const srcElement = hero();
const endRect = srcElement?.getBoundingClientRect();
if (endRect) {
const startRect = element.getBoundingClientRect();
const animation = animateHero(startRect, endRect, element, true);
animation.addEventListener("finish", onClose);
animation.addEventListener("cancel", onClose);
} else {
if (window.innerWidth > 560 && !props.bottomUp) { if (window.innerWidth > 560 && !props.bottomUp) {
onClose(); onClose();
return; return;
@ -106,21 +70,12 @@ const BottomSheet: ParentComponent<BottomSheetProps> = (props) => {
: animateSlideOutToRight(element, { easing: ANIM_CURVE_ACELERATION }); : animateSlideOutToRight(element, { easing: ANIM_CURVE_ACELERATION });
animation.addEventListener("finish", onAnimationEnd); animation.addEventListener("finish", onAnimationEnd);
animation.addEventListener("cancel", onAnimationEnd); animation.addEventListener("cancel", onAnimationEnd);
}
}; };
const animatedOpen = () => { const animatedOpen = () => {
element.showModal(); element.showModal();
const srcElement = hero(); if (props.bottomUp) {
const startRect = srcElement?.getBoundingClientRect();
if (!startRect) {
console.debug("no source element");
}
if (startRect) {
srcElement!.style.visibility = "hidden";
const endRect = element.getBoundingClientRect();
animateHero(startRect, endRect, element);
} else if (props.bottomUp) {
animateSlideInFromBottom(element); animateSlideInFromBottom(element);
} else if (window.innerWidth <= 560) { } else if (window.innerWidth <= 560) {
element.classList.add("animated"); element.classList.add("animated");
@ -165,41 +120,6 @@ const BottomSheet: ParentComponent<BottomSheetProps> = (props) => {
return animation; return animation;
}; };
const animateHero = (
startRect: DOMRect,
endRect: DOMRect,
element: HTMLElement,
reserve?: boolean,
) => {
const easing = ANIM_CURVE_STD;
element.classList.add("animated");
// distance_lt = (|top_start - top_end|^2 + |left_end - left_end|^2)^(-2)
const distancelt = Math.sqrt(
Math.pow(Math.abs(startRect.top - endRect.top), 2) +
Math.pow(Math.abs(startRect.left - endRect.left), 2),
);
const distancerb = Math.sqrt(
Math.pow(Math.abs(startRect.bottom - endRect.bottom), 2) +
Math.pow(Math.abs(startRect.right - endRect.right), 2),
);
const distance = distancelt + distancerb;
const duration = distance / 1.6;
animation = element.animate(
[
composeAnimationFrame(startRect, { transform: "none" }),
composeAnimationFrame(endRect, { transform: "none" }),
],
{ easing, duration },
);
const onAnimationEnd = () => {
element.classList.remove("animated");
animation = undefined;
};
animation.addEventListener("finish", onAnimationEnd);
animation.addEventListener("cancel", onAnimationEnd);
return animation;
};
onCleanup(() => { onCleanup(() => {
if (animation) { if (animation) {
animation.cancel(); animation.cancel();

View file

@ -1,58 +1,3 @@
import {
createContext,
createRenderEffect,
createSignal,
untrack,
useContext,
type Accessor,
type Signal,
} from "solid-js";
export type HeroSource = {
[key: string | symbol | number]: HTMLElement | undefined;
};
const HeroSourceContext = createContext<Signal<HeroSource>>(
/* __@PURE__ */ undefined,
);
export const HeroSourceProvider = HeroSourceContext.Provider;
export function useHeroSource() {
return useContext(HeroSourceContext);
}
/**
* Use hero value for the {@link key}.
*
* Note: the setter here won't set the value of the hero source.
* To set hero source, use {@link useHeroSource} and set the corresponding key.
*/
export function useHeroSignal(
key: string | symbol | number,
): Signal<HTMLElement | undefined> {
const source = useHeroSource();
if (source) {
const [get, set] = createSignal<HTMLElement>();
createRenderEffect(() => {
const value = source[0]();
if (value[key]) {
set(value[key]);
source[1]((x) => {
const cpy = Object.assign({}, x);
delete cpy[key];
return cpy;
});
}
});
return [get, set];
} else {
console.debug("no hero source");
return [() => undefined, () => undefined];
}
}
export function animateRollOutFromTop( export function animateRollOutFromTop(
root: HTMLElement, root: HTMLElement,

View file

@ -10,8 +10,6 @@ import {
import { type mastodon } from "masto"; import { type mastodon } from "masto";
import { vibrate } from "../platform/hardware"; import { vibrate } from "../platform/hardware";
import { useDefaultSession } from "../masto/clients"; import { useDefaultSession } from "../masto/clients";
import { useHeroSource } from "../platform/anim";
import { HERO as BOTTOM_SHEET_HERO } from "../material/BottomSheet";
import { setCache as setTootBottomSheetCache } from "./TootBottomSheet"; import { setCache as setTootBottomSheetCache } from "./TootBottomSheet";
import RegularToot, { import RegularToot, {
findElementActionable, findElementActionable,
@ -53,7 +51,6 @@ const TootList: Component<{
onChangeToot: (id: string, value: mastodon.v1.Status) => void; onChangeToot: (id: string, value: mastodon.v1.Status) => void;
}> = (props) => { }> = (props) => {
const session = useDefaultSession(); const session = useDefaultSession();
const heroSrc = useHeroSource();
const [expandedThreadId, setExpandedThreadId] = createSignal<string>(); const [expandedThreadId, setExpandedThreadId] = createSignal<string>();
const { push } = useNavigator(); const { push } = useNavigator();
@ -124,9 +121,6 @@ const TootList: Component<{
console.warn("no account info?"); console.warn("no account info?");
return; return;
} }
if (heroSrc) {
heroSrc[1]((x) => ({ ...x, [BOTTOM_SHEET_HERO]: srcElement }));
}
const acct = `${inf.username}@${p.site}`; const acct = `${inf.username}@${p.site}`;
setTootBottomSheetCache(acct, toot); setTootBottomSheetCache(acct, toot);