mirror of
https://gitee.com/dapppp/ruoyi-plus-vben5.git
synced 2026-03-14 09:10:53 +08:00
feat(theme): 新增按钮水波纹样式配置选项
添加按钮水波纹效果的自定义配置功能,支持默认、禁用、内嵌、抖动和欢乐五种样式。用户可在主题设置中选择不同效果,增强交互视觉体验。 - 在主题配置类型中添加 buttonWaveMode 字段 - 新增按钮水波纹配置组件和样式实现 - 更新中英文国际化文本 - 在应用配置中集成水波纹效果
This commit is contained in:
142
apps/web-antd/src/components/global/button-wave.ts
Normal file
142
apps/web-antd/src/components/global/button-wave.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
import type { ConfigProviderProps } from 'antdv-next';
|
||||
|
||||
import type { ThemePreferences } from '@vben/preferences';
|
||||
|
||||
const createHolder = (node: HTMLElement) => {
|
||||
const { borderWidth } = getComputedStyle(node);
|
||||
const borderWidthNum = Number.parseInt(borderWidth, 10);
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.style.position = 'absolute';
|
||||
div.style.inset = `-${borderWidthNum}px`;
|
||||
div.style.borderRadius = 'inherit';
|
||||
div.style.background = 'transparent';
|
||||
div.style.zIndex = '999';
|
||||
div.style.pointerEvents = 'none';
|
||||
div.style.overflow = 'hidden';
|
||||
node.append(div);
|
||||
|
||||
return div;
|
||||
};
|
||||
|
||||
const createDot = (
|
||||
holder: HTMLElement,
|
||||
color: string,
|
||||
left: number,
|
||||
top: number,
|
||||
size = 0,
|
||||
) => {
|
||||
const dot = document.createElement('div');
|
||||
dot.style.position = 'absolute';
|
||||
dot.style.left = `${left}px`;
|
||||
dot.style.top = `${top}px`;
|
||||
dot.style.width = `${size}px`;
|
||||
dot.style.height = `${size}px`;
|
||||
dot.style.borderRadius = '50%';
|
||||
dot.style.background = color;
|
||||
dot.style.transform = 'translate3d(-50%, -50%, 0)';
|
||||
dot.style.transition = 'all 1s ease-out';
|
||||
holder.append(dot);
|
||||
return dot;
|
||||
};
|
||||
|
||||
type WaveConfig = NonNullable<ConfigProviderProps['wave']>;
|
||||
|
||||
const showInsetEffect: WaveConfig['showEffect'] = (
|
||||
node,
|
||||
{ event, component },
|
||||
) => {
|
||||
if (component !== 'Button') {
|
||||
return;
|
||||
}
|
||||
|
||||
const holder = createHolder(node);
|
||||
const rect = holder.getBoundingClientRect();
|
||||
const left = event.clientX - rect.left;
|
||||
const top = event.clientY - rect.top;
|
||||
|
||||
const dot = createDot(holder, 'rgba(255, 255, 255, 0.65)', left, top);
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
dot.addEventListener('transitionend', () => {
|
||||
holder.remove();
|
||||
});
|
||||
|
||||
dot.style.width = '200px';
|
||||
dot.style.height = '200px';
|
||||
dot.style.opacity = '0';
|
||||
});
|
||||
};
|
||||
|
||||
const showShakeEffect: WaveConfig['showEffect'] = (node, { component }) => {
|
||||
if (component !== 'Button') {
|
||||
return;
|
||||
}
|
||||
|
||||
const seq = [0, -15, 15, -5, 5, 0];
|
||||
const itv = 10;
|
||||
let steps = 0;
|
||||
|
||||
const loop = () => {
|
||||
cancelAnimationFrame((node as any).effectTimeout);
|
||||
(node as any).effectTimeout = requestAnimationFrame(() => {
|
||||
const currentStep = Math.floor(steps / itv);
|
||||
const current = seq[currentStep];
|
||||
const next = seq[currentStep + 1];
|
||||
|
||||
if (next === undefined || next === null) {
|
||||
node.style.transform = '';
|
||||
node.style.transition = '';
|
||||
return;
|
||||
}
|
||||
|
||||
const angle =
|
||||
(current ?? 0) + ((next - (current ?? 0)) / itv) * (steps % itv);
|
||||
|
||||
node.style.transform = `rotate(${angle}deg)`;
|
||||
node.style.transition = 'none';
|
||||
|
||||
steps += 1;
|
||||
loop();
|
||||
});
|
||||
};
|
||||
|
||||
loop();
|
||||
};
|
||||
|
||||
const showHappyEffect: WaveConfig['showEffect'] = (
|
||||
node,
|
||||
{ event, component },
|
||||
) => {
|
||||
if (component !== 'Button') {
|
||||
return;
|
||||
}
|
||||
|
||||
const holder = createHolder(node);
|
||||
const rect = holder.getBoundingClientRect();
|
||||
const left = event.clientX - rect.left;
|
||||
const top = event.clientY - rect.top;
|
||||
const color = `hsl(${Math.floor(Math.random() * 360)}, 80%, 70%)`;
|
||||
|
||||
const dot = createDot(holder, color, left, top, 16);
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
dot.addEventListener('transitionend', () => {
|
||||
holder.remove();
|
||||
});
|
||||
dot.style.width = '220px';
|
||||
dot.style.height = '220px';
|
||||
dot.style.opacity = '0';
|
||||
});
|
||||
};
|
||||
|
||||
export const waveConfigs: Array<{
|
||||
name: ThemePreferences['buttonWaveMode'];
|
||||
wave: WaveConfig;
|
||||
}> = [
|
||||
{ name: 'Disabled', wave: { disabled: true } },
|
||||
{ name: 'Default', wave: {} },
|
||||
{ name: 'Inset', wave: { showEffect: showInsetEffect } },
|
||||
{ name: 'Shake', wave: { showEffect: showShakeEffect } },
|
||||
{ name: 'Happy', wave: { showEffect: showHappyEffect } },
|
||||
];
|
||||
Reference in New Issue
Block a user