v2.2
MENU ANIMATIONS
UI SURECART
BUTTONS
<!DOCTYPE html>
<html lang="en">
<head>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Aura Flow Configurator - BricksFusion</title>
<style>
:root {
--background: #000;
--card-bg: #1e1e1e;
--card-bg-hover: #252525;
--text-primary: #f2f2f7;
--text-secondary: #8e8e93;
--accent: #ef6013;
--accent-hover: #c64c0c;
--border: #2c2c2e;
--shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
--track: #2c2c2e;
--thumb: #ef6013;
--card-radius: 16px;
--input-radius: 8px;
--button-radius: 12px;
--transition: all 0.25s ease;
--font: 'Inter', BlinkMacSystemFont, "San Francisco", "Helvetica Neue", Helvetica, Arial, sans-serif;
--action-bar-height: 70px;
--success: #28a745;
--warning: #ffc107;
--danger: #dc3545;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: var(--font);
background-color: var(--background);
color: var(--text-primary);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
padding-bottom: var(--action-bar-height);
}
.action-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: var(--action-bar-height);
background: linear-gradient(145deg, #1a1a1a, #0f0f0f);
border-top: 1px solid var(--border);
z-index: 1000;
display: flex;
align-items: center;
padding: 0 1.5rem;
gap: 1rem;
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.3);
backdrop-filter: blur(10px);
}
.breadcrumb {
display: flex;
align-items: center;
gap: 0.5rem;
flex: 1;
}
.breadcrumb-item {
color: var(--text-secondary);
font-size: var(--text-xs);
font-weight: 500;
text-decoration: none;
transition: var(--transition);
padding: 0.5rem 0.75rem;
border-radius: 6px;
}
.breadcrumb-item:hover {
color: var(--text-primary);
background-color: rgba(255, 255, 255, 0.05);
}
.breadcrumb-item.active {
color: var(--accent);
background-color: rgba(239, 96, 19, 0.1);
}
.breadcrumb-separator {
color: var(--text-secondary);
font-size: var(--text-xs);
opacity: 0.5;
}
.action-buttons {
display: flex;
align-items: center;
gap: 0.75rem;
}
.action-btn {
padding: 0.6rem 1rem;
background-color: var(--card-bg);
color: var(--text-primary);
font-family: var(--font);
font-size: var(--text-xs);
font-weight: 500;
border: 1px solid var(--border);
border-radius: var(--button-radius);
cursor: pointer;
transition: var(--transition);
display: flex;
align-items: center;
gap: 0.5rem;
text-decoration: none;
white-space: nowrap;
}
.action-btn:hover {
background-color: var(--card-bg-hover);
border-color: var(--accent);
transform: translateY(-1px);
}
.action-btn.primary {
background: linear-gradient(90deg, var(--accent), #ff8c51);
border-color: var(--accent);
color: white;
}
.action-btn.primary:hover {
background: linear-gradient(90deg, var(--accent-hover), #e67a3f);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(239, 96, 19, 0.3);
}
.data-attribute-display {
background-color: rgba(50, 50, 50, 0.8);
border: 1px solid var(--border);
border-radius: 6px;
padding: 0.5rem 0.75rem;
font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
font-size: var(--text-xs);
color: #ff8c51;
cursor: pointer;
transition: var(--transition);
user-select: all;
}
.data-attribute-display:hover {
background-color: rgba(239, 96, 19, 0.2);
border-color: var(--accent);
}
.container {
max-width: 100%;
margin: 0 auto;
padding: 2rem 1.5rem;
}
.page-header {
text-align: center;
margin-bottom: 2rem;
}
.page-title {
font-size: 2.5rem;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 0.5rem;
background: linear-gradient(90deg, var(--accent), #ff8c51);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.page-subtitle {
font-size: var(--text-s);
color: var(--text-secondary);
font-weight: 500;
}
.instructions-toggle {
margin-bottom: 2rem;
}
.instructions-card {
background-color: var(--card-bg);
border: 1px solid var(--border);
border-radius: var(--card-radius);
box-shadow: var(--shadow);
overflow: hidden;
transition: var(--transition);
}
.instructions-header {
padding: 1rem 1.5rem;
cursor: pointer;
transition: var(--transition);
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid transparent;
}
.instructions-header:hover {
background-color: var(--card-bg-hover);
}
.instructions-card.expanded .instructions-header {
border-bottom-color: var(--border);
}
.instructions-title {
font-size: var(--text-s);
font-weight: 600;
}
.toggle-icon {
font-size: 1.2em;
transition: transform 0.3s ease;
}
.toggle-icon.expanded {
transform: rotate(180deg);
}
.instructions-content {
padding: 0 1.5rem;
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease, padding 0.3s ease;
}
.instructions-content.show {
max-height: 500px;
padding: 1.5rem;
}
.instructions-grid {
display: grid;
grid-template-columns: 1fr;
gap: 1.5rem;
}
.how-to-use ol {
padding-left: 1.5rem;
}
.how-to-use li {
margin-bottom: 0.75rem;
font-size: var(--text-xs);
color: var(--text-secondary);
line-height: 1.6;
}
.how-to-use strong {
color: var(--text-primary);
font-weight: 600;
}
.how-to-use code {
background-color: rgba(50, 50, 50, 0.5);
padding: 0.2rem 0.5rem;
border-radius: 4px;
font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
font-size: var(--text-xs);
color: #ff8c51;
}
.content {
display: grid;
grid-template-columns: 1fr 500px;
gap: 2rem;
align-items: start;
}
.preview-section {
position: sticky;
top: 2rem;
}
.controls-section {
max-width: 500px;
}
.card {
background-color: var(--card-bg);
border-radius: var(--card-radius);
box-shadow: var(--shadow);
overflow: hidden;
margin-bottom: 1.5rem;
border: 1px solid var(--border);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 6px 24px rgba(0, 0, 0, 0.4);
}
.preview-container {
height: 400px;
width: 100%;
position: relative;
overflow: hidden;
border-radius: var(--card-radius);
background-color: #000000;
border: 1px solid var(--border);
box-shadow: var(--shadow);
}
.preview-content {
color: white;
text-align: center;
font-weight: bold;
font-size: var(--text-s);
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 2;
}
.preview-controls {
position: absolute;
top: 1rem;
right: 1rem;
display: flex;
gap: 0.5rem;
z-index: 10;
}
.preview-btn {
padding: 0.5rem;
background-color: rgba(0, 0, 0, 0.7);
color: white;
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 6px;
cursor: pointer;
transition: var(--transition);
font-size: var(--text-xs);
backdrop-filter: blur(5px);
}
.preview-btn:hover {
background-color: var(--accent);
border-color: var(--accent);
}
.background-selector-wrapper {
position: relative;
display: inline-block;
}
.background-selector-btn {
position: relative;
}
.background-selector-btn:hover {
background-color: rgba(239, 96, 19, 0.2);
border-color: var(--accent);
box-shadow: 0 0 8px rgba(239, 96, 19, 0.3);
}
.hidden-color-input {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
cursor: pointer;
z-index: 1;
}
.card-heading {
padding: 1rem 1.5rem;
font-size: var(--text-s);
font-weight: 600;
border-bottom: 1px solid var(--border);
letter-spacing: 0.3px;
display: flex;
justify-content: space-between;
align-items: center;
}
.card-actions {
display: flex;
gap: 0.5rem;
}
.card-action-btn {
padding: 0.4rem 0.8rem;
background-color: transparent;
color: var(--text-secondary);
border: 1px solid var(--border);
border-radius: 6px;
cursor: pointer;
font-size: var(--text-xs);
transition: var(--transition);
}
.card-action-btn:hover {
color: var(--text-primary);
border-color: var(--accent);
background-color: rgba(239, 96, 19, 0.1);
}
.card-content {
padding: 1.5rem;
}
.control-group {
margin-bottom: 1.5rem;
position: relative;
}
.control-group:last-child {
margin-bottom: 0;
}
.control-label {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.75rem;
}
.label-text {
font-size: var(--text-xs);
font-weight: 500;
letter-spacing: 0.2px;
display: flex;
align-items: center;
gap: 0.5rem;
}
.help-tooltip {
cursor: help;
opacity: 0.7;
transition: var(--transition);
}
.help-tooltip:hover {
opacity: 1;
color: var(--accent);
}
.value-display {
display: flex;
align-items: center;
gap: 0.5rem;
}
.value-text {
font-size: var(--text-xs);
color: var(--text-secondary);
background-color: rgba(50, 50, 50, 0.5);
padding: 2px 8px;
border-radius: 4px;
min-width: 45px;
text-align: center;
}
.reset-btn {
padding: 0.2rem 0.4rem;
background-color: transparent;
color: var(--text-secondary);
border: 1px solid var(--border);
border-radius: 4px;
cursor: pointer;
font-size: 10px;
transition: var(--transition);
}
.reset-btn:hover {
color: var(--danger);
border-color: var(--danger);
background-color: rgba(220, 53, 69, 0.1);
}
input[type="range"] {
-webkit-appearance: none;
width: 100%;
height: 6px;
background: var(--track);
border-radius: 3px;
outline: none;
margin: 0.8rem 0;
position: relative;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 20px;
height: 20px;
background: var(--thumb);
border-radius: 50%;
cursor: pointer;
transition: var(--transition);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
}
input[type="range"]::-webkit-slider-thumb:hover {
transform: scale(1.2);
box-shadow: 0 0 10px rgba(239, 96, 19, 0.5);
}
.color-list {
display: flex;
flex-direction: column;
gap: 1rem;
margin-bottom: 1.5rem;
}
.color-row {
display: flex;
align-items: center;
gap: 1.25rem;
padding: 1rem 1.25rem;
background-color: rgba(30, 30, 30, 0.7);
border: 1px solid var(--border);
border-radius: var(--input-radius);
transition: var(--transition);
}
.color-row:hover {
border-color: var(--accent);
box-shadow: 0 0 0 1px rgba(239, 96, 19, 0.1);
}
.color-picker-container {
position: relative;
width: 40px;
height: 40px;
border-radius: 8px;
overflow: hidden;
border: 2px solid var(--border);
cursor: pointer;
transition: var(--transition);
flex-shrink: 0;
background: linear-gradient(45deg, #f0f0f0 25%, transparent 25%),
linear-gradient(-45deg, #f0f0f0 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, #f0f0f0 75%),
linear-gradient(-45deg, transparent 75%, #f0f0f0 75%);
background-size: 8px 8px;
background-position: 0 0, 0 4px, 4px -4px, -4px 0px;
}
.color-picker-container::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: var(--current-color, #4444ff);
border-radius: 6px;
z-index: 1;
}
.color-picker-container:hover {
border-color: var(--accent);
transform: scale(1.05);
}
input[type="color"] {
position: absolute;
top: -2px;
left: -2px;
width: calc(100% + 4px);
height: calc(100% + 4px);
border: none;
cursor: pointer;
background: transparent;
z-index: 2;
opacity: 0;
}
.color-input-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.color-label {
font-size: 10px;
font-weight: 500;
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 0.5px;
margin-left: 0.25rem;
}
.color-input {
padding: 0.5rem 0.75rem;
background-color: rgba(0, 0, 0, 0.3);
border: 1px solid var(--border);
border-radius: 6px;
color: var(--text-primary);
font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
font-size: 12px;
transition: var(--transition);
min-width: 0;
}
.color-input:focus {
border-color: var(--accent);
box-shadow: 0 0 0 1px rgba(239, 96, 19, 0.2);
outline: none;
}
.color-input.invalid {
border-color: var(--danger);
box-shadow: 0 0 0 1px rgba(220, 53, 69, 0.2);
}
.hex-input,
.hsl-input {
width: 100%;
}
.color-input-group:nth-child(2) {
flex: 0.3;
}
.color-input-group:nth-child(3) {
flex: 0.7;
}
.switch-container {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 1.2rem;
padding: 0.5rem 0;
}
.switch-label {
font-size: var(--text-xs);
font-weight: 500;
letter-spacing: 0.2px;
display: flex;
align-items: center;
gap: 0.5rem;
}
.switch {
position: relative;
display: inline-block;
width: 52px;
height: 28px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: var(--track);
transition: var(--transition);
border-radius: 34px;
}
.slider:before {
position: absolute;
content: "";
height: 20px;
width: 20px;
left: 4px;
bottom: 4px;
background-color: #f2f2f7;
transition: var(--transition);
border-radius: 50%;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}
input:checked + .slider {
background-color: var(--accent);
}
input:checked + .slider:before {
transform: translateX(24px);
}
.notification {
position: fixed;
bottom: calc(var(--action-bar-height) + 1rem);
left: 50%;
background-color: var(--success);
color: white;
padding: 0.75rem 1rem;
border-radius: var(--input-radius);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
z-index: 1001;
transform: translate(-50%, 200px);
opacity: 0;
transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.4s ease;
font-size: var(--text-xs);
font-weight: 500;
max-width: 320px;
word-wrap: break-word;
line-height: 1.4;
text-align: center;
}
.notification.show {
transform: translate(-50%, 0);
opacity: 1;
}
@media (max-width: 1200px) {
.content {
grid-template-columns: 1fr;
gap: 1.5rem;
}
.preview-section {
position: static;
}
.controls-section {
max-width: 100%;
}
}
@media (max-width: 768px) {
.action-bar {
flex-direction: column;
height: auto;
min-height: var(--action-bar-height);
padding: 0.75rem;
}
.breadcrumb {
order: 1;
width: 100%;
}
.action-buttons {
order: 2;
width: 100%;
justify-content: center;
flex-wrap: wrap;
}
body {
padding-bottom: calc(var(--action-bar-height) + 20px);
}
.notification {
bottom: calc(var(--action-bar-height) + 2rem);
max-width: 280px;
transform: translate(-50%, 250px);
}
.notification.show {
transform: translate(-50%, 0);
opacity: 1;
}
.color-row {
flex-direction: column;
align-items: stretch;
gap: 1rem;
padding: 1rem;
}
.color-picker-container {
align-self: center;
margin-bottom: 0.5rem;
}
.color-input-group {
align-items: stretch;
}
.hex-input,
.hsl-input {
width: 100%;
}
.preview-container {
height: 300px;
}
.data-attribute-display {
font-size: 10px;
padding: 0.4rem 0.6rem;
}
.action-btn {
font-size: 11px;
padding: 0.5rem 0.8rem;
}
.page-title {
font-size: 2rem;
}
}
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
button:focus-visible,
input:focus-visible,
.action-btn:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: var(--background);
}
::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--accent);
}
.loading {
opacity: 0.6;
pointer-events: none;
position: relative;
}
.loading::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 20px;
height: 20px;
margin: -10px 0 0 -10px;
border: 2px solid var(--border);
border-top-color: var(--accent);
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
</style>
</head>
<body>
<div class="action-bar">
<nav class="breadcrumb">
<a href="https://bricksfusion.com" class="breadcrumb-item">Home</a>
<span class="breadcrumb-separator">›</span>
<a href="https://bricksfusion.com/corebackground/" class="breadcrumb-item">Core Backgrounds</a>
<span class="breadcrumb-separator">›</span>
<span class="breadcrumb-item active">Aura Flow</span>
</nav>
<div class="action-buttons">
<div class="data-attribute-display" id="quick-attribute" title="Click to copy data attribute">
data-aura-flow
</div>
<button class="action-btn primary" id="download-config" title="Copy JavaScript code (Ctrl+D)" data-protection-animation="true">
<span>⬇</span>
Copy JS
</button>
<button class="action-btn" id="copy-full-section" title="Copy complete section JSON for Bricks Builder (Ctrl+S)" data-protection-animation="true">
<span>📦</span>
Copy Full Section
</button>
</div>
</div>
<div class="container">
<div class="page-header">
<h1 class="page-title">Aura Flow</h1>
<p class="page-subtitle">Interactive fluid animations for Bricks Builder</p>
</div>
<div class="instructions-toggle">
<div class="instructions-card" id="instructions-card">
<div class="instructions-header" id="instructions-toggle">
<div class="instructions-title">
How to Use & Code Information
</div>
<span class="toggle-icon">▼</span>
</div>
<div class="instructions-content" id="instructions-content">
<div class="instructions-grid">
<div class="how-to-use">
<ol>
<li>Customize your Aura Flow animation using the controls below</li>
<li>Click <strong>Copy JS</strong> to copy the JavaScript code to clipboard</li>
<li>In Bricks Builder, add a <strong>Code</strong> element</li>
<li>Paste or upload the JavaScript code</li>
<li>To add the effect to any section: go to <strong>Section → Style → Attributes</strong>, add <code>data-aura-flow</code> as attribute name (leave value empty)</li>
</ol>
</div>
</div>
</div>
</div>
</div>
<div class="content">
<section class="preview-section">
<div class="preview-container" id="aura-flow-preview" data-aura-flow="true">
<div class="preview-content">Interactive Aura Flow Preview</div>
<div class="preview-controls">
<button class="preview-btn" id="randomize-aura-flow" title="Randomize (R)">🎲</button>
<div class="background-selector-wrapper">
<button class="preview-btn background-selector-btn" id="background-selector">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polygon points="12,2 2,7 12,12 22,7"/>
<polyline points="2,17 12,22 22,17"/>
<polyline points="2,12 12,17 22,12"/>
</svg>
</button>
<input type="color" id="preview-background-picker" class="hidden-color-input" value="#000000" title="Change Preview Background (B)">
</div>
</div>
</div>
</section>
<section class="controls-section">
<div class="card">
<div class="card-heading">
Aura Colors
<div class="card-actions">
<button class="card-action-btn" id="reset-colors" title="Reset Colors">↺</button>
</div>
</div>
<div class="card-content">
<div class="color-list">
<div class="color-row">
<div class="color-picker-container">
<input type="color" id="color1" value="#4444ff">
</div>
<div class="color-input-group">
<span class="color-label">HEX</span>
<input type="text" class="color-input hex-input" id="color1-hex" value="#4444ff" placeholder="#FFFFFF">
</div>
<div class="color-input-group">
<span class="color-label">HSL</span>
<input type="text" class="color-input hsl-input" id="color1-hsl" placeholder="hsl(0, 100%, 50%)">
</div>
</div>
<div class="color-row">
<div class="color-picker-container">
<input type="color" id="color2" value="#0088ff">
</div>
<div class="color-input-group">
<span class="color-label">HEX</span>
<input type="text" class="color-input hex-input" id="color2-hex" value="#0088ff" placeholder="#FFFFFF">
</div>
<div class="color-input-group">
<span class="color-label">HSL</span>
<input type="text" class="color-input hsl-input" id="color2-hsl" placeholder="hsl(0, 100%, 50%)">
</div>
</div>
<div class="color-row">
<div class="color-picker-container">
<input type="color" id="color3" value="#00ffff">
</div>
<div class="color-input-group">
<span class="color-label">HEX</span>
<input type="text" class="color-input hex-input" id="color3-hex" value="#00ffff" placeholder="#FFFFFF">
</div>
<div class="color-input-group">
<span class="color-label">HSL</span>
<input type="text" class="color-input hsl-input" id="color3-hsl" placeholder="hsl(0, 100%, 50%)">
</div>
</div>
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">
Opacity
<span class="help-tooltip" title="Controls the overall opacity of the effect">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="opacity-value">0.9</span></span>
<button class="reset-btn" onclick="resetParameter('opacity', 0.9)">↺</button>
</div>
</div>
<input type="range" id="opacity" min="0.1" max="1" value="0.9" step="0.05">
</div>
</div>
</div>
<div class="card">
<div class="card-heading">
Effect Properties
<div class="card-actions">
<button class="card-action-btn" id="reset-effects" title="Reset Effect Settings">↺</button>
</div>
</div>
<div class="card-content">
<div class="control-group">
<div class="control-label">
<span class="label-text">
Intensity
<span class="help-tooltip" title="Controls the overall intensity of the effect">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="intensity-value">1.0</span></span>
<button class="reset-btn" onclick="resetParameter('intensity', 1.0)">↺</button>
</div>
</div>
<input type="range" id="intensity" min="0.1" max="2" value="1.0" step="0.1">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">
Glow Effect
<span class="help-tooltip" title="Controls the glow radius around the cursor">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="glow-value">0.8</span></span>
<button class="reset-btn" onclick="resetParameter('glow', 0.8)">↺</button>
</div>
</div>
<input type="range" id="glow" min="0.1" max="2" value="0.8" step="0.1">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">
Electric Effect
<span class="help-tooltip" title="Controls the electric lightning trails">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="electric-value">1.2</span></span>
<button class="reset-btn" onclick="resetParameter('electric', 1.2)">↺</button>
</div>
</div>
<input type="range" id="electric" min="0.1" max="2" value="1.2" step="0.1">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">
Effect Radius
<span class="help-tooltip" title="Controls the radius of the effect area">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="radius-value">100</span></span>
<button class="reset-btn" onclick="resetParameter('radius', 100)">↺</button>
</div>
</div>
<input type="range" id="radius" min="10" max="300" value="100" step="5">
</div>
</div>
</div>
<div class="card">
<div class="card-heading">
Animation Settings
<div class="card-actions">
<button class="card-action-btn" id="reset-animation" title="Reset Animation Settings">↺</button>
</div>
</div>
<div class="card-content">
<div class="control-group">
<div class="control-label">
<span class="label-text">Animation Speed</span>
<div class="value-display">
<span class="value-text"><span id="speed-value">1.0</span></span>
<button class="reset-btn" onclick="resetParameter('speed', 1.0)">↺</button>
</div>
</div>
<input type="range" id="speed" min="0.1" max="2" value="1.0" step="0.1">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">Pulse Speed</span>
<div class="value-display">
<span class="value-text"><span id="pulse-speed-value">8.0</span></span>
<button class="reset-btn" onclick="resetParameter('pulse-speed', 8.0)">↺</button>
</div>
</div>
<input type="range" id="pulse-speed" min="1" max="15" value="8.0" step="0.5">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">Mouse Transition</span>
<div class="value-display">
<span class="value-text"><span id="transition-value">0.08</span></span>
<button class="reset-btn" onclick="resetParameter('transition', 0.08)">↺</button>
</div>
</div>
<input type="range" id="transition" min="0.01" max="0.2" value="0.08" step="0.01">
</div>
</div>
</div>
<div class="card">
<div class="card-heading">
Advanced Options
<div class="card-actions">
<button class="card-action-btn" id="reset-advanced" title="Reset Advanced Settings">↺</button>
</div>
</div>
<div class="card-content">
<div class="switch-container">
<span class="switch-label">Auto Movement</span>
<label class="switch">
<input type="checkbox" id="auto-movement" checked>
<span class="slider"></span>
</label>
</div>
<div class="switch-container">
<span class="switch-label">Performance Mode</span>
<label class="switch">
<input type="checkbox" id="performance-mode">
<span class="slider"></span>
</label>
</div>
</div>
</div>
</section>
</div>
</div>
<div class="notification" id="notification"></div>
<script>
document.addEventListener('DOMContentLoaded', function() {
let fluidConfig = {
colors: ['#4444ff', '#0088ff', '#00ffff'],
opacity: 0.9,
intensity: 1.0,
glow: 0.8,
electric: 1.2,
radius: 100,
speed: 1.0,
pulseSpeed: 8.0,
transition: 0.08,
autoMovement: true,
performanceMode: false
};
const defaultConfig = { ...fluidConfig };
let activeFluid = null;
function hexToRGB(hex) {
try {
if (!hex || typeof hex !== 'string') {
return [0.5, 0.5, 1.0];
}
if (hex.charAt(0) !== '#') {
hex = '#' + hex;
}
const shorthand = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace(shorthand, (m, r, g, b) => {
return '#' + r + r + g + g + b + b;
});
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
if (!result) {
return [0.5, 0.5, 1.0];
}
const r = parseInt(result[1], 16) / 255;
const g = parseInt(result[2], 16) / 255;
const b = parseInt(result[3], 16) / 255;
return [r, g, b];
} catch (error) {
return [0.5, 0.5, 1.0];
}
}
function initFluidAnimation() {
destroyExistingFluid();
const container = document.getElementById('aura-flow-preview');
container.setAttribute('data-aura-flow-color1', fluidConfig.colors[0]);
container.setAttribute('data-aura-flow-color2', fluidConfig.colors[1]);
container.setAttribute('data-aura-flow-color3', fluidConfig.colors[2]);
container.setAttribute('data-aura-flow-opacity', fluidConfig.opacity);
container.setAttribute('data-aura-flow-intensity', fluidConfig.intensity);
container.setAttribute('data-aura-flow-glow', fluidConfig.glow);
container.setAttribute('data-aura-flow-electric', fluidConfig.electric);
container.setAttribute('data-aura-flow-radius', fluidConfig.radius);
container.setAttribute('data-aura-flow-speed', fluidConfig.speed);
container.setAttribute('data-aura-flow-pulse-speed', fluidConfig.pulseSpeed);
container.setAttribute('data-aura-flow-transition', fluidConfig.transition);
activeFluid = new FluidAnimation(container);
}
function destroyExistingFluid() {
if (activeFluid) {
activeFluid.destroy();
activeFluid = null;
}
}
class FluidAnimation {
constructor(container) {
this.container = container;
this._cache = {
dimensions: { width: 0, height: 0, aspect: 0 },
mouse: { current: { x: 0.5, y: 0.5 }, last: { x: 0.5, y: 0.5 } },
time: 0,
frameCount: 0,
autonomous: true
};
this.isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
this._frameInterval = this.isMobile ? 1000/30 : 1000/60;
this._lastFrameTime = 0;
this.mouseX = 0.5;
this.mouseY = 0.5;
this.lastX = 0.5;
this.lastY = 0.5;
this.targetX = 0.5;
this.targetY = 0.5;
this.transitionSpeed = parseFloat(container.getAttribute('data-aura-flow-transition')) || fluidConfig.transition;
this.isMouseDown = false;
this.config = this._parseConfig(container);
this._setup(container);
this._initWebGL();
this._setupShaders();
this._setupEvents();
this._animate();
}
_parseConfig(container) {
const colors = [];
for (let i = 1; i <= 3; i++) {
const colorAttr = 'data-aura-flow-color' + i;
if (container.hasAttribute(colorAttr)) {
const hexColor = container.getAttribute(colorAttr);
colors.push(hexToRGB(hexColor));
}
}
if (colors.length !== 3) {
colors.length = 0;
colors.push(hexToRGB(fluidConfig.colors[0]));
colors.push(hexToRGB(fluidConfig.colors[1]));
colors.push(hexToRGB(fluidConfig.colors[2]));
}
return {
colors: colors,
opacity: parseFloat(container.getAttribute('data-aura-flow-opacity')) || fluidConfig.opacity,
intensity: parseFloat(container.getAttribute('data-aura-flow-intensity')) || fluidConfig.intensity,
glow: parseFloat(container.getAttribute('data-aura-flow-glow')) || fluidConfig.glow,
electric: parseFloat(container.getAttribute('data-aura-flow-electric')) || fluidConfig.electric,
radius: parseFloat(container.getAttribute('data-aura-flow-radius')) || fluidConfig.radius,
speed: parseFloat(container.getAttribute('data-aura-flow-speed')) || fluidConfig.speed,
pulseSpeed: parseFloat(container.getAttribute('data-aura-flow-pulse-speed')) || fluidConfig.pulseSpeed
};
}
_setup(container) {
if (getComputedStyle(container).position === 'static') {
container.style.position = 'relative';
}
this.wrapper = document.createElement('div');
Object.assign(this.wrapper.style, {
position: 'absolute',
top: '0',
left: '0',
width: '100%',
height: '100%',
zIndex: '1',
pointerEvents: 'none',
opacity: this.config.opacity.toString()
});
this.canvas = document.createElement('canvas');
Object.assign(this.canvas.style, {
position: 'absolute',
top: '0',
left: '0',
width: '100%',
height: '100%',
mixBlendMode: 'screen',
pointerEvents: 'none'
});
this.wrapper.appendChild(this.canvas);
container.insertBefore(this.wrapper, container.firstChild);
}
_initWebGL() {
this.gl = this.canvas.getContext('webgl', {
premultipliedAlpha: false,
alpha: true
});
if (!this.gl) {
return;
}
this.gl.clearColor(0.0, 0.0, 0.0, 0.0);
this.startTime = Date.now();
this.vertices = new Float32Array([-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0]);
this._resize();
}
_setupShaders() {
const vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER);
this.gl.shaderSource(vertexShader, `
attribute vec2 position;
varying vec2 uv;
void main() {
uv = position * 0.5 + 0.5;
gl_Position = vec4(position, 0.0, 1.0);
}
`);
this.gl.compileShader(vertexShader);
const fragmentShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
this.gl.shaderSource(fragmentShader, `
precision highp float;
varying vec2 uv;
uniform vec2 resolution;
uniform vec2 mouse;
uniform float time;
uniform vec2 lastMouse;
uniform float isMouseDown;
uniform vec3 color1;
uniform vec3 color2;
uniform vec3 color3;
uniform float intensity;
uniform float glow;
uniform float electric;
uniform float radius;
uniform float pulseSpeed;
float lightning(vec2 p, vec2 a, vec2 b) {
vec2 pa = p - a;
vec2 ba = b - a;
float h = clamp(dot(pa,ba)/dot(ba,ba), 0.0, 1.0);
float d = length(pa - ba*h);
float strength = 1.0 - smoothstep(0.0, 0.02 / electric, d);
strength *= sin(time * pulseSpeed + p.x * 10.0) * 0.5 + 0.5;
return strength;
}
void main() {
vec2 p = (uv * resolution * 2.0 - resolution) / min(resolution.x, resolution.y);
vec2 m = (mouse * resolution * 2.0 - resolution) / min(resolution.x, resolution.y);
vec2 lm = (lastMouse * resolution * 2.0 - resolution) / min(resolution.x, resolution.y);
vec3 color = vec3(0.0);
vec2 mouseVel = (m - lm) * isMouseDown;
float mouseLen = length(mouseVel);
for(float i = 0.0; i < 3.0; i++) {
vec2 pos = p - m;
float dist = length(pos);
float a = atan(pos.y, pos.x) + sin(time * 0.5) * 2.0;
float h = dist * (1.0 + mouseLen * 2.0);
vec3 col = mix(
mix(color1, color2, i / 2.0),
color3,
sin(time * 0.5 + h) * 0.5 + 0.5
);
color += col * (0.15 * intensity / abs(dist - mouseLen * 2.0));
color += col * (0.15 * intensity / abs(dist - mouseLen));
}
float glowStr = exp(-length(p - m) * (1.5 / glow));
color += mix(color1, color2, 0.5) * glowStr * 0.8;
if (mouseLen > 0.001) {
float electricEffect = lightning(p, lm, m);
electricEffect += lightning(p, lm + vec2(0.1), m + vec2(-0.1));
electricEffect += lightning(p, lm - vec2(0.1), m + vec2(0.1));
color += mix(color2, color3, 0.5) * electricEffect * electric;
}
float pulse = sin(time * pulseSpeed) * 0.5 + 0.5;
float mouseGlow = exp(-length(p - m) * (2.0 + pulse * 2.0));
color += mix(color1, color3, pulse) * mouseGlow * pulse * intensity;
color = pow(color, vec3(0.8));
float alpha = min(1.0, length(color) * 0.7);
gl_FragColor = vec4(color, alpha);
}
`);
this.gl.compileShader(fragmentShader);
if (!this.gl.getShaderParameter(vertexShader, this.gl.COMPILE_STATUS)) {
return;
}
if (!this.gl.getShaderParameter(fragmentShader, this.gl.COMPILE_STATUS)) {
return;
}
this.program = this.gl.createProgram();
this.gl.attachShader(this.program, vertexShader);
this.gl.attachShader(this.program, fragmentShader);
this.gl.linkProgram(this.program);
if (!this.gl.getProgramParameter(this.program, this.gl.LINK_STATUS)) {
return;
}
this.gl.useProgram(this.program);
const buffer = this.gl.createBuffer();
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);
this.gl.bufferData(this.gl.ARRAY_BUFFER, this.vertices, this.gl.STATIC_DRAW);
const positionLocation = this.gl.getAttribLocation(this.program, 'position');
this.gl.enableVertexAttribArray(positionLocation);
this.gl.vertexAttribPointer(positionLocation, 2, this.gl.FLOAT, false, 0, 0);
this.uniformLocations = {};
['resolution', 'mouse', 'time', 'lastMouse', 'isMouseDown',
'color1', 'color2', 'color3', 'intensity', 'glow',
'electric', 'radius', 'pulseSpeed'].forEach(name => {
this.uniformLocations[name] = this.gl.getUniformLocation(this.program, name);
});
}
_setupEvents() {
this.resizeHandler = this._resize.bind(this);
window.addEventListener('resize', this.resizeHandler);
const updatePosition = (e, touch = false) => {
const rect = this.container.getBoundingClientRect();
const clientX = touch ? e.touches[0].clientX : e.clientX;
const clientY = touch ? e.touches[0].clientY : e.clientY;
if (clientX < rect.left || clientX > rect.right ||
clientY < rect.top || clientY > rect.bottom) {
this._cache.autonomous = true;
return;
}
this._cache.autonomous = false;
this.targetX = (clientX - rect.left) / rect.width;
this.targetY = 1.0 - (clientY - rect.top) / rect.height;
clearTimeout(this.autonomousTimeout);
this.autonomousTimeout = setTimeout(() => {
this._cache.autonomous = true;
}, 2000);
};
this.mouseMoveHandler = e => updatePosition(e);
this.mouseDownHandler = e => {
const rect = this.container.getBoundingClientRect();
if (e.clientX >= rect.left && e.clientX <= rect.right &&
e.clientY >= rect.top && e.clientY <= rect.bottom) {
this.isMouseDown = true;
}
};
this.mouseUpHandler = () => this.isMouseDown = false;
this.mouseLeaveHandler = () => this._cache.autonomous = true;
this.container.addEventListener('mousemove', this.mouseMoveHandler);
this.container.addEventListener('mousedown', this.mouseDownHandler);
window.addEventListener('mouseup', this.mouseUpHandler);
this.container.addEventListener('mouseleave', this.mouseLeaveHandler);
this.touchStartHandler = e => {
this.isMouseDown = true;
updatePosition(e, true);
};
this.touchMoveHandler = e => {
if (this.isMouseDown) {
updatePosition(e, true);
}
};
this.touchEndHandler = () => {
this.isMouseDown = false;
this._cache.autonomous = true;
};
this.container.addEventListener('touchstart', this.touchStartHandler, {passive: true});
this.container.addEventListener('touchmove', this.touchMoveHandler, {passive: true});
this.container.addEventListener('touchend', this.touchEndHandler, {passive: true});
}
_updateAutonomousPosition(time) {
if (this._cache.autonomous) {
const radius = 0.3;
const speed = 0.5 * this.config.speed;
this.targetX = 0.5 + Math.cos(time * speed) * radius;
this.targetY = 0.5 + Math.sin(time * speed * 1.5) * radius;
}
this.lastX = this.mouseX;
this.lastY = this.mouseY;
this.mouseX += (this.targetX - this.mouseX) * this.transitionSpeed;
this.mouseY += (this.targetY - this.mouseY) * this.transitionSpeed;
}
_resize() {
const width = this.canvas.clientWidth;
const height = this.canvas.clientHeight;
if (this.canvas.width !== width || this.canvas.height !== height) {
this.canvas.width = width;
this.canvas.height = height;
this.gl.viewport(0, 0, width, height);
}
}
_animate(timestamp = 0) {
if (timestamp - this._lastFrameTime < this._frameInterval) {
this.animationFrame = requestAnimationFrame(this._animate.bind(this));
return;
}
this._lastFrameTime = timestamp;
this._resize();
const time = (Date.now() - this.startTime) * 0.001 * this.config.speed;
this._updateAutonomousPosition(time);
this.gl.enable(this.gl.BLEND);
this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
this.gl.uniform2f(this.uniformLocations.resolution, this.canvas.width, this.canvas.height);
this.gl.uniform2f(this.uniformLocations.mouse, this.mouseX, this.mouseY);
this.gl.uniform2f(this.uniformLocations.lastMouse, this.lastX, this.lastY);
this.gl.uniform1f(this.uniformLocations.time, time);
this.gl.uniform1f(this.uniformLocations.isMouseDown, this.isMouseDown ? 1.0 : 0.0);
this.gl.uniform3fv(this.uniformLocations.color1, this.config.colors[0]);
this.gl.uniform3fv(this.uniformLocations.color2, this.config.colors[1]);
this.gl.uniform3fv(this.uniformLocations.color3, this.config.colors[2]);
this.gl.uniform1f(this.uniformLocations.intensity, this.config.intensity);
this.gl.uniform1f(this.uniformLocations.glow, this.config.glow);
this.gl.uniform1f(this.uniformLocations.electric, this.config.electric);
this.gl.uniform1f(this.uniformLocations.radius, this.config.radius);
this.gl.uniform1f(this.uniformLocations.pulseSpeed, this.config.pulseSpeed);
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
this.animationFrame = requestAnimationFrame(this._animate.bind(this));
}
destroy() {
if (this.animationFrame) {
cancelAnimationFrame(this.animationFrame);
}
if (this.autonomousTimeout) {
clearTimeout(this.autonomousTimeout);
}
window.removeEventListener('resize', this.resizeHandler);
this.container.removeEventListener('mousemove', this.mouseMoveHandler);
this.container.removeEventListener('mousedown', this.mouseDownHandler);
window.removeEventListener('mouseup', this.mouseUpHandler);
this.container.removeEventListener('mouseleave', this.mouseLeaveHandler);
this.container.removeEventListener('touchstart', this.touchStartHandler);
this.container.removeEventListener('touchmove', this.touchMoveHandler);
this.container.removeEventListener('touchend', this.touchEndHandler);
if (this.gl) {
this.gl.deleteProgram(this.program);
const ext = this.gl.getExtension('WEBGL_lose_context');
if (ext) ext.loseContext();
}
if (this.wrapper && this.wrapper.parentNode) {
this.wrapper.parentNode.removeChild(this.wrapper);
}
}
}
function generateJavaScriptCode() {
return `(function() {
const DEFAULT_CONFIG = {
opacity: ${fluidConfig.opacity},
intensity: ${fluidConfig.intensity},
glow: ${fluidConfig.glow},
electric: ${fluidConfig.electric},
radius: ${fluidConfig.radius},
speed: ${fluidConfig.speed},
pulseSpeed: ${fluidConfig.pulseSpeed},
transition: ${fluidConfig.transition},
colors: [
'${fluidConfig.colors[0]}',
'${fluidConfig.colors[1]}',
'${fluidConfig.colors[2]}'
]
};
function hexToRGB(hex) {
try {
if (!hex || typeof hex !== 'string') {
return [0.5, 0.5, 1.0];
}
if (hex.charAt(0) !== '#') {
hex = '#' + hex;
}
const shorthand = /^#?([a-f\\d])([a-f\\d])([a-f\\d])$/i;
hex = hex.replace(shorthand, (m, r, g, b) => {
return '#' + r + r + g + g + b + b;
});
const result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hex);
if (!result) {
return [0.5, 0.5, 1.0];
}
const r = parseInt(result[1], 16) / 255;
const g = parseInt(result[2], 16) / 255;
const b = parseInt(result[3], 16) / 255;
return [r, g, b];
} catch (error) {
return [0.5, 0.5, 1.0];
}
}
class FluidAnimation {
constructor(container) {
this.container = container;
this._cache = {
dimensions: { width: 0, height: 0, aspect: 0 },
mouse: { current: { x: 0.5, y: 0.5 }, last: { x: 0.5, y: 0.5 } },
time: 0,
frameCount: 0,
autonomous: true
};
this.isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
this._frameInterval = this.isMobile ? 1000/30 : 1000/60;
this._lastFrameTime = 0;
this.mouseX = 0.5;
this.mouseY = 0.5;
this.lastX = 0.5;
this.lastY = 0.5;
this.targetX = 0.5;
this.targetY = 0.5;
this.transitionSpeed = parseFloat(container.getAttribute('data-aura-flow-transition')) || DEFAULT_CONFIG.transition;
this.isMouseDown = false;
this.config = this._parseConfig(container);
this._setup(container);
this._initWebGL();
this._setupShaders();
this._setupEvents();
this._animate();
}
_parseConfig(container) {
const colors = [];
for (let i = 1; i <= 3; i++) {
const colorAttr = 'data-aura-flow-color' + i;
if (container.hasAttribute(colorAttr)) {
const hexColor = container.getAttribute(colorAttr);
colors.push(hexToRGB(hexColor));
}
}
if (colors.length !== 3) {
colors.length = 0;
colors.push(hexToRGB(DEFAULT_CONFIG.colors[0]));
colors.push(hexToRGB(DEFAULT_CONFIG.colors[1]));
colors.push(hexToRGB(DEFAULT_CONFIG.colors[2]));
}
return {
colors: colors,
opacity: parseFloat(container.getAttribute('data-aura-flow-opacity')) || DEFAULT_CONFIG.opacity,
intensity: parseFloat(container.getAttribute('data-aura-flow-intensity')) || DEFAULT_CONFIG.intensity,
glow: parseFloat(container.getAttribute('data-aura-flow-glow')) || DEFAULT_CONFIG.glow,
electric: parseFloat(container.getAttribute('data-aura-flow-electric')) || DEFAULT_CONFIG.electric,
radius: parseFloat(container.getAttribute('data-aura-flow-radius')) || DEFAULT_CONFIG.radius,
speed: parseFloat(container.getAttribute('data-aura-flow-speed')) || DEFAULT_CONFIG.speed,
pulseSpeed: parseFloat(container.getAttribute('data-aura-flow-pulse-speed')) || DEFAULT_CONFIG.pulseSpeed
};
}
_setup(container) {
if (getComputedStyle(container).position === 'static') {
container.style.position = 'relative';
}
this.wrapper = document.createElement('div');
Object.assign(this.wrapper.style, {
position: 'absolute',
top: '0',
left: '0',
width: '100%',
height: '100%',
zIndex: '1',
pointerEvents: 'none',
opacity: this.config.opacity.toString()
});
this.canvas = document.createElement('canvas');
Object.assign(this.canvas.style, {
position: 'absolute',
top: '0',
left: '0',
width: '100%',
height: '100%',
mixBlendMode: 'screen',
pointerEvents: 'none'
});
this.wrapper.appendChild(this.canvas);
container.insertBefore(this.wrapper, container.firstChild);
}
_initWebGL() {
this.gl = this.canvas.getContext('webgl', {
premultipliedAlpha: false,
alpha: true
});
if (!this.gl) {
return;
}
this.gl.clearColor(0.0, 0.0, 0.0, 0.0);
this.startTime = Date.now();
this.vertices = new Float32Array([-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0]);
this._resize();
}
_setupShaders() {
const vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER);
this.gl.shaderSource(vertexShader, \`
attribute vec2 position;
varying vec2 uv;
void main() {
uv = position * 0.5 + 0.5;
gl_Position = vec4(position, 0.0, 1.0);
}
\`);
this.gl.compileShader(vertexShader);
const fragmentShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
this.gl.shaderSource(fragmentShader, \`
precision highp float;
varying vec2 uv;
uniform vec2 resolution;
uniform vec2 mouse;
uniform float time;
uniform vec2 lastMouse;
uniform float isMouseDown;
uniform vec3 color1;
uniform vec3 color2;
uniform vec3 color3;
uniform float intensity;
uniform float glow;
uniform float electric;
uniform float radius;
uniform float pulseSpeed;
float lightning(vec2 p, vec2 a, vec2 b) {
vec2 pa = p - a;
vec2 ba = b - a;
float h = clamp(dot(pa,ba)/dot(ba,ba), 0.0, 1.0);
float d = length(pa - ba*h);
float strength = 1.0 - smoothstep(0.0, 0.02 / electric, d);
strength *= sin(time * pulseSpeed + p.x * 10.0) * 0.5 + 0.5;
return strength;
}
void main() {
vec2 p = (uv * resolution * 2.0 - resolution) / min(resolution.x, resolution.y);
vec2 m = (mouse * resolution * 2.0 - resolution) / min(resolution.x, resolution.y);
vec2 lm = (lastMouse * resolution * 2.0 - resolution) / min(resolution.x, resolution.y);
vec3 color = vec3(0.0);
vec2 mouseVel = (m - lm) * isMouseDown;
float mouseLen = length(mouseVel);
for(float i = 0.0; i < 3.0; i++) {
vec2 pos = p - m;
float dist = length(pos);
float a = atan(pos.y, pos.x) + sin(time * 0.5) * 2.0;
float h = dist * (1.0 + mouseLen * 2.0);
vec3 col = mix(
mix(color1, color2, i / 2.0),
color3,
sin(time * 0.5 + h) * 0.5 + 0.5
);
color += col * (0.15 * intensity / abs(dist - mouseLen * 2.0));
color += col * (0.15 * intensity / abs(dist - mouseLen));
}
float glowStr = exp(-length(p - m) * (1.5 / glow));
color += mix(color1, color2, 0.5) * glowStr * 0.8;
if (mouseLen > 0.001) {
float electricEffect = lightning(p, lm, m);
electricEffect += lightning(p, lm + vec2(0.1), m + vec2(-0.1));
electricEffect += lightning(p, lm - vec2(0.1), m + vec2(0.1));
color += mix(color2, color3, 0.5) * electricEffect * electric;
}
float pulse = sin(time * pulseSpeed) * 0.5 + 0.5;
float mouseGlow = exp(-length(p - m) * (2.0 + pulse * 2.0));
color += mix(color1, color3, pulse) * mouseGlow * pulse * intensity;
color = pow(color, vec3(0.8));
float alpha = min(1.0, length(color) * 0.7);
gl_FragColor = vec4(color, alpha);
}
\`);
this.gl.compileShader(fragmentShader);
if (!this.gl.getShaderParameter(vertexShader, this.gl.COMPILE_STATUS)) {
return;
}
if (!this.gl.getShaderParameter(fragmentShader, this.gl.COMPILE_STATUS)) {
return;
}
this.program = this.gl.createProgram();
this.gl.attachShader(this.program, vertexShader);
this.gl.attachShader(this.program, fragmentShader);
this.gl.linkProgram(this.program);
if (!this.gl.getProgramParameter(this.program, this.gl.LINK_STATUS)) {
return;
}
this.gl.useProgram(this.program);
const buffer = this.gl.createBuffer();
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);
this.gl.bufferData(this.gl.ARRAY_BUFFER, this.vertices, this.gl.STATIC_DRAW);
const positionLocation = this.gl.getAttribLocation(this.program, 'position');
this.gl.enableVertexAttribArray(positionLocation);
this.gl.vertexAttribPointer(positionLocation, 2, this.gl.FLOAT, false, 0, 0);
this.uniformLocations = {};
['resolution', 'mouse', 'time', 'lastMouse', 'isMouseDown',
'color1', 'color2', 'color3', 'intensity', 'glow',
'electric', 'radius', 'pulseSpeed'].forEach(name => {
this.uniformLocations[name] = this.gl.getUniformLocation(this.program, name);
});
}
_setupEvents() {
this.resizeHandler = this._resize.bind(this);
window.addEventListener('resize', this.resizeHandler);
const updatePosition = (e, touch = false) => {
const rect = this.container.getBoundingClientRect();
const clientX = touch ? e.touches[0].clientX : e.clientX;
const clientY = touch ? e.touches[0].clientY : e.clientY;
if (clientX < rect.left || clientX > rect.right ||
clientY < rect.top || clientY > rect.bottom) {
this._cache.autonomous = true;
return;
}
this._cache.autonomous = false;
this.targetX = (clientX - rect.left) / rect.width;
this.targetY = 1.0 - (clientY - rect.top) / rect.height;
clearTimeout(this.autonomousTimeout);
this.autonomousTimeout = setTimeout(() => {
this._cache.autonomous = true;
}, 2000);
};
this.mouseMoveHandler = e => updatePosition(e);
this.mouseDownHandler = e => {
const rect = this.container.getBoundingClientRect();
if (e.clientX >= rect.left && e.clientX <= rect.right &&
e.clientY >= rect.top && e.clientY <= rect.bottom) {
this.isMouseDown = true;
}
};
this.mouseUpHandler = () => this.isMouseDown = false;
this.mouseLeaveHandler = () => this._cache.autonomous = true;
this.container.addEventListener('mousemove', this.mouseMoveHandler);
this.container.addEventListener('mousedown', this.mouseDownHandler);
window.addEventListener('mouseup', this.mouseUpHandler);
this.container.addEventListener('mouseleave', this.mouseLeaveHandler);
this.touchStartHandler = e => {
this.isMouseDown = true;
updatePosition(e, true);
};
this.touchMoveHandler = e => {
if (this.isMouseDown) {
updatePosition(e, true);
}
};
this.touchEndHandler = () => {
this.isMouseDown = false;
this._cache.autonomous = true;
};
this.container.addEventListener('touchstart', this.touchStartHandler, {passive: true});
this.container.addEventListener('touchmove', this.touchMoveHandler, {passive: true});
this.container.addEventListener('touchend', this.touchEndHandler, {passive: true});
}
_updateAutonomousPosition(time) {
if (this._cache.autonomous) {
const radius = 0.3;
const speed = 0.5 * this.config.speed;
this.targetX = 0.5 + Math.cos(time * speed) * radius;
this.targetY = 0.5 + Math.sin(time * speed * 1.5) * radius;
}
this.lastX = this.mouseX;
this.lastY = this.mouseY;
this.mouseX += (this.targetX - this.mouseX) * this.transitionSpeed;
this.mouseY += (this.targetY - this.mouseY) * this.transitionSpeed;
}
_resize() {
const width = this.canvas.clientWidth;
const height = this.canvas.clientHeight;
if (this.canvas.width !== width || this.canvas.height !== height) {
this.canvas.width = width;
this.canvas.height = height;
this.gl.viewport(0, 0, width, height);
}
}
_animate(timestamp = 0) {
if (timestamp - this._lastFrameTime < this._frameInterval) {
this.animationFrame = requestAnimationFrame(this._animate.bind(this));
return;
}
this._lastFrameTime = timestamp;
this._resize();
const time = (Date.now() - this.startTime) * 0.001 * this.config.speed;
this._updateAutonomousPosition(time);
this.gl.enable(this.gl.BLEND);
this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
this.gl.uniform2f(this.uniformLocations.resolution, this.canvas.width, this.canvas.height);
this.gl.uniform2f(this.uniformLocations.mouse, this.mouseX, this.mouseY);
this.gl.uniform2f(this.uniformLocations.lastMouse, this.lastX, this.lastY);
this.gl.uniform1f(this.uniformLocations.time, time);
this.gl.uniform1f(this.uniformLocations.isMouseDown, this.isMouseDown ? 1.0 : 0.0);
this.gl.uniform3fv(this.uniformLocations.color1, this.config.colors[0]);
this.gl.uniform3fv(this.uniformLocations.color2, this.config.colors[1]);
this.gl.uniform3fv(this.uniformLocations.color3, this.config.colors[2]);
this.gl.uniform1f(this.uniformLocations.intensity, this.config.intensity);
this.gl.uniform1f(this.uniformLocations.glow, this.config.glow);
this.gl.uniform1f(this.uniformLocations.electric, this.config.electric);
this.gl.uniform1f(this.uniformLocations.radius, this.config.radius);
this.gl.uniform1f(this.uniformLocations.pulseSpeed, this.config.pulseSpeed);
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
this.animationFrame = requestAnimationFrame(this._animate.bind(this));
}
destroy() {
if (this.animationFrame) {
cancelAnimationFrame(this.animationFrame);
}
if (this.autonomousTimeout) {
clearTimeout(this.autonomousTimeout);
}
window.removeEventListener('resize', this.resizeHandler);
this.container.removeEventListener('mousemove', this.mouseMoveHandler);
this.container.removeEventListener('mousedown', this.mouseDownHandler);
window.removeEventListener('mouseup', this.mouseUpHandler);
this.container.removeEventListener('mouseleave', this.mouseLeaveHandler);
this.container.removeEventListener('touchstart', this.touchStartHandler);
this.container.removeEventListener('touchmove', this.touchMoveHandler);
this.container.removeEventListener('touchend', this.touchEndHandler);
if (this.gl) {
this.gl.deleteProgram(this.program);
const ext = this.gl.getExtension('WEBGL_lose_context');
if (ext) ext.loseContext();
}
if (this.wrapper && this.wrapper.parentNode) {
this.wrapper.parentNode.removeChild(this.wrapper);
}
}
}
document.addEventListener('DOMContentLoaded', () => {
const fluidElements = new Map();
document.querySelectorAll('[data-aura-flow]').forEach(element => {
const animation = new FluidAnimation(element);
fluidElements.set(element, animation);
});
const observer = new MutationObserver(mutations => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.nodeType === 1 && node.matches('[data-aura-flow]:not([data-aura-flow-initialized])')) {
const animation = new FluidAnimation(node);
fluidElements.set(node, animation);
node.dataset.auraFlowInitialized = 'true';
}
});
mutation.removedNodes.forEach(node => {
if (node.nodeType === 1 && fluidElements.has(node)) {
const animation = fluidElements.get(node);
animation.destroy();
fluidElements.delete(node);
}
});
});
});
observer.observe(document.body, {
childList: true,
subtree: true
});
document.addEventListener('bricks-lazy-load', () => {
document.querySelectorAll('[data-aura-flow]:not([data-aura-flow-initialized])').forEach(element => {
const animation = new FluidAnimation(element);
fluidElements.set(element, animation);
element.dataset.auraFlowInitialized = 'true';
});
});
});
})();`;
}
function generateFullSectionJSON() {
// Function to generate unique 6-character IDs
function generateUniqueId() {
const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
for (let i = 0; i < 6; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
}
// Generate unique IDs for each element
const sectionId = generateUniqueId();
const containerId = generateUniqueId();
const codeId = generateUniqueId();
const attributeId = generateUniqueId();
// Only include basic data-aura-flow attribute
const dataAttributes = [
{ "id": attributeId, "name": "data-aura-flow" }
];
// Get generated JavaScript code
const rawJsCode = generateJavaScriptCode();
// JSON.stringify will automatically handle correct escaping
const jsCode = rawJsCode;
// Create complete Bricks Builder JSON structure
const bricksJson = {
"content": [
{
"id": sectionId,
"name": "section",
"parent": 0,
"children": [containerId, codeId],
"settings": {
"_height": "500",
"_justifyContent": "center",
"_background": {
"color": {
"hex": "#000000"
}
},
"_attributes": dataAttributes
},
"label": "Aura Flow Section"
},
{
"id": containerId,
"name": "container",
"parent": sectionId,
"children": [],
"settings": {},
"label": ""
},
{
"id": codeId,
"name": "code",
"parent": sectionId,
"children": [],
"settings": {
"javascriptCode": jsCode,
"executeCode": true,
"_display": "none"
},
"label": "Aura Flow JS"
}
],
"source": "bricksCopiedElements",
"sourceUrl": "https://bricksfusion.com/configurator/aura-flow",
"version": "2.0.1",
"globalClasses": [],
"globalElements": []
};
// Return formatted JSON
return JSON.stringify(bricksJson, null, 2);
}
function generateRandomColor() {
return '#' + Math.floor(Math.random() * 16777215).toString(16).padStart(6, '0');
}
function showNotification(message, type = 'success') {
const notification = document.getElementById('notification');
notification.textContent = message;
notification.className = `notification ${type}`;
notification.offsetHeight;
notification.style.visibility = 'visible';
notification.classList.add('show');
setTimeout(() => {
notification.classList.remove('show');
setTimeout(() => {
if (!notification.classList.contains('show')) {
notification.style.visibility = 'hidden';
}
}, 400);
}, 3000);
}
function hexToHsl(hex) {
const r = parseInt(hex.slice(1, 3), 16) / 255;
const g = parseInt(hex.slice(3, 5), 16) / 255;
const b = parseInt(hex.slice(5, 7), 16) / 255;
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
let h, s, l = (max + min) / 2;
if (max === min) {
h = s = 0;
} else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return `hsl(${Math.round(h * 360)}, ${Math.round(s * 100)}%, ${Math.round(l * 100)}%)`;
}
function hslToHex(hsl) {
const match = hsl.match(/hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/);
if (!match) return null;
let h = parseInt(match[1]) / 360;
let s = parseInt(match[2]) / 100;
let l = parseInt(match[3]) / 100;
const hue2rgb = (p, q, t) => {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1/6) return p + (q - p) * 6 * t;
if (t < 1/2) return q;
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
};
let r, g, b;
if (s === 0) {
r = g = b = l;
} else {
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
const toHex = (c) => {
const hex = Math.round(c * 255).toString(16);
return hex.length === 1 ? '0' + hex : hex;
};
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
}
function isValidHex(hex) {
return /^#[0-9A-F]{6}$/i.test(hex);
}
function isValidHsl(hsl) {
return /^hsl\(\s*(\d{1,3})\s*,\s*(\d{1,3})%\s*,\s*(\d{1,3})%\s*\)$/i.test(hsl);
}
function formatHex(value) {
let hex = value.replace(/[^0-9A-Fa-f#]/g, '');
if (!hex.startsWith('#')) {
hex = '#' + hex;
}
if (hex.length > 7) {
hex = hex.substring(0, 7);
}
return hex.toUpperCase();
}
function formatHsl(value) {
const cleanValue = value.replace(/[^\d,\s]/g, '');
const numbers = cleanValue.match(/\d+/g);
if (!numbers || numbers.length < 3) {
const partialMatch = value.match(/(\d+)/g);
if (partialMatch && partialMatch.length >= 1) {
const h = Math.min(360, Math.max(0, parseInt(partialMatch[0]) || 0));
const s = Math.min(100, Math.max(0, parseInt(partialMatch[1]) || 50));
const l = Math.min(100, Math.max(0, parseInt(partialMatch[2]) || 50));
return `hsl(${h}, ${s}%, ${l}%)`;
}
return value;
}
let h = Math.min(360, Math.max(0, parseInt(numbers[0])));
let s = Math.min(100, Math.max(0, parseInt(numbers[1])));
let l = Math.min(100, Math.max(0, parseInt(numbers[2])));
return `hsl(${h}, ${s}%, ${l}%)`;
}
function copyJsToClipboard() {
const jsCode = generateJavaScriptCode();
navigator.clipboard.writeText(jsCode)
.then(() => {
showNotification('JavaScript code copied to clipboard!');
})
.catch(err => {
try {
const textArea = document.createElement('textarea');
textArea.value = jsCode;
textArea.style.position = 'fixed';
textArea.style.opacity = '0';
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
showNotification('JavaScript code copied to clipboard!');
} catch (fallbackErr) {
showNotification('Failed to copy to clipboard. Please try again.', 'error');
}
});
}
function copyFullSectionToClipboard() {
const sectionJSON = generateFullSectionJSON();
navigator.clipboard.writeText(sectionJSON)
.then(() => {
showNotification('Full section JSON copied to clipboard!');
})
.catch(err => {
try {
const textArea = document.createElement('textarea');
textArea.value = sectionJSON;
textArea.style.position = 'fixed';
textArea.style.opacity = '0';
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
showNotification('Full section JSON copied to clipboard!');
} catch (fallbackErr) {
showNotification('Failed to copy to clipboard. Please try again.', 'error');
}
});
}
function copyToClipboard(text) {
navigator.clipboard.writeText(text)
.then(() => {
showNotification('Copied to clipboard!');
})
.catch(err => {
showNotification('Failed to copy to clipboard', 'error');
});
}
window.resetParameter = function(parameterId, defaultValue) {
const element = document.getElementById(parameterId);
if (element) {
element.value = defaultValue;
const valueElement = document.getElementById(`${parameterId}-value`);
if (valueElement) {
valueElement.textContent = defaultValue;
}
switch (parameterId) {
case 'opacity':
fluidConfig.opacity = defaultValue;
break;
case 'intensity':
fluidConfig.intensity = defaultValue;
break;
case 'glow':
fluidConfig.glow = defaultValue;
break;
case 'electric':
fluidConfig.electric = defaultValue;
break;
case 'radius':
fluidConfig.radius = defaultValue;
break;
case 'speed':
fluidConfig.speed = defaultValue;
break;
case 'pulse-speed':
fluidConfig.pulseSpeed = defaultValue;
break;
case 'transition':
fluidConfig.transition = defaultValue;
break;
}
initFluidAnimation();
showNotification(`${parameterId.replace(/-/g, ' ')} reset to default`);
}
};
function generateRandomAuraFlow() {
const randomColors = Array.from({ length: 3 }, () => generateRandomColor());
fluidConfig.colors = randomColors;
updateColorInputs();
initFluidAnimation();
showNotification('Random aura flow generated!');
}
function updateColorInputs() {
for (let i = 1; i <= 3; i++) {
const color = fluidConfig.colors[i - 1];
const colorInput = document.getElementById(`color${i}`);
const hexInput = document.getElementById(`color${i}-hex`);
const hslInput = document.getElementById(`color${i}-hsl`);
if (colorInput && hexInput && hslInput) {
colorInput.value = color;
hexInput.value = color;
hslInput.value = hexToHsl(color);
// Update color picker container background
const container = colorInput.closest('.color-picker-container');
if (container) {
container.style.setProperty('--current-color', color);
}
hexInput.classList.remove('invalid');
hslInput.classList.remove('invalid');
}
}
}
function initializeUI() {
initFluidAnimation();
const instructionsToggle = document.getElementById('instructions-toggle');
const instructionsContent = document.getElementById('instructions-content');
const instructionsCard = document.getElementById('instructions-card');
const toggleIcon = instructionsToggle.querySelector('.toggle-icon');
instructionsToggle.addEventListener('click', () => {
const isVisible = instructionsContent.classList.contains('show');
if (isVisible) {
instructionsContent.classList.remove('show');
instructionsCard.classList.remove('expanded');
toggleIcon.classList.remove('expanded');
} else {
instructionsContent.classList.add('show');
instructionsCard.classList.add('expanded');
toggleIcon.classList.add('expanded');
}
});
document.getElementById('quick-attribute').addEventListener('click', () => {
copyToClipboard('data-aura-flow');
});
document.getElementById('download-config').addEventListener('click', () => {
copyJsToClipboard();
});
document.getElementById('copy-full-section').addEventListener('click', () => {
copyFullSectionToClipboard();
});
document.getElementById('randomize-aura-flow').addEventListener('click', () => {
generateRandomAuraFlow();
});
// Background selector for preview
const backgroundPicker = document.getElementById('preview-background-picker');
const previewContainer = document.getElementById('aura-flow-preview');
backgroundPicker.addEventListener('input', (e) => {
const selectedColor = e.target.value;
previewContainer.style.backgroundColor = selectedColor;
// Show notification
showNotification(`Preview background changed to ${selectedColor}`);
});
// Set initial background color
previewContainer.style.backgroundColor = '#000000';
document.getElementById('reset-colors').addEventListener('click', () => {
fluidConfig.colors = [...defaultConfig.colors];
updateColorInputs();
initFluidAnimation();
showNotification('Colors reset to default');
});
document.getElementById('reset-effects').addEventListener('click', () => {
fluidConfig.intensity = defaultConfig.intensity;
fluidConfig.glow = defaultConfig.glow;
fluidConfig.electric = defaultConfig.electric;
fluidConfig.radius = defaultConfig.radius;
document.getElementById('intensity').value = defaultConfig.intensity;
document.getElementById('glow').value = defaultConfig.glow;
document.getElementById('electric').value = defaultConfig.electric;
document.getElementById('radius').value = defaultConfig.radius;
document.getElementById('intensity-value').textContent = defaultConfig.intensity;
document.getElementById('glow-value').textContent = defaultConfig.glow;
document.getElementById('electric-value').textContent = defaultConfig.electric;
document.getElementById('radius-value').textContent = defaultConfig.radius;
initFluidAnimation();
showNotification('Effect settings reset');
});
document.getElementById('reset-animation').addEventListener('click', () => {
fluidConfig.speed = defaultConfig.speed;
fluidConfig.pulseSpeed = defaultConfig.pulseSpeed;
fluidConfig.transition = defaultConfig.transition;
document.getElementById('speed').value = defaultConfig.speed;
document.getElementById('pulse-speed').value = defaultConfig.pulseSpeed;
document.getElementById('transition').value = defaultConfig.transition;
document.getElementById('speed-value').textContent = defaultConfig.speed;
document.getElementById('pulse-speed-value').textContent = defaultConfig.pulseSpeed;
document.getElementById('transition-value').textContent = defaultConfig.transition;
initFluidAnimation();
showNotification('Animation settings reset');
});
document.getElementById('reset-advanced').addEventListener('click', () => {
fluidConfig.autoMovement = defaultConfig.autoMovement;
fluidConfig.performanceMode = defaultConfig.performanceMode;
document.getElementById('auto-movement').checked = defaultConfig.autoMovement;
document.getElementById('performance-mode').checked = defaultConfig.performanceMode;
initFluidAnimation();
showNotification('Advanced settings reset');
});
const colorInputs = document.querySelectorAll('input[type="color"]');
colorInputs.forEach((input, index) => {
const colorIndex = index + 1;
const hexInput = document.getElementById(`color${colorIndex}-hex`);
const hslInput = document.getElementById(`color${colorIndex}-hsl`);
hslInput.value = hexToHsl(input.value);
// Initialize color picker container background
const container = input.closest('.color-picker-container');
if (container) {
container.style.setProperty('--current-color', input.value);
}
input.addEventListener('input', () => {
const color = input.value;
hexInput.value = color;
hslInput.value = hexToHsl(color);
hexInput.classList.remove('invalid');
hslInput.classList.remove('invalid');
// Update color picker container background
const container = input.closest('.color-picker-container');
if (container) {
container.style.setProperty('--current-color', color);
}
fluidConfig.colors[index] = color;
initFluidAnimation();
});
hexInput.addEventListener('input', (e) => {
let hex = e.target.value;
hex = formatHex(hex);
e.target.value = hex;
if (isValidHex(hex)) {
input.value = hex;
hslInput.value = hexToHsl(hex);
// Update color picker container background
const container = input.closest('.color-picker-container');
if (container) {
container.style.setProperty('--current-color', hex);
}
fluidConfig.colors[index] = hex;
e.target.classList.remove('invalid');
hslInput.classList.remove('invalid');
initFluidAnimation();
} else {
e.target.classList.add('invalid');
}
});
hexInput.addEventListener('blur', (e) => {
if (!isValidHex(e.target.value)) {
e.target.value = input.value;
e.target.classList.remove('invalid');
}
});
hslInput.addEventListener('input', (e) => {
let hsl = e.target.value;
if (isValidHsl(hsl)) {
const hex = hslToHex(hsl);
if (hex) {
input.value = hex;
hexInput.value = hex;
// Update color picker container background
const container = input.closest('.color-picker-container');
if (container) {
container.style.setProperty('--current-color', hex);
}
fluidConfig.colors[index] = hex;
e.target.classList.remove('invalid');
hexInput.classList.remove('invalid');
initFluidAnimation();
return;
}
}
e.target.classList.add('invalid');
});
hslInput.addEventListener('blur', (e) => {
let hsl = e.target.value;
if (!isValidHsl(hsl) && hsl.trim()) {
const formatted = formatHsl(hsl);
if (isValidHsl(formatted)) {
e.target.value = formatted;
const hex = hslToHex(formatted);
if (hex) {
input.value = hex;
hexInput.value = hex;
fluidConfig.colors[index] = hex;
e.target.classList.remove('invalid');
hexInput.classList.remove('invalid');
initFluidAnimation();
return;
}
}
}
if (!isValidHsl(e.target.value)) {
e.target.value = hexToHsl(input.value);
e.target.classList.remove('invalid');
}
});
});
const rangeInputs = document.querySelectorAll('input[type="range"]');
rangeInputs.forEach(input => {
let valueElement;
switch (input.id) {
case 'pulse-speed':
valueElement = document.getElementById('pulse-speed-value');
break;
default:
valueElement = document.getElementById(`${input.id}-value`);
}
if (valueElement) {
valueElement.textContent = input.value;
}
input.addEventListener('input', () => {
if (valueElement) {
valueElement.textContent = input.value;
}
switch (input.id) {
case 'opacity':
fluidConfig.opacity = parseFloat(input.value);
break;
case 'intensity':
fluidConfig.intensity = parseFloat(input.value);
break;
case 'glow':
fluidConfig.glow = parseFloat(input.value);
break;
case 'electric':
fluidConfig.electric = parseFloat(input.value);
break;
case 'radius':
fluidConfig.radius = parseFloat(input.value);
break;
case 'speed':
fluidConfig.speed = parseFloat(input.value);
break;
case 'pulse-speed':
fluidConfig.pulseSpeed = parseFloat(input.value);
break;
case 'transition':
fluidConfig.transition = parseFloat(input.value);
break;
}
initFluidAnimation();
});
});
document.getElementById('auto-movement').addEventListener('change', function() {
fluidConfig.autoMovement = this.checked;
initFluidAnimation();
});
document.getElementById('performance-mode').addEventListener('change', function() {
fluidConfig.performanceMode = this.checked;
initFluidAnimation();
});
document.addEventListener('keydown', (e) => {
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') {
return;
}
if (e.ctrlKey || e.metaKey) {
switch (e.key.toLowerCase()) {
case 'd':
e.preventDefault();
const downloadBtn = document.getElementById('download-config');
if (downloadBtn && downloadBtn.hasAttribute('data-protection-animation')) {
downloadBtn.click();
} else {
copyJsToClipboard();
}
break;
case 's':
e.preventDefault();
const fullSectionBtn = document.getElementById('copy-full-section');
if (fullSectionBtn && fullSectionBtn.hasAttribute('data-protection-animation')) {
fullSectionBtn.click();
} else {
copyFullSectionToClipboard();
}
break;
}
} else {
switch (e.key.toLowerCase()) {
case 'r':
generateRandomAuraFlow();
break;
case 'b':
document.getElementById('preview-background-picker').click();
break;
}
}
});
updateColorInputs();
setTimeout(() => {
showNotification('BricksFusion Aura Flow Configurator loaded!');
}, 500);
function saveConfiguration() {
try {
localStorage.setItem('bricksfusion-aura-flow-config', JSON.stringify(fluidConfig));
} catch (e) {
}
}
function loadConfiguration() {
try {
const saved = localStorage.getItem('bricksfusion-aura-flow-config');
if (saved) {
const savedConfig = JSON.parse(saved);
Object.assign(fluidConfig, savedConfig);
savedConfig.colors.forEach((color, index) => {
document.getElementById(`color${index + 1}`).value = color;
document.getElementById(`color${index + 1}-hex`).value = color;
document.getElementById(`color${index + 1}-hsl`).value = hexToHsl(color);
});
document.getElementById('opacity').value = savedConfig.opacity;
document.getElementById('intensity').value = savedConfig.intensity;
document.getElementById('glow').value = savedConfig.glow;
document.getElementById('electric').value = savedConfig.electric;
document.getElementById('radius').value = savedConfig.radius;
document.getElementById('speed').value = savedConfig.speed;
document.getElementById('pulse-speed').value = savedConfig.pulseSpeed;
document.getElementById('transition').value = savedConfig.transition;
document.getElementById('auto-movement').checked = savedConfig.autoMovement;
document.getElementById('performance-mode').checked = savedConfig.performanceMode;
document.getElementById('opacity-value').textContent = savedConfig.opacity;
document.getElementById('intensity-value').textContent = savedConfig.intensity;
document.getElementById('glow-value').textContent = savedConfig.glow;
document.getElementById('electric-value').textContent = savedConfig.electric;
document.getElementById('radius-value').textContent = savedConfig.radius;
document.getElementById('speed-value').textContent = savedConfig.speed;
document.getElementById('pulse-speed-value').textContent = savedConfig.pulseSpeed;
document.getElementById('transition-value').textContent = savedConfig.transition;
initFluidAnimation();
}
} catch (e) {
}
}
const originalInitFluidAnimation = initFluidAnimation;
initFluidAnimation = function() {
originalInitFluidAnimation();
saveConfiguration();
};
loadConfiguration();
}
initializeUI();
});
</script>
</body>
</html>
Aura Flow
Creates a fluid, glowing energy effect that follows your mouse with electric trails and pulsing light. Perfect for premium hero sections and interactive showcases.
Move your mouse here
Colors
Choose 3 colors that will blend together in the fluid animation. The colors mix and flow creating an aurora-like effect.
Default: Blue, Cyan, Light Blue
Appearance
How transparent the entire effect is. 1.0 is fully visible, 0.5 is half transparent, 0.0 is invisible.
Default: 0.9
How bright and strong the colors appear. Higher values make it more vibrant and glowing.
Default: 1.0
Size of the glowing halo around the mouse cursor. Larger values create a bigger, softer glow.
Default: 0.8
Strength of the lightning-like trails that follow the mouse when you move it. Higher = more intense electric effect.
Default: 1.2
Animation
Overall animation speed. Affects how fast the fluid moves and swirls. Lower = slower and calmer.
Default: 1.0
How fast the light pulses and breathes. Higher values create faster, more energetic pulsing.
Default: 8.0
How smoothly the effect follows your mouse. Lower = smoother and laggier, higher = snappier and more responsive.
Default: 0.08
Advanced
Size of the main fluid area. Larger values spread the effect across a bigger area.
Default: 100
Performance
This element uses WebGL for advanced graphics rendering, making it resource-intensive. Use it sparingly on a page - ideally just one instance per page. It may affect performance on older devices or computers. The effect automatically adjusts to 30fps on mobile devices to save battery.