Browse Source

chore: effect of it

happy
二货机器人 2 years ago
parent
commit
8a729d6642
  1. 29
      components/_util/wave/WaveEffect.tsx
  2. 2
      components/button/demo/basic.tsx
  3. 6
      components/config-provider/index.tsx
  4. 234
      components/theme/themesToken/happy/style/wave.ts
  5. 30
      components/theme/themesToken/happy/wave.tsx

29
components/_util/wave/WaveEffect.tsx

@ -11,10 +11,12 @@ function validateNum(value: number) {
return Number.isNaN(value) ? 0 : value;
}
export interface WaveEffectProps extends Omit<WaveWrapperProps, 'token'> {}
export interface WaveEffectProps extends Omit<WaveWrapperProps, 'token'> {
onFinish: VoidFunction;
}
const WaveEffect: React.FC<WaveEffectProps> = (props) => {
const { className, target } = props;
const { className, target, onFinish } = props;
const divRef = React.useRef<HTMLDivElement>(null);
const [color, setWaveColor] = React.useState<string | null>(null);
@ -106,10 +108,7 @@ const WaveEffect: React.FC<WaveEffectProps> = (props) => {
motionDeadline={5000}
onAppearEnd={(_, event) => {
if (event.deadline || (event as TransitionEvent).propertyName === 'opacity') {
const holder = divRef.current?.parentElement!;
unmount(holder).then(() => {
holder.parentElement?.removeChild(holder);
});
onFinish();
}
return false;
}}
@ -126,16 +125,23 @@ export interface WaveWrapperProps {
target: HTMLElement;
token: ReturnType<typeof useToken>;
wave: ConfigProviderProps['wave'];
holder: HTMLElement;
}
function WaveWrapper(props: WaveWrapperProps) {
const { token, target, wave } = props;
const { token, target, wave, holder } = props;
function onFinish() {
unmount(holder).then(() => {
holder.parentElement?.removeChild(holder);
});
}
if (wave?.render) {
return wave?.render(target, token) as React.ReactElement;
return wave?.render({ target, token, onFinish }) as React.ReactElement;
}
return <WaveEffect {...props} />;
return <WaveEffect {...props} onFinish={onFinish} />;
}
export default function showWaveEffect(
@ -151,5 +157,8 @@ export default function showWaveEffect(
holder.style.top = `0px`;
node.parentElement?.appendChild(holder);
render(<WaveWrapper target={node} className={className} token={token} wave={wave} />, holder);
render(
<WaveWrapper target={node} className={className} token={token} wave={wave} holder={holder} />,
holder,
);
}

2
components/button/demo/basic.tsx

@ -2,7 +2,7 @@ import React from 'react';
import { Button, Space } from 'antd';
const App: React.FC = () => (
<Space wrap style={{ margin: 64 }}>
<Space wrap>
<Button type="primary">Primary Button</Button>
<Button>Default Button</Button>
<Button type="dashed">Dashed Button</Button>

6
components/config-provider/index.tsx

@ -84,7 +84,11 @@ export interface ConfigProviderProps {
showSizeChanger?: boolean;
};
wave?: {
render?: (target: HTMLElement, token: ReturnType<typeof useToken>) => React.ReactNode;
render?: (props: {
target: HTMLElement;
token: ReturnType<typeof useToken>;
onFinish: VoidFunction;
}) => React.ReactNode;
};
locale?: Locale;
pageHeader?: {

234
components/theme/themesToken/happy/style/wave.ts

@ -1,7 +1,7 @@
import { Keyframes, useStyleRegister } from '@ant-design/cssinjs';
import type { UseTokenType } from '../wave';
const antCheckboxEffect = new Keyframes('antCheckboxEffect', {
const antWaveTargetEffect = new Keyframes('antWaveTargetEffect', {
'0%': {
transform: 'scale(1)',
},
@ -22,19 +22,46 @@ const antCheckboxEffect = new Keyframes('antCheckboxEffect', {
},
});
const antWaveDotEffect = new Keyframes('antWaveDotEffect', {
'0%': {
opacity: 0,
left: `var(--start-x)`,
top: `var(--start-y)`,
width: `var(--start-size)`,
height: `var(--start-size)`,
background: `var(--background)`,
border: `var(--border)`,
},
'25%': {
opacity: 1,
},
'50%': {
opacity: 0.8,
},
'100%': {
opacity: 0,
left: `var(--end-x)`,
top: `var(--end-y)`,
width: `var(--end-size)`,
height: `var(--end-size)`,
background: `var(--background)`,
border: `var(--border)`,
},
});
export default function useStyle(tokenConfig: UseTokenType) {
const [theme, token, hashId] = tokenConfig;
useStyleRegister({ theme, token, hashId, path: ['Customize', 'Wave', 'Effect'] }, () => {
const { motionDurationSlow } = token;
const prefixCls = '.happy-wave';
const { colorPrimary, colorPrimaryBgHover, motionDurationFast, motionDurationSlow } = token;
const activeCls = '&.happy-in-out-appear-active';
return [
{
// ======================== Target ========================
[`${prefixCls}-target`]: {
animationName: antCheckboxEffect,
animationName: antWaveTargetEffect,
animationDuration: `0.45s`,
animationTimingFunction: 'ease-in-out',
animationFillMode: 'backwards',
@ -44,214 +71,25 @@ export default function useStyle(tokenConfig: UseTokenType) {
[prefixCls]: {
position: 'fixed',
pointerEvents: 'none',
zIndex: 999,
[`${prefixCls}-dot`]: {
boxSizing: 'border-box',
position: 'absolute',
// width: 8,
// height: 8,
borderRadius: '100%',
opacity: 0,
transform: 'translate(-50%, -50%)',
// display: 'none',
// Start Position
// left: '50%',
// top: '50%',
left: `var(--start-x)`,
top: `var(--start-y)`,
width: `var(--size)`,
height: `var(--size)`,
background: `var(--background)`,
border: `var(--border)`,
zIndex: 999,
// =================== Basic Motion ===================
'&.happy-in-out': {
transition: [
`opacity ${motionDurationSlow} linear`,
`width ${motionDurationSlow} linear`,
`height ${motionDurationSlow} linear`,
`left ${motionDurationSlow} ease-out`,
`top ${motionDurationSlow} ease-out`,
].join(','),
display: 'block',
'&-appear': {
// opacity: 1,
opacity: 0,
'&-active': {
left: `var(--end-x)`,
top: `var(--end-y)`,
// opacity: 0,
opacity: 1,
width: 0,
height: 0,
},
},
animationName: antWaveDotEffect,
animationDuration: motionDurationSlow,
animationTimingFunction: 'linear',
animationFillMode: 'backwards',
},
// Dot 1
// '&-1': {
// left: 0,
// top: 0,
// border: `1px solid ${colorPrimary}`,
// [activeCls]: {
// left: -30,
// top: -10,
// },
// },
// // Dot 2
// '&-2': {
// left: '10%',
// top: 0,
// width: 4,
// height: 4,
// background: colorPrimaryBgHover,
// [activeCls]: {
// left: 'calc(10% - 10px)',
// top: -20,
// width: 1,
// height: 1,
// },
// },
// // Dot 3
// '&-3': {
// left: '35%',
// top: -5,
// width: 4,
// height: 4,
// background: colorPrimary,
// opacity: 0.8,
// [activeCls]: {
// left: 'calc(35% - 3px)',
// top: -15,
// width: 1,
// height: 1,
// },
// },
// // -----------------------
// // Dot 4
// '&-4': {
// left: '80%',
// top: 0,
// width: 6,
// height: 6,
// background: colorPrimaryBgHover,
// [activeCls]: {
// left: 'calc(80% + 10px)',
// top: -20,
// width: 1,
// height: 1,
// },
// },
// // -----------------------
// // Dot 5
// '&-5': {
// left: '100%',
// top: '45%',
// width: 10,
// height: 10,
// background: colorPrimary,
// [activeCls]: {
// left: 'calc(100% + 20px)',
// top: 'calc(45% - 3px)',
// width: 1,
// height: 1,
// },
// },
// // Dot 6
// '&-6': {
// left: '100%',
// top: '90%',
// width: 4,
// height: 4,
// background: colorPrimary,
// [activeCls]: {
// left: 'calc(100% + 30px)',
// top: 'calc(90% + 15px)',
// width: 1,
// height: 1,
// },
// },
// // -----------------------
// // Dot 7
// '&-7': {
// left: '80%',
// top: '100%',
// width: 10,
// height: 10,
// background: colorPrimary,
// [activeCls]: {
// left: 'calc(80% + 10px)',
// top: 'calc(100% + 10px)',
// width: 1,
// height: 1,
// },
// },
// // Dot 8
// '&-8': {
// left: '55%',
// top: '100%',
// width: 4,
// height: 4,
// background: colorPrimaryBgHover,
// [activeCls]: {
// left: 'calc(55% + 2px)',
// top: 'calc(100% + 20px)',
// width: 1,
// height: 1,
// },
// },
// // Dot 9
// '&-9': {
// left: 0,
// top: '100%',
// width: 10,
// height: 10,
// background: colorPrimaryBgHover,
// [activeCls]: {
// left: -15,
// top: 'calc(100% + 15px)',
// width: 1,
// height: 1,
// },
// },
// // -----------------------
// // Dot 10
// '&-10': {
// left: 0,
// top: '50%',
// width: 10,
// height: 10,
// background: colorPrimary,
// [activeCls]: {
// left: -50,
// top: '50%',
// width: 1,
// height: 1,
// },
// },
},
},
},

30
components/theme/themesToken/happy/wave.tsx

@ -6,18 +6,20 @@ import type { ConfigProviderProps } from '../../../config-provider';
import useWaveStyle from './style/wave';
export type WaveRender = Required<Required<ConfigProviderProps>['wave']>['render'];
export type UseTokenType = Parameters<WaveRender>[1];
export type UseTokenType = Parameters<WaveRender>[0]['token'];
interface HappyWaveProps {
target: HTMLElement;
token: UseTokenType;
onFinish: VoidFunction;
}
const DOT_COUNT = 10;
interface DotInfo {
key: number;
size: string;
startSize: string;
endSize: string;
type: 'fill' | 'outlined';
color: string;
startX: string;
@ -30,7 +32,7 @@ function inRange(x: number, y: number, left: number, top: number, right: number,
return x >= left && x <= right && y >= top && y <= bottom;
}
function HappyWave({ target, token }: HappyWaveProps) {
function HappyWave({ target, token, onFinish }: HappyWaveProps) {
const prefixCls = 'happy-wave';
const dotPrefixCls = `${prefixCls}-dot`;
const targetPrefixCls = `${prefixCls}-target`;
@ -60,7 +62,7 @@ function HappyWave({ target, token }: HappyWaveProps) {
const halfWidth = width / 2;
const halfHeight = height / 2;
const OFFSET_MIN = 10;
const OFFSET_MIN = 15;
const OFFSET_MAX = 30;
const halfOffsetMinWidth = halfWidth + OFFSET_MIN;
const halfOffsetMinHeight = halfHeight + OFFSET_MIN;
@ -69,12 +71,12 @@ function HappyWave({ target, token }: HappyWaveProps) {
// Delay to start dot motion
setTimeout(() => {
const offsetAngle = Math.random() * 360;
setDots(
new Array(DOT_COUNT).fill(null).map((_, index) => {
const rotate: number = 360 / DOT_COUNT;
const startAngle: number = rotate * index;
const endAngle: number = rotate * (index + 1);
const randomAngle = Math.random() * (endAngle - startAngle) + startAngle;
const randomAngle = offsetAngle + rotate * index;
// Get start XY (Which should align the rect edge)
let startX = 0;
@ -110,13 +112,16 @@ function HappyWave({ target, token }: HappyWaveProps) {
inRange(endX, endY, -endHalfWidth, -endHalfHeight, endHalfWidth, endHalfHeight)
);
const size = Math.random() * 4 + 6;
return {
key: index + 1,
startX: `${startX}px`,
startY: `${startY}px`,
endX: `${endX}px`,
endY: `${endY}px`,
size: `${Math.random() * 4 + 8}px`,
startSize: `${size}px`,
endSize: `${Math.random() > 0.75 ? size : 0}px`,
type: Math.random() > 0.6 ? 'outlined' : 'fill',
color:
Math.random() > 0.5 ? globalToken.colorPrimary : globalToken.colorPrimaryBgHover,
@ -137,6 +142,7 @@ function HappyWave({ target, token }: HappyWaveProps) {
React.useEffect(() => {
const id = setTimeout(() => {
target.className = target.className.replace(` ${targetPrefixCls}`, '');
onFinish();
}, 600);
return () => {
@ -166,7 +172,8 @@ function HappyWave({ target, token }: HappyWaveProps) {
startY,
endX,
endY,
size,
startSize,
endSize,
type,
color,
}) => {
@ -179,7 +186,8 @@ function HappyWave({ target, token }: HappyWaveProps) {
[`--start-y`]: startY,
[`--end-x`]: endX,
[`--end-y`]: endY,
[`--size`]: size,
[`--start-size`]: startSize,
[`--end-size`]: endSize,
};
if (type === 'fill') {
dotStyle['--background'] = color;
@ -202,7 +210,7 @@ function HappyWave({ target, token }: HappyWaveProps) {
);
}
const renderWave: WaveRender = (target, token) => <HappyWave target={target} token={token} />;
const renderWave: WaveRender = (props: HappyWaveProps) => <HappyWave {...props} />;
const Wave: ConfigProviderProps['wave'] = {
render: renderWave,

Loading…
Cancel
Save