mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-27 20:49:53 +08:00
site: use vanilla-tilt.js for banner motion
This commit is contained in:
parent
3233ac498f
commit
5aafe6d39a
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
33
.dumi/pages/index/components/PreviewBanner/Tilt.tsx
Normal file
33
.dumi/pages/index/components/PreviewBanner/Tilt.tsx
Normal 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;
|
@ -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} />
|
||||||
|
|
||||||
|
@ -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;
|
|
@ -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"
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user