site: use vanilla-tilt.js for banner motion

This commit is contained in:
afc163 2023-11-22 17:57:53 +08:00
parent 3233ac498f
commit 5aafe6d39a
5 changed files with 51 additions and 83 deletions

View File

@ -19,6 +19,7 @@ import {
import { createStyles } from 'antd-style'; import { createStyles } from 'antd-style';
import classNames from 'classnames'; import classNames from 'classnames';
import Tilt from './Tilt';
import useLocale from '../../../../hooks/useLocale'; import useLocale from '../../../../hooks/useLocale';
const { _InternalPanelDoNotUseOrYouWillBeFired: ModalPanel } = Modal; const { _InternalPanelDoNotUseOrYouWillBeFired: ModalPanel } = Modal;
@ -79,7 +80,7 @@ const useStyle = createStyles(({ token, css }) => {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
row-gap: ${gap}px; row-gap: ${gap}px;
opacity: 0.65; opacity: 0.8;
`, `,
flex: css` flex: css`
@ -117,7 +118,11 @@ const ComponentsBlock: React.FC<ComponentsBlockProps> = (props) => {
const { styles } = useStyle(); const { styles } = useStyle();
return ( return (
<div className={classNames(className, styles.holder)} style={style}> <Tilt
options={{ max: 20, glare: true, scale: 1 }}
className={classNames(className, styles.holder)}
style={style}
>
<ModalPanel title="Ant Design 5.0" width="100%"> <ModalPanel title="Ant Design 5.0" width="100%">
{locale.text} {locale.text}
</ModalPanel> </ModalPanel>
@ -252,7 +257,7 @@ const ComponentsBlock: React.FC<ComponentsBlockProps> = (props) => {
<InternalTooltip title={locale.hello} placement="topLeft" className={styles.noMargin} /> <InternalTooltip title={locale.hello} placement="topLeft" className={styles.noMargin} />
<Alert message="Ant Design love you!" type="success" /> <Alert message="Ant Design love you!" type="success" />
</div> </Tilt>
); );
}; };

View File

@ -0,0 +1,33 @@
import React, { useEffect, useRef } from 'react';
import VanillaTilt, { type TiltOptions } from 'vanilla-tilt';
interface TiltProps extends React.HTMLAttributes<HTMLDivElement> {
options?: TiltOptions;
}
// https://micku7zu.github.io/vanilla-tilt.js/index.html
const defaultTiltOptions = {
scale: 1.02,
max: 8,
speed: 1500,
glare: true,
'max-glare': 0.8,
};
const Tilt: React.FC<TiltProps> = ({ options, ...props }) => {
const node = useRef<HTMLDivElement>(null);
useEffect(() => {
if (node.current) {
VanillaTilt.init(node.current, {
...defaultTiltOptions,
...options,
});
}
return () => {
(node.current as any)?.vanillaTilt.destroy();
};
}, []);
return <div ref={node} {...props} />;
};
export default Tilt;

View File

@ -7,7 +7,6 @@ import useLocale from '../../../../hooks/useLocale';
import SiteContext from '../../../../theme/slots/SiteContext'; import SiteContext from '../../../../theme/slots/SiteContext';
import * as utils from '../../../../theme/utils'; import * as utils from '../../../../theme/utils';
import { GroupMask } from '../Group'; import { GroupMask } from '../Group';
import useMouseTransform from './useMouseTransform';
const ComponentsBlock = React.lazy(() => import('./ComponentsBlock')); const ComponentsBlock = React.lazy(() => import('./ComponentsBlock'));
@ -28,7 +27,6 @@ const locales = {
const useStyle = () => { const useStyle = () => {
const { direction } = React.useContext(ConfigProvider.ConfigContext); const { direction } = React.useContext(ConfigProvider.ConfigContext);
const isRTL = direction === 'rtl'; const isRTL = direction === 'rtl';
return createStyles(({ token, css, cx }) => { return createStyles(({ token, css, cx }) => {
const textShadow = `0 0 3px ${token.colorBgContainer}`; const textShadow = `0 0 3px ${token.colorBgContainer}`;
@ -37,7 +35,9 @@ const useStyle = () => {
inset: 0; inset: 0;
backdrop-filter: blur(4px); backdrop-filter: blur(4px);
opacity: 1; opacity: 1;
transition: opacity 1s ease; background: rgba(255, 255, 255, 0.2);
transition: all 1s ease;
pointer-events: none;
`); `);
return { return {
@ -114,10 +114,8 @@ const PreviewBanner: React.FC<PreviewBannerProps> = (props) => {
const { pathname, search } = useLocation(); const { pathname, search } = useLocation();
const isZhCN = utils.isZhCN(pathname); const isZhCN = utils.isZhCN(pathname);
const [componentsBlockStyle, mouseEvents] = useMouseTransform();
return ( return (
<GroupMask {...mouseEvents}> <GroupMask>
{/* Image Left Top */} {/* Image Left Top */}
<img <img
style={{ position: 'absolute', left: isMobile ? -120 : 0, top: 0, width: 240 }} style={{ position: 'absolute', left: isMobile ? -120 : 0, top: 0, width: 240 }}
@ -134,7 +132,11 @@ const PreviewBanner: React.FC<PreviewBannerProps> = (props) => {
<div className={styles.holder}> <div className={styles.holder}>
{/* Mobile not show the component preview */} {/* Mobile not show the component preview */}
<Suspense fallback={null}> <Suspense fallback={null}>
{!isMobile && <ComponentsBlock className={styles.block} style={componentsBlockStyle} />} {isMobile ? null : (
<div className={styles.block}>
<ComponentsBlock />
</div>
)}
</Suspense> </Suspense>
<div className={styles.mask} /> <div className={styles.mask} />

View File

@ -1,73 +0,0 @@
import React, { startTransition } from 'react';
import { ConfigProvider } from 'antd';
const getTransformRotateStyle = (
event: React.MouseEvent<HTMLDivElement, MouseEvent>,
currentTarget: EventTarget & HTMLDivElement,
multiple: number,
isRTL: boolean,
): string => {
const box = currentTarget?.getBoundingClientRect();
const calcX = -(event.clientY - box.y - box.height / 2) / multiple;
const calcY = (event.clientX - box.x - box.width / 2) / multiple;
return isRTL
? `rotate3d(${24 + calcX}, ${83 + calcY}, -45, 57deg)`
: `rotate3d(${24 + calcX}, ${-83 + calcY}, 45, 57deg)`;
};
const useMouseTransform = ({ transitionDuration = 500, multiple = 36 } = {}) => {
const [componentsBlockStyle, setComponentsBlockStyle] = React.useState<React.CSSProperties>({});
const { direction } = React.useContext(ConfigProvider.ConfigContext);
const isRTL = direction === 'rtl';
const onMouseMove: React.MouseEventHandler<HTMLDivElement> = (event) => {
const { currentTarget } = event;
startTransition(() => {
setComponentsBlockStyle((style) => ({
...style,
transform: getTransformRotateStyle(event, currentTarget, multiple, isRTL),
}));
});
};
const onMouseEnter: React.MouseEventHandler<HTMLDivElement> = () => {
startTransition(() => {
setComponentsBlockStyle((style) => ({
...style,
transition: `transform ${transitionDuration / 1000}s`,
}));
});
setTimeout(() => {
startTransition(() => {
setComponentsBlockStyle((style) => ({
...style,
transition: '',
}));
});
}, transitionDuration);
};
const onMouseLeave: React.MouseEventHandler<HTMLDivElement> = () => {
startTransition(() => {
setComponentsBlockStyle((style) => ({
...style,
transition: `transform ${transitionDuration / 1000}s`,
transform: '',
}));
});
};
return [
componentsBlockStyle,
{
onMouseMove,
onMouseEnter,
onMouseLeave,
},
] as const;
};
export default useMouseTransform;

View File

@ -310,6 +310,7 @@
"typedoc": "^0.25.0", "typedoc": "^0.25.0",
"typescript": "~5.2.2", "typescript": "~5.2.2",
"vanilla-jsoneditor": "^0.18.0", "vanilla-jsoneditor": "^0.18.0",
"vanilla-tilt": "^1.8.1",
"webpack-bundle-analyzer": "^4.1.0", "webpack-bundle-analyzer": "^4.1.0",
"xhr-mock": "^2.4.1" "xhr-mock": "^2.4.1"
}, },