🕶️ 阴影效果不仅能提升页面的层次感,还能有效增强用户界面的交互感和立体感。尤其是在按钮、卡片和其他交互元素上,恰当的阴影效果能够让设计更具活力和动感,从而提升用户的视觉体验。
传统的阴影生成器通常是静态的,用户需要手动输入阴影的各种属性(如模糊度、偏移量、透明度等),但这并不直观。为了简化这一过程,并让设计师能够更高效地生成阴影效果,我们开发了动感阴影生成器。该工具允许用户通过拖动交互生成不同的 box-shadow 效果,并能实时预览按钮或卡片的立体感效果。
🌟 动感阴影的重要性
阴影效果在界面设计中的作用不仅仅是装饰,它能够通过以下几个方面提升用户体验:
提升层次感:阴影可以帮助界面元素与背景区分开,增加层次感,避免界面看起来过于平面。
强化交互感:按钮或卡片上的阴影能让用户感觉到元素是可点击的,提高交互感。例如,鼠标悬停时可以改变阴影的强度,使得界面更加生动。
传达情感和风格:阴影的变化可以营造不同的情感氛围。例如,深色阴影可以传达稳重感,而浅色阴影则给人一种轻盈的感觉。
无论是用于按钮、卡片,还是其他交互元素,合理的阴影效果都能显著提升界面的用户体验。因此,动感阴影生成器的出现,为设计师提供了一个高效、直观的工具,使得阴影效果的生成更加便捷。
🛠 动感阴影生成器的核心功能
动感阴影生成器的设计目标是让用户能够轻松创建多种 box-shadow 效果,并实时预览其效果,避免繁琐的手动调试。其主要功能包括:
1. 实时预览
用户可以通过滑动不同的控制条(如阴影的偏移量、模糊程度、扩展大小等),即时看到阴影效果的变化。每当用户调整某个属性,预览框会立即更新,帮助用户直观地感受到每个参数的变化对设计效果的影响。
2. 拖动交互生成阴影
与传统的静态输入框不同,动感阴影生成器支持拖动交互式调节。用户可以通过拖动界面上的滑块、圆形控制点或按钮,轻松调节阴影的大小、方向和模糊度,直接在界面上实时看到不同阴影效果的变化。
例如,用户可以拖动阴影偏移的控制点,查看不同的偏移量对阴影效果的影响,或通过拖动模糊度滑块观察阴影的柔和度变化。
3. 支持多种阴影效果配置
动感阴影生成器支持配置不同的 box-shadow 参数,用户可以自由调整以下属性:
水平偏移(Horizontal Offset):控制阴影沿水平方向的偏移量,决定阴影的位置。
垂直偏移(Vertical Offset):控制阴影沿垂直方向的偏移量,决定阴影的上下位置。
模糊度(Blur Radius):控制阴影的模糊程度。值越大,阴影越模糊;值越小,阴影越清晰。
扩展大小(Spread Radius):控制阴影的扩展大小。值为正时,阴影扩展;值为负时,阴影收缩。
颜色(Color):设置阴影的颜色及透明度,以适应不同的背景和设计风格。
用户可以通过拖动不同的控制条或滑块,快速调节这些属性,从而生成理想的阴影效果。
4. 一键复制 CSS 代码
生成理想的阴影效果后,用户可以直接复制生成的 CSS 代码。无论是 HEX、RGB 还是 HSL 颜色模式,生成器都会自动为你生成相应的阴影代码,并允许你一键复制到剪贴板。这样,设计师或开发者就能轻松将这些阴影效果应用到他们的项目中,无需手动编写 CSS。
用户只需点击“复制”按钮,便可将这段 CSS 代码粘贴到自己的项目中。
5. 动态样式调整
动感阴影生成器还具备动态样式调整功能。用户可以通过选择不同的按钮或卡片样式,查看阴影效果在不同元素上的表现。例如,用户可以切换卡片、按钮、悬浮效果等多种样式,观察同一个阴影效果在不同设计中的表现,确保其在项目中的统一性和美观性。
🤖 与 Trae 的交互指令
为了进一步简化操作,动感阴影生成器可以与 Trae 的智能命令交互。用户可以通过简单的自然语言指令与 Trae 进行交互,快速生成和调整阴影效果。以下是几个常见的指令示例:
动感阴影生成器, 拖动生成 box-shadow 效果,实时预览按钮或卡片立体感。
Trae 会根据指令生成一个模糊度较大的卡片阴影,并展示预览。
追加功能:
动态模糊(Motion Blur):为运动中的物体添加拖影效果。
颜色自定义:支持非黑色阴影(如彩色投影、渐变阴影)。 前端架构师
追加功能,多格式导出:支持PNG(透明背景)、GIF(动画)、SVG(矢量)等。
通过与 Trae 的结合,动感阴影生成器让设计过程变得更加智能化、便捷化,并提升了工作效率。
动感阴影生成器是一款帮助设计师和开发者快速生成高质量阴影效果的工具。通过交互式的拖动调整和实时预览,用户可以精确控制阴影的各个参数,生成符合需求的 box-shadow 效果。同时,一键复制的功能使得阴影的应用变得轻松快捷。结合 Trae 的智能命令交互,用户可以通过简洁的指令,快速调整和应用阴影效果。
无论是设计精美的卡片、按钮,还是其他需要层次感的元素,动感阴影生成器都能为你提供高效且精确的支持,提升设计的质量和效率。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>动感阴影生成器</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Arial', sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
overflow-x: hidden;
position: relative;
}
/* 粒子背景 */
.particles {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
}
.particle {
position: absolute;
background: rgba(255, 255, 255, 0.5);
border-radius: 50%;
animation: float 6s ease-in-out infinite;
}
@keyframes float {
0%, 100% { transform: translateY(0) rotate(0deg); }
33% { transform: translateY(-30px) rotate(120deg); }
66% { transform: translateY(30px) rotate(240deg); }
}
.container {
max-width: 1400px;
margin: 0 auto;
padding: 20px;
position: relative;
z-index: 1;
}
.header {
text-align: center;
margin-bottom: 30px;
color: white;
}
.header h1 {
font-size: 2.5em;
margin-bottom: 10px;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}
.main-content {
display: grid;
grid-template-columns: 1fr 400px;
gap: 30px;
margin-bottom: 30px;
}
.preview-section {
background: rgba(255, 255, 255, 0.95);
border-radius: 20px;
padding: 40px;
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
backdrop-filter: blur(10px);
}
.control-panel {
background: rgba(255, 255, 255, 0.95);
border-radius: 20px;
padding: 30px;
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
backdrop-filter: blur(10px);
height: fit-content;
}
.preview-container {
min-height: 400px;
border: 2px dashed #ddd;
border-radius: 15px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
background: #f8f9fa;
cursor: crosshair;
}
.shadow-element {
width: 200px;
height: 120px;
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 15px;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
font-size: 16px;
position: relative;
}
.control-group {
margin-bottom: 25px;
}
.control-group label {
display: block;
margin-bottom: 8px;
font-weight: bold;
color: #333;
}
.slider-container {
display: flex;
align-items: center;
gap: 15px;
}
.slider {
flex: 1;
height: 6px;
border-radius: 3px;
background: #ddd;
outline: none;
-webkit-appearance: none;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: #667eea;
cursor: pointer;
box-shadow: 0 2px 6px rgba(0,0,0,0.2);
}
.slider::-moz-range-thumb {
width: 20px;
height: 20px;
border-radius: 50%;
background: #667eea;
cursor: pointer;
border: none;
box-shadow: 0 2px 6px rgba(0,0,0,0.2);
}
.value-display {
min-width: 50px;
text-align: center;
font-weight: bold;
color: #667eea;
}
.color-picker {
width: 100%;
height: 40px;
border: none;
border-radius: 8px;
cursor: pointer;
}
.checkbox-group {
display: flex;
align-items: center;
gap: 10px;
}
.checkbox {
width: 18px;
height: 18px;
cursor: pointer;
}
.export-section {
background: rgba(255, 255, 255, 0.95);
border-radius: 20px;
padding: 30px;
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
backdrop-filter: blur(10px);
}
.export-buttons {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 15px;
margin-top: 20px;
}
.export-btn {
padding: 12px 20px;
border: none;
border-radius: 10px;
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
}
.export-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(102, 126, 234, 0.4);
}
.css-output {
background: #f8f9fa;
border: 1px solid #ddd;
border-radius: 10px;
padding: 20px;
margin-top: 20px;
font-family: 'Courier New', monospace;
font-size: 14px;
white-space: pre-wrap;
max-height: 200px;
overflow-y: auto;
}
.preset-buttons {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
gap: 10px;
margin-bottom: 20px;
}
.preset-btn {
padding: 8px 15px;
border: 2px solid #667eea;
border-radius: 8px;
background: transparent;
color: #667eea;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
font-size: 12px;
}
.preset-btn:hover {
background: #667eea;
color: white;
}
@media (max-width: 768px) {
.main-content {
grid-template-columns: 1fr;
gap: 20px;
}
.container {
padding: 10px;
}
.header h1 {
font-size: 2em;
}
}
</style>
</head>
<body>
<div class="particles" id="particles"></div>
<div class="container">
<div class="header">
<h1>🎨 动感阴影生成器</h1>
<p>拖动预览区域生成立体阴影效果,支持动态模糊和多格式导出</p>
</div>
<div class="main-content">
<div class="preview-section">
<h3>实时预览</h3>
<div class="preview-container" id="previewContainer">
<div class="shadow-element" id="shadowElement">
拖动我试试!
</div>
</div>
</div>
<div class="control-panel">
<h3>阴影控制</h3>
<div class="preset-buttons">
<button class="preset-btn" onclick="applyPreset('soft')">柔和</button>
<button class="preset-btn" onclick="applyPreset('sharp')">锐利</button>
<button class="preset-btn" onclick="applyPreset('floating')">悬浮</button>
<button class="preset-btn" onclick="applyPreset('pressed')">按压</button>
<button class="preset-btn" onclick="applyPreset('glow')">发光</button>
<button class="preset-btn" onclick="applyPreset('neon')">霓虹</button>
</div>
<div class="control-group">
<label>水平偏移 (X)</label>
<div class="slider-container">
<input type="range" class="slider" id="offsetX" min="-50" max="50" value="10">
<span class="value-display" id="offsetXValue">10px</span>
</div>
</div>
<div class="control-group">
<label>垂直偏移 (Y)</label>
<div class="slider-container">
<input type="range" class="slider" id="offsetY" min="-50" max="50" value="10">
<span class="value-display" id="offsetYValue">10px</span>
</div>
</div>
<div class="control-group">
<label>模糊半径</label>
<div class="slider-container">
<input type="range" class="slider" id="blurRadius" min="0" max="100" value="20">
<span class="value-display" id="blurRadiusValue">20px</span>
</div>
</div>
<div class="control-group">
<label>扩展半径</label>
<div class="slider-container">
<input type="range" class="slider" id="spreadRadius" min="-20" max="20" value="0">
<span class="value-display" id="spreadRadiusValue">0px</span>
</div>
</div>
<div class="control-group">
<label>阴影颜色</label>
<input type="color" class="color-picker" id="shadowColor" value="#000000">
</div>
<div class="control-group">
<label>阴影透明度</label>
<div class="slider-container">
<input type="range" class="slider" id="shadowOpacity" min="0" max="100" value="30">
<span class="value-display" id="shadowOpacityValue">30%</span>
</div>
</div>
<div class="control-group">
<div class="checkbox-group">
<input type="checkbox" class="checkbox" id="insetShadow">
<label for="insetShadow">内阴影</label>
</div>
</div>
<div class="control-group">
<div class="checkbox-group">
<input type="checkbox" class="checkbox" id="motionBlur">
<label for="motionBlur">动态模糊</label>
</div>
</div>
<div class="control-group">
<label>动态模糊强度</label>
<div class="slider-container">
<input type="range" class="slider" id="motionBlurIntensity" min="0" max="20" value="5" disabled>
<span class="value-display" id="motionBlurIntensityValue">5px</span>
</div>
</div>
</div>
</div>
<div class="export-section">
<h3>导出功能</h3>
<div class="export-buttons">
<button class="export-btn" onclick="exportCSS()">复制CSS</button>
<button class="export-btn" onclick="exportPNG()">导出PNG</button>
<button class="export-btn" onclick="exportSVG()">导出SVG</button>
<button class="export-btn" onclick="exportGIF()">导出GIF</button>
<button class="export-btn" onclick="savePreset()">保存预设</button>
<button class="export-btn" onclick="loadPreset()">加载预设</button>
</div>
<div class="css-output" id="cssOutput">
box-shadow: 10px 10px 20px 0px rgba(0, 0, 0, 0.3);
</div>
</div>
</div>
<script>
// 粒子背景生成
function createParticles() {
const particlesContainer = document.getElementById('particles');
const particleCount = 50;
for (let i = 0; i < particleCount; i++) {
const particle = document.createElement('div');
particle.className = 'particle';
const size = Math.random() * 5 + 2;
particle.style.width = size + 'px';
particle.style.height = size + 'px';
particle.style.left = Math.random() * 100 + '%';
particle.style.top = Math.random() * 100 + '%';
particle.style.animationDelay = Math.random() * 6 + 's';
particle.style.animationDuration = (Math.random() * 4 + 4) + 's';
particlesContainer.appendChild(particle);
}
}
// 阴影生成器类
class ShadowGenerator {
constructor() {
this.shadowElement = document.getElementById('shadowElement');
this.previewContainer = document.getElementById('previewContainer');
this.isDragging = false;
this.lastMouseX = 0;
this.lastMouseY = 0;
this.initializeControls();
this.addEventListeners();
this.updateShadow();
}
initializeControls() {
this.controls = {
offsetX: document.getElementById('offsetX'),
offsetY: document.getElementById('offsetY'),
blurRadius: document.getElementById('blurRadius'),
spreadRadius: document.getElementById('spreadRadius'),
shadowColor: document.getElementById('shadowColor'),
shadowOpacity: document.getElementById('shadowOpacity'),
insetShadow: document.getElementById('insetShadow'),
motionBlur: document.getElementById('motionBlur'),
motionBlurIntensity: document.getElementById('motionBlurIntensity')
};
this.valueDisplays = {
offsetX: document.getElementById('offsetXValue'),
offsetY: document.getElementById('offsetYValue'),
blurRadius: document.getElementById('blurRadiusValue'),
spreadRadius: document.getElementById('spreadRadiusValue'),
shadowOpacity: document.getElementById('shadowOpacityValue'),
motionBlurIntensity: document.getElementById('motionBlurIntensityValue')
};
}
addEventListeners() {
// 滑块事件
Object.keys(this.controls).forEach(key => {
if (this.controls[key].type === 'range' || this.controls[key].type === 'color' || this.controls[key].type === 'checkbox') {
this.controls[key].addEventListener('input', () => this.updateShadow());
}
});
// 动态模糊复选框事件
this.controls.motionBlur.addEventListener('change', () => {
this.controls.motionBlurIntensity.disabled = !this.controls.motionBlur.checked;
this.updateShadow();
});
// 拖拽事件
this.previewContainer.addEventListener('mousedown', (e) => this.startDrag(e));
this.previewContainer.addEventListener('mousemove', (e) => this.drag(e));
this.previewContainer.addEventListener('mouseup', () => this.endDrag());
this.previewContainer.addEventListener('mouseleave', () => this.endDrag());
// 触摸事件
this.previewContainer.addEventListener('touchstart', (e) => this.startDrag(e.touches[0]));
this.previewContainer.addEventListener('touchmove', (e) => {
e.preventDefault();
this.drag(e.touches[0]);
});
this.previewContainer.addEventListener('touchend', () => this.endDrag());
}
startDrag(e) {
this.isDragging = true;
this.lastMouseX = e.clientX;
this.lastMouseY = e.clientY;
this.previewContainer.style.cursor = 'grabbing';
}
drag(e) {
if (!this.isDragging) return;
const deltaX = e.clientX - this.lastMouseX;
const deltaY = e.clientY - this.lastMouseY;
this.controls.offsetX.value = parseInt(this.controls.offsetX.value) + Math.round(deltaX / 5);
this.controls.offsetY.value = parseInt(this.controls.offsetY.value) + Math.round(deltaY / 5);
// 限制范围
this.controls.offsetX.value = Math.max(-50, Math.min(50, this.controls.offsetX.value));
this.controls.offsetY.value = Math.max(-50, Math.min(50, this.controls.offsetY.value));
this.lastMouseX = e.clientX;
this.lastMouseY = e.clientY;
this.updateShadow();
}
endDrag() {
this.isDragging = false;
this.previewContainer.style.cursor = 'crosshair';
}
updateShadow() {
// 更新数值显示
Object.keys(this.valueDisplays).forEach(key => {
if (key === 'shadowOpacity') {
this.valueDisplays[key].textContent = this.controls[key].value + '%';
} else {
this.valueDisplays[key].textContent = this.controls[key].value + 'px';
}
});
// 生成阴影
const offsetX = this.controls.offsetX.value;
const offsetY = this.controls.offsetY.value;
const blurRadius = this.controls.blurRadius.value;
const spreadRadius = this.controls.spreadRadius.value;
const shadowColor = this.controls.shadowColor.value;
const shadowOpacity = this.controls.shadowOpacity.value / 100;
const inset = this.controls.insetShadow.checked ? 'inset ' : '';
const motionBlur = this.controls.motionBlur.checked;
const motionBlurIntensity = this.controls.motionBlurIntensity.value;
// 转换颜色为rgba
const hex2rgba = (hex, alpha) => {
const r = parseInt(hex.slice(1, 3), 16);
const g = parseInt(hex.slice(3, 5), 16);
const b = parseInt(hex.slice(5, 7), 16);
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
};
let boxShadow = `${inset}${offsetX}px ${offsetY}px ${blurRadius}px ${spreadRadius}px ${hex2rgba(shadowColor, shadowOpacity)}`;
// 动态模糊效果
if (motionBlur) {
const blurShadows = [];
for (let i = 1; i <= motionBlurIntensity; i++) {
const intensity = i / motionBlurIntensity;
const blurOffsetX = offsetX + (i * 2);
const blurOffsetY = offsetY + (i * 2);
const blurOpacity = shadowOpacity * (1 - intensity * 0.7);
blurShadows.push(`${inset}${blurOffsetX}px ${blurOffsetY}px ${blurRadius}px ${spreadRadius}px ${hex2rgba(shadowColor, blurOpacity)}`);
}
boxShadow = [boxShadow, ...blurShadows].join(', ');
}
this.shadowElement.style.boxShadow = boxShadow;
// 更新CSS输出
document.getElementById('cssOutput').textContent = `box-shadow: ${boxShadow};`;
}
getShadowCSS() {
return document.getElementById('cssOutput').textContent;
}
}
// 预设功能
const presets = {
soft: { offsetX: 0, offsetY: 8, blurRadius: 25, spreadRadius: 0, shadowColor: '#000000', shadowOpacity: 15, insetShadow: false },
sharp: { offsetX: 2, offsetY: 2, blurRadius: 0, spreadRadius: 0, shadowColor: '#000000', shadowOpacity: 50, insetShadow: false },
floating: { offsetX: 0, offsetY: 20, blurRadius: 40, spreadRadius: -10, shadowColor: '#000000', shadowOpacity: 20, insetShadow: false },
pressed: { offsetX: 0, offsetY: 2, blurRadius: 8, spreadRadius: 0, shadowColor: '#000000', shadowOpacity: 25, insetShadow: true },
glow: { offsetX: 0, offsetY: 0, blurRadius: 20, spreadRadius: 5, shadowColor: '#667eea', shadowOpacity: 60, insetShadow: false },
neon: { offsetX: 0, offsetY: 0, blurRadius: 30, spreadRadius: 0, shadowColor: '#00ffff', shadowOpacity: 80, insetShadow: false }
};
function applyPreset(presetName) {
const preset = presets[presetName];
if (!preset) return;
Object.keys(preset).forEach(key => {
const control = shadowGenerator.controls[key];
if (control) {
if (control.type === 'checkbox') {
control.checked = preset[key];
} else {
control.value = preset[key];
}
}
});
shadowGenerator.updateShadow();
}
// 导出功能
function exportCSS() {
const css = shadowGenerator.getShadowCSS();
navigator.clipboard.writeText(css).then(() => {
alert('CSS已复制到剪贴板!');
});
}
function exportPNG() {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = 400;
canvas.height = 300;
// 绘制背景
ctx.fillStyle = '#f8f9fa';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 绘制阴影元素(简化版)
ctx.fillStyle = '#667eea';
ctx.fillRect(100, 90, 200, 120);
// 下载
const link = document.createElement('a');
link.download = 'shadow-preview.png';
link.href = canvas.toDataURL();
link.click();
}
function exportSVG() {
const svg = `
<svg width="400" height="300" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="shadow">
<feDropShadow dx="${shadowGenerator.controls.offsetX.value}"
dy="${shadowGenerator.controls.offsetY.value}"
stdDeviation="${shadowGenerator.controls.blurRadius.value / 2}"/>
</filter>
</defs>
<rect x="100" y="90" width="200" height="120" fill="#667eea" filter="url(#shadow)"/>
</svg>
`;
const blob = new Blob([svg], { type: 'image/svg+xml' });
const link = document.createElement('a');
link.download = 'shadow-preview.svg';
link.href = URL.createObjectURL(blob);
link.click();
}
function exportGIF() {
alert('GIF导出功能需要额外的库支持,这里展示一个模拟的动画效果!');
// 创建动画效果
const element = shadowGenerator.shadowElement;
let frame = 0;
const animate = () => {
frame++;
const offset = Math.sin(frame * 0.1) * 10;
shadowGenerator.controls.offsetX.value = 10 + offset;
shadowGenerator.controls.offsetY.value = 10 + offset;
shadowGenerator.updateShadow();
if (frame < 60) {
requestAnimationFrame(animate);
} else {
// 重置
shadowGenerator.controls.offsetX.value = 10;
shadowGenerator.controls.offsetY.value = 10;
shadowGenerator.updateShadow();
}
};
animate();
}
function savePreset() {
const preset = {};
Object.keys(shadowGenerator.controls).forEach(key => {
const control = shadowGenerator.controls[key];
if (control.type === 'checkbox') {
preset[key] = control.checked;
} else {
preset[key] = control.value;
}
});
const presetName = prompt('请输入预设名称:');
if (presetName) {
localStorage.setItem(`shadow-preset-${presetName}`, JSON.stringify(preset));
alert(`预设 "${presetName}" 已保存!`);
}
}
function loadPreset() {
const presetName = prompt('请输入要加载的预设名称:');
if (presetName) {
const preset = localStorage.getItem(`shadow-preset-${presetName}`);
if (preset) {
const presetData = JSON.parse(preset);
Object.keys(presetData).forEach(key => {
const control = shadowGenerator.controls[key];
if (control) {
if (control.type === 'checkbox') {
control.checked = presetData[key];
} else {
control.value = presetData[key];
}
}
});
shadowGenerator.updateShadow();
alert(`预设 "${presetName}" 已加载!`);
} else {
alert('未找到该预设!');
}
}
}
// 初始化
let shadowGenerator;
document.addEventListener('DOMContentLoaded', () => {
createParticles();
shadowGenerator = new ShadowGenerator();
});
</script>
</body>
</html>