Documentation
v1.9
GETTING STARTED
DYNAMIC ISLAND
Premium element
Core Background
Ambient Gradient
Transform your web space into a dynamic visual experience, reacting seamlessly to user interaction with smooth, fluid color transitions.
Preview
Open previewQuick Setup
data-ambient-gradient
Required
Add this attribute to enable the gradient effect
data-ambient-gradient-auto
Optional
Sets the gradient animation to automatic without mouse-over
Available Presets
data-ambient-gradient-preset="preset-name"
Optional
sunset
fresh
peach
pastel
sunrise
sunset
twilight
twilight
autumn
We're here to help
Choose how you'd like to connect with our support team
Contact Support
Get help from our team of experts
Response within 24 hours
<!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>Ambient Gradient Configurator</title>
<style>
:root {
--background: #121212;
--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;
}
* {
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;
}
.container {
max-width: 100%;
margin: 0 auto;
padding: 2.5rem 1.5rem;
}
.content {
display: flex;
flex-wrap: wrap;
gap: 2.5rem;
}
.preview-section {
flex: 1;
min-width: 300px;
}
.controls-section {
flex: 1;
min-width: 300px;
max-width: 500px;
}
.card {
background-color: var(--card-bg);
border-radius: var(--card-radius);
box-shadow: var(--shadow);
overflow: hidden;
margin-bottom: 2rem;
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: 500px;
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;
}
.card-heading {
padding: 1.25rem 1.5rem;
font-size: var(--text-s);
font-weight: 600;
border-bottom: 1px solid var(--border);
letter-spacing: 0.3px;
}
.card-content {
padding: 1.75rem;
}
.control-group {
margin-bottom: 1.75rem;
}
.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;
}
.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;
}
input[type="range"] {
-webkit-appearance: none;
width: 100%;
height: 4px;
background: var(--track);
border-radius: 2px;
outline: none;
margin: 0.8rem 0;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 18px;
height: 18px;
background: var(--thumb);
border-radius: 50%;
cursor: pointer;
transition: var(--transition);
box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
}
input[type="range"]::-webkit-slider-thumb:hover {
transform: scale(1.1);
box-shadow: 0 0 8px rgba(239, 96, 19, 0.5);
}
input[type="range"]::-moz-range-thumb {
width: 18px;
height: 18px;
background: var(--thumb);
border: none;
border-radius: 50%;
cursor: pointer;
transition: var(--transition);
box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
}
input[type="range"]::-moz-range-thumb:hover {
transform: scale(1.1);
box-shadow: 0 0 8px rgba(239, 96, 19, 0.5);
}
input[type="text"],
input[type="number"],
select {
width: 100%;
padding: 0.8rem 1rem;
border: 1px solid var(--border);
border-radius: var(--input-radius);
font-family: var(--font);
font-size: var(--text-xs);
color: var(--text-primary);
background-color: rgba(30, 30, 30, 0.7);
margin-bottom: 0.75rem;
outline: none;
transition: var(--transition);
}
input[type="text"]:focus,
input[type="number"]:focus,
select:focus {
border-color: var(--accent);
box-shadow: 0 0 0 2px rgba(239, 96, 19, 0.2);
}
.color-mode {
display: flex;
gap: 1rem;
margin-bottom: 1.5rem;
}
.radio-label {
display: flex;
align-items: center;
gap: 0.5rem;
cursor: pointer;
position: relative;
padding-left: 1.5rem;
font-size: var(--text-xs);
}
.radio-label input {
position: absolute;
opacity: 0;
cursor: pointer;
}
.radio-checkmark {
position: absolute;
top: 2px;
left: 0;
height: 16px;
width: 16px;
background-color: var(--card-bg);
border: 1px solid var(--border);
border-radius: 50%;
}
.radio-label input:checked ~ .radio-checkmark {
background-color: var(--card-bg);
border-color: var(--accent);
}
.radio-checkmark:after {
content: "";
position: absolute;
display: none;
}
.radio-label input:checked ~ .radio-checkmark:after {
display: block;
top: 3px;
left: 3px;
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--accent);
}
.color-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 0.8rem;
margin-bottom: 1.2rem;
}
.color-input-group {
position: relative;
border: 1px solid var(--border);
border-radius: var(--input-radius);
padding: 0.4rem;
background-color: rgba(30, 30, 30, 0.7);
transition: var(--transition);
}
.color-input-group:hover {
border-color: var(--accent);
box-shadow: 0 0 0 1px rgba(239, 96, 19, 0.1);
}
.color-preview {
position: absolute;
top: 0.75rem;
left: 0.75rem;
width: 24px;
height: 24px;
border-radius: 4px;
pointer-events: none;
}
input[type="color"] {
-webkit-appearance: none;
border: none;
width: 100%;
height: 24px;
cursor: pointer;
background-color: transparent;
}
input[type="color"]::-webkit-color-swatch-wrapper {
padding: 0;
}
input[type="color"]::-webkit-color-swatch {
border: none;
border-radius: 4px;
}
input[type="color"]::-moz-color-swatch {
border: none;
border-radius: 4px;
}
.color-text {
padding-left: 2.5rem;
border: none;
background-color: transparent;
color: var(--text-secondary);
font-size: 0.8rem;
letter-spacing: 0.3px;
}
.color-text:focus {
color: var(--text-primary);
box-shadow: none;
}
.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;
}
.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:focus + .slider {
box-shadow: 0 0 1px var(--accent);
}
input:checked + .slider:before {
transform: translateX(24px);
}
.btn {
display: block;
width: 100%;
padding: 1rem 1.5rem;
background-color: var(--accent);
color: white;
font-family: var(--font);
font-size: var(--text-xs);
font-weight: 600;
text-align: center;
border: none;
border-radius: var(--button-radius);
cursor: pointer;
transition: var(--transition);
letter-spacing: 0.3px;
}
.btn:hover {
background-color: var(--accent-hover);
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(239, 96, 19, 0.3);
}
.btn:active {
transform: translateY(0);
box-shadow: 0 2px 8px rgba(239, 96, 19, 0.2);
}
.instructions {
background-color: var(--card-bg);
padding: 1.75rem;
border-radius: var(--card-radius);
margin-bottom: 2rem;
box-shadow: var(--shadow);
border: 1px solid var(--border);
}
.instructions h3 {
font-size: var(--text-s);
margin-bottom: 1rem;
color: var(--text-primary);
font-weight: 600;
letter-spacing: 0.3px;
}
.instructions ol {
padding-left: 1.5rem;
}
.instructions li {
margin-bottom: 0.75rem;
font-size: var(--text-xs);
color: var(--text-secondary);
line-height: 1.6;
}
.instructions 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;
}
.instructions strong {
font-weight: 500;
color: var(--text-primary);
}
.notice {
background-color: rgba(239, 96, 19, 0.08);
color: #ff8c51;
padding: 1rem 1.2rem;
border-radius: var(--input-radius);
font-size: var(--text-xs);
margin-top: 1.5rem;
border-left: 3px solid var(--accent);
letter-spacing: 0.2px;
line-height: 1.6;
}
.code-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.8);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
visibility: hidden;
opacity: 0;
transition: all 0.3s ease;
backdrop-filter: blur(5px);
}
.code-modal.active {
visibility: visible;
opacity: 1;
}
.modal-content {
width: 90%;
max-width: 1000px;
max-height: 90vh;
background-color: var(--card-bg);
border-radius: var(--card-radius);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
overflow: hidden;
transform: translateY(20px);
transition: all 0.3s ease;
border: 1px solid var(--border);
}
.code-modal.active .modal-content {
transform: translateY(0);
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.25rem 1.5rem;
border-bottom: 1px solid var(--border);
}
.modal-title {
font-size: var(--text-s);
font-weight: 600;
letter-spacing: 0.3px;
}
.close-modal {
background: none;
border: none;
font-size: var(--text-s);
cursor: pointer;
color: var(--text-secondary);
transition: var(--transition);
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.close-modal:hover {
color: var(--text-primary);
background-color: rgba(255, 255, 255, 0.1);
}
.modal-body {
padding: 1.75rem;
overflow-y: auto;
max-height: calc(90vh - 130px);
}
.code-display {
background-color: #161616;
color: #e4e4e4;
border-radius: var(--card-radius);
padding: 1.5rem;
overflow-x: auto;
font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
font-size: var(--text-xs);
line-height: 1.6;
white-space: pre;
margin-bottom: 1rem;
max-height: 400px;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
border: 1px solid #252525;
}
.modal-footer {
padding: 1.25rem 1.75rem;
border-top: 1px solid var(--border);
display: flex;
justify-content: flex-end;
gap: 1rem;
}
.copy-btn,
.download-file-btn {
padding: 0.8rem 1.2rem;
background-color: var(--accent);
color: white;
font-family: var(--font);
font-size: var(--text-xs);
font-weight: 500;
border: none;
border-radius: var(--button-radius);
cursor: pointer;
transition: var(--transition);
letter-spacing: 0.2px;
}
.copy-btn:hover,
.download-file-btn:hover {
background-color: var(--accent-hover);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(239, 96, 19, 0.3);
}
.copy-btn:active,
.download-file-btn:active {
transform: scale(0.98);
box-shadow: 0 2px 8px rgba(239, 96, 19, 0.2);
}
.premium-card {
margin-top: 2rem;
border: 1px solid var(--border);
transition: transform 0.3s ease, box-shadow 0.3s ease;
background: linear-gradient(145deg, #212121, #1e1e1e);
}
.premium-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
border-color: rgba(239, 96, 19, 0.3);
}
.premium-badge {
display: inline-block;
background: linear-gradient(90deg, #ef6013, #ff8c51);
color: white;
font-size: var(--text-xs);
padding: 0.3rem 0.7rem;
border-radius: 6px;
margin-right: 0.75rem;
font-weight: 600;
vertical-align: middle;
letter-spacing: 0.3px;
box-shadow: 0 2px 8px rgba(239, 96, 19, 0.3);
}
.premium-description {
color: var(--text-secondary);
font-size: var(--text-xs);
margin-bottom: 1.5rem;
line-height: 1.7;
letter-spacing: 0.2px;
}
.feature-list {
list-style: none;
padding: 0;
margin: 0 0 1.75rem 0;
}
.feature-list li {
position: relative;
padding-left: 1.75rem;
font-size: var(--text-xs);
color: var(--text-secondary);
margin-bottom: 0.9rem;
letter-spacing: 0.2px;
line-height: 1.5;
}
.feature-list li:before {
content: "✓";
position: absolute;
left: 0;
color: var(--accent);
font-weight: bold;
}
.premium-btn {
background: linear-gradient(90deg, #ef6013, #ff8c51);
transition: transform 0.2s ease, box-shadow 0.2s ease;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
letter-spacing: 0.3px;
}
.premium-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(239, 96, 19, 0.4);
}
.btn-icon {
font-size: 1.1em;
}
.gradient-direction {
position: relative;
width: 100%;
height: 150px;
border-radius: var(--input-radius);
border: 1px solid var(--border);
margin-bottom: 1rem;
cursor: pointer;
overflow: hidden;
background-color: rgba(30, 30, 30, 0.7);
}
.direction-marker {
position: absolute;
width: 22px;
height: 22px;
border-radius: 50%;
background-color: var(--accent);
transform: translate(-50%, -50%);
z-index: 2;
cursor: move;
box-shadow: 0 2px 8px rgba(239, 96, 19, 0.4);
}
.direction-line {
position: absolute;
height: 2px;
background-color: rgba(255, 255, 255, 0.5);
transform-origin: left center;
pointer-events: none;
z-index: 1;
}
.direction-value {
font-size: var(--text-xs);
color: var(--text-secondary);
text-align: center;
margin-top: 0.5rem;
}
.responsive-options {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 0.8rem;
}
.responsive-option {
font-size: var(--text-xs);
padding: 0.75rem;
text-align: center;
border: 1px solid var(--border);
border-radius: var(--input-radius);
cursor: pointer;
transition: var(--transition);
background-color: rgba(30, 30, 30, 0.7);
}
.responsive-option.active {
background-color: var(--accent);
color: white;
border-color: var(--accent);
box-shadow: 0 2px 8px rgba(239, 96, 19, 0.4);
}
@media (max-width: 768px) {
.content {
flex-direction: column;
}
.premium-card {
margin-top: 1.5rem;
}
.preview-container {
height: 350px;
}
}
.copy-attribute {
display: inline-flex;
align-items: center;
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;
cursor: pointer;
transition: var(--transition);
margin: 0 0.2rem;
}
.copy-attribute:hover {
background-color: rgba(239, 96, 19, 0.2);
}
.copy-icon {
margin-left: 0.5rem;
font-size: 0.9em;
opacity: 0.7;
}
.copy-attribute:hover .copy-icon {
opacity: 1;
}
</style>
</head>
<body>
<div class="container">
<div class="instructions">
<h3>How to Use in Bricks Builder</h3>
<ol>
<li>Design your ambient gradient effect using the controls below</li>
<li>Click the <strong>Get Code</strong> button and copy the JavaScript</li>
<li>In Bricks Builder, add a <strong>Code</strong> element and paste the JavaScript</li>
<li>Add the attribute <div class="copy-attribute" data-attribute="data-ambient-gradient">data-ambient-gradient<span class="copy-icon">📋</span></div> to any section you want to apply the effect to</li>
</ol>
<div class="notice">This tool creates a custom script with your settings built-in. You only need to add the data attribute to see your exact configuration!</div>
</div>
<div class="content">
<section class="preview-section">
<div class="preview-container" id="gradient-preview" data-ambient-gradient="true">
<div class="preview-content"></div>
</div>
<div class="card premium-card">
<div class="card-heading">
<span class="premium-badge">BricksFusion Exclusive</span>
Get Your Custom Gradient Effect
</div>
<div class="card-content">
<p class="premium-description">Create stunning interactive gradient backgrounds for your Bricks Builder projects with this custom-crafted JavaScript snippet.</p>
<ul class="feature-list">
<li>Easy implementation with a single attribute</li>
<li>Interactive mouse-follow or auto-animation modes</li>
<li>Fully customizable colors and animations</li>
<li>Works with any Bricks Builder section</li>
</ul>
<button class="btn premium-btn" id="download-btn" data-protection-animation="true">
<span class="btn-icon">⚡</span>
Get Instant Code
</button>
</div>
</div>
</section>
<section class="controls-section">
<div class="card">
<div class="card-heading">Gradient Colors</div>
<div class="card-content">
<div class="control-group">
<div class="control-label">
<span class="label-text">Color Palette</span>
</div>
<div class="color-grid">
<div class="color-input-group">
<input type="color" id="color1" value="#ee7752">
<input type="text" id="color1-text" class="color-text" value="#ee7752">
</div>
<div class="color-input-group">
<input type="color" id="color2" value="#e73c7e">
<input type="text" id="color2-text" class="color-text" value="#e73c7e">
</div>
<div class="color-input-group">
<input type="color" id="color3" value="#23a6d5">
<input type="text" id="color3-text" class="color-text" value="#23a6d5">
</div>
<div class="color-input-group">
<input type="color" id="color4" value="#23d5ab">
<input type="text" id="color4-text" class="color-text" value="#23d5ab">
</div>
</div>
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">Gradient Angle</span>
<span class="value-text"><span id="angle-value">45</span>°</span>
</div>
<input type="range" id="gradient-angle" min="0" max="360" value="45">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">Background Size</span>
<span class="value-text"><span id="bg-size-value">400</span>%</span>
</div>
<input type="range" id="bg-size" min="100" max="800" value="400" step="50">
</div>
</div>
</div>
<div class="card">
<div class="card-heading">Animation Settings</div>
<div class="card-content">
<div class="switch-container">
<span class="switch-label">Auto Animation</span>
<label class="switch">
<input type="checkbox" id="auto-animate">
<span class="slider"></span>
</label>
</div>
<div id="auto-animation-settings">
<div class="control-group">
<div class="control-label">
<span class="label-text">Animation Speed</span>
<span class="value-text" id="auto-speed-value">0.005</span>
</div>
<input type="range" id="auto-speed" min="0.001" max="0.01" step="0.001" value="0.005">
</div>
</div>
<div id="mouse-animation-settings">
<div class="control-group">
<div class="control-label">
<span class="label-text">Mouse Influence</span>
<span class="value-text" id="mouse-influence-value">100</span>%</span>
</div>
<input type="range" id="mouse-influence" min="0" max="100" value="100">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">Idle Animation Speed</span>
<span class="value-text" id="idle-speed-value">0.05</span>
</div>
<input type="range" id="idle-speed" min="0.01" max="0.2" step="0.01" value="0.05">
</div>
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">Transition Smoothness</span>
<span class="value-text" id="smoothness-value">0.5</span>s</span>
</div>
<input type="range" id="transition-smoothness" min="0" max="2" step="0.1" value="0.5">
</div>
</div>
</div>
<div class="card">
<div class="card-heading">Advanced Options</div>
<div class="card-content">
<div class="control-group">
<div class="control-label">
<span class="label-text">Z-Index Position</span>
<span class="value-text" id="z-index-value">1</span>
</div>
<input type="range" id="z-index" min="-10" max="10" value="1" step="1">
</div>
<div class="switch-container">
<span class="switch-label">Keep Content Centered</span>
<label class="switch">
<input type="checkbox" id="center-content" checked>
<span class="slider"></span>
</label>
</div>
<div class="switch-container">
<span class="switch-label">Border Radius Inherit</span>
<label class="switch">
<input type="checkbox" id="border-radius-inherit" checked>
<span class="slider"></span>
</label>
</div>
</div>
</div>
</section>
</div>
</div>
<div class="code-modal" id="code-modal">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">JavaScript Code</h3>
<button class="close-modal" id="close-modal">×</button>
</div>
<div class="modal-body">
<div class="code-display" id="js-code"></div>
</div>
<div class="modal-footer">
<button class="copy-btn" id="copy-btn">Copy to Clipboard</button>
<button class="download-file-btn" id="download-file-btn">Download JS File</button>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Configuration object for the ambient gradient
let gradientConfig = {
colors: ['#ee7752', '#e73c7e', '#23a6d5', '#23d5ab'],
angle: 45,
backgroundSize: 400, // in percentage
autoAnimate: false,
autoSpeed: 0.005,
mouseInfluence: 100, // in percentage
idleSpeed: 0.05,
transitionSmoothness: 0.5, // in seconds
zIndex: 1,
centerContent: true,
borderRadiusInherit: true
};
let activeGradient = null;
function initAmbientGradient() {
const sections = document.querySelectorAll('[data-ambient-gradient]:not([data-ambient-initialized="true"])');
sections.forEach((section) => {
// Get configuration from data attributes or use defaults
const isAutoAnimate = section.hasAttribute('data-ambient-gradient-auto') || gradientConfig.autoAnimate;
// Parse colors from data attributes or use defaults
let colors = [];
for (let i = 1; i <= 4; i++) {
const colorAttr = `data-ambient-gradient-color${i}`;
if (section.hasAttribute(colorAttr)) {
colors.push(section.getAttribute(colorAttr));
}
}
if (colors.length === 0) {
colors = gradientConfig.colors;
}
// Get other configuration options from data attributes or use defaults
const angle = section.hasAttribute('data-ambient-gradient-angle')
? parseFloat(section.getAttribute('data-ambient-gradient-angle'))
: gradientConfig.angle;
const bgSize = section.hasAttribute('data-ambient-gradient-size')
? parseFloat(section.getAttribute('data-ambient-gradient-size'))
: gradientConfig.backgroundSize;
const autoSpeed = section.hasAttribute('data-ambient-gradient-auto-speed')
? parseFloat(section.getAttribute('data-ambient-gradient-auto-speed'))
: gradientConfig.autoSpeed;
const mouseInfluence = section.hasAttribute('data-ambient-gradient-mouse-influence')
? parseFloat(section.getAttribute('data-ambient-gradient-mouse-influence'))
: gradientConfig.mouseInfluence / 100; // Convert from percentage to decimal
const idleSpeed = section.hasAttribute('data-ambient-gradient-idle-speed')
? parseFloat(section.getAttribute('data-ambient-gradient-idle-speed'))
: gradientConfig.idleSpeed;
const transitionSmoothness = section.hasAttribute('data-ambient-gradient-transition')
? parseFloat(section.getAttribute('data-ambient-gradient-transition'))
: gradientConfig.transitionSmoothness;
const zIndex = section.hasAttribute('data-ambient-gradient-z-index')
? parseInt(section.getAttribute('data-ambient-gradient-z-index'))
: gradientConfig.zIndex;
const centerContent = section.hasAttribute('data-ambient-gradient-center')
? section.getAttribute('data-ambient-gradient-center') === 'true'
: gradientConfig.centerContent;
const borderRadiusInherit = section.hasAttribute('data-ambient-gradient-border-inherit')
? section.getAttribute('data-ambient-gradient-border-inherit') === 'true'
: gradientConfig.borderRadiusInherit;
// Create configuration object for this specific section
const options = {
colors,
angle,
backgroundSize: bgSize,
autoAnimate: isAutoAnimate,
autoSpeed,
mouseInfluence,
idleSpeed,
transitionSmoothness,
zIndex,
centerContent,
borderRadiusInherit
};
// Setup state object for tracking mouse position and animation
const state = {
mouseX: 0.5,
mouseY: 0.5,
targetX: 0.5,
targetY: 0.5,
isMouseInside: false,
isAutoAnimate: isAutoAnimate
};
// Setup the gradient element
setupGradient(section, options);
// Add event listeners for mouse interaction if not in auto mode
if (!isAutoAnimate) {
setupEventListeners(section, state, options);
}
// Start the animation
startAnimation(section, state, options);
// Mark as initialized
section.dataset.ambientInitialized = 'true';
// Store reference to active gradient for preview purposes
if (section.id === 'gradient-preview') {
activeGradient = {
element: section,
state,
options
};
// Update the configuration to match
gradientConfig = {
colors: options.colors,
angle: options.angle,
backgroundSize: options.backgroundSize,
autoAnimate: options.autoAnimate,
autoSpeed: options.autoSpeed,
mouseInfluence: options.mouseInfluence * 100, // Convert to percentage for UI
idleSpeed: options.idleSpeed,
transitionSmoothness: options.transitionSmoothness,
zIndex: options.zIndex,
centerContent: options.centerContent,
borderRadiusInherit: options.borderRadiusInherit
};
}
});
}
function setupGradient(element, options) {
// Create gradient element
const gradientElement = document.createElement('div');
// Set positioning styles
gradientElement.style.position = 'absolute';
gradientElement.style.top = '0';
gradientElement.style.left = '0';
gradientElement.style.width = '100%';
gradientElement.style.height = '100%';
// Set gradient styles
gradientElement.style.background = 'linear-gradient(' + options.angle + 'deg, ' + options.colors.join(', ') + ')';
gradientElement.style.backgroundSize = options.backgroundSize + '% ' + options.backgroundSize + '%';
gradientElement.style.transition = 'background-position ' + options.transitionSmoothness + 's ease-out';
gradientElement.style.zIndex = options.zIndex;
// Set border radius if inherit is enabled
if (options.borderRadiusInherit) {
gradientElement.style.borderRadius = 'inherit';
}
// Ensure parent element has the right positioning context
const computedStyle = window.getComputedStyle(element);
if (computedStyle.position === 'static') {
element.style.position = 'relative';
}
// Insert gradient element as first child
if (element.firstChild) {
element.insertBefore(gradientElement, element.firstChild);
} else {
element.appendChild(gradientElement);
}
// Store reference to gradient element
element.gradientElement = gradientElement;
// If centerContent is true, center all child elements
if (options.centerContent) {
Array.from(element.children).forEach(child => {
if (child !== gradientElement) {
if (getComputedStyle(child).position === 'static') {
child.style.position = 'relative';
}
child.style.zIndex = '1';
}
});
}
}
function setupEventListeners(element, state, options) {
// Mouse move event
element.addEventListener('mousemove', (e) => {
const rect = element.getBoundingClientRect();
// Calculate mouse position as a percentage of element dimensions
state.mouseX = (e.clientX - rect.left) / rect.width;
state.mouseY = (e.clientY - rect.top) / rect.height;
state.isMouseInside = true;
});
// Mouse leave event
element.addEventListener('mouseleave', () => {
state.isMouseInside = false;
// Set random target for idle animation
state.targetX = Math.random();
state.targetY = Math.random();
});
// Mouse enter event
element.addEventListener('mouseenter', () => {
state.isMouseInside = true;
});
// Touch events for mobile
element.addEventListener('touchmove', (e) => {
e.preventDefault();
const touch = e.touches[0];
const rect = element.getBoundingClientRect();
state.mouseX = (touch.clientX - rect.left) / rect.width;
state.mouseY = (touch.clientY - rect.top) / rect.height;
state.isMouseInside = true;
}, { passive: false });
element.addEventListener('touchend', () => {
state.isMouseInside = false;
state.targetX = Math.random();
state.targetY = Math.random();
});
// Focus events for accessibility
element.addEventListener('focus', () => {
state.isMouseInside = true;
});
element.addEventListener('blur', () => {
state.isMouseInside = false;
state.targetX = Math.random();
state.targetY = Math.random();
});
}
function startAnimation(element, state, options) {
let animationFrameId;
let autoAnimateTime = 0;
function updateGradient() {
// Handle auto animation mode
if (state.isAutoAnimate) {
autoAnimateTime += options.autoSpeed;
// Create circular motion using sine and cosine
state.mouseX = (Math.sin(autoAnimateTime) + 1) / 2;
state.mouseY = (Math.cos(autoAnimateTime * 0.8) + 1) / 2;
}
// Handle mouse-based animation
else if (!state.isMouseInside) {
// Smoothly animate to random target when mouse is not in element
state.mouseX += (state.targetX - state.mouseX) * options.idleSpeed;
state.mouseY += (state.targetY - state.mouseY) * options.idleSpeed;
}
// Apply mouse influence
const effectiveX = state.mouseX * options.mouseInfluence + 0.5 * (1 - options.mouseInfluence);
const effectiveY = state.mouseY * options.mouseInfluence + 0.5 * (1 - options.mouseInfluence);
// Update background position
const posX = effectiveX * 100;
const posY = effectiveY * 100;
element.gradientElement.style.backgroundPosition = posX + '% ' + posY + '%';
}
// Animation loop
function smoothUpdate() {
updateGradient();
animationFrameId = requestAnimationFrame(smoothUpdate);
}
// Start animation
smoothUpdate();
// Add intersection observer to pause animation when not visible
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// Resume animation when visible
if (!animationFrameId) {
smoothUpdate();
}
} else {
// Pause animation when not visible
if (animationFrameId) {
cancelAnimationFrame(animationFrameId);
animationFrameId = null;
}
}
});
}, { threshold: 0 });
observer.observe(element);
// Store cleanup function
element._cleanupGradient = () => {
if (animationFrameId) {
cancelAnimationFrame(animationFrameId);
}
observer.disconnect();
if (element.gradientElement && element.gradientElement.parentNode) {
element.gradientElement.parentNode.removeChild(element.gradientElement);
}
element.dataset.ambientInitialized = 'false';
};
}
function updateGradientPreview() {
const preview = document.getElementById('gradient-preview');
if (!preview) return;
// If not initialized, initialize it
if (preview.dataset.ambientInitialized !== 'true') {
initAmbientGradient();
return;
}
// If we have an active gradient, clean it up
if (preview._cleanupGradient) {
preview._cleanupGradient();
}
// Set data attributes based on current configuration
preview.setAttribute('data-ambient-gradient', 'true');
// Set auto animation attribute
if (gradientConfig.autoAnimate) {
preview.setAttribute('data-ambient-gradient-auto', 'true');
} else {
preview.removeAttribute('data-ambient-gradient-auto');
}
// Set colors
for (let i = 0; i < gradientConfig.colors.length; i++) {
preview.setAttribute(`data-ambient-gradient-color${i+1}`, gradientConfig.colors[i]);
}
// Set other attributes
preview.setAttribute('data-ambient-gradient-angle', gradientConfig.angle);
preview.setAttribute('data-ambient-gradient-size', gradientConfig.backgroundSize);
preview.setAttribute('data-ambient-gradient-auto-speed', gradientConfig.autoSpeed);
preview.setAttribute('data-ambient-gradient-mouse-influence', gradientConfig.mouseInfluence / 100);
preview.setAttribute('data-ambient-gradient-idle-speed', gradientConfig.idleSpeed);
preview.setAttribute('data-ambient-gradient-transition', gradientConfig.transitionSmoothness);
preview.setAttribute('data-ambient-gradient-z-index', gradientConfig.zIndex);
preview.setAttribute('data-ambient-gradient-center', gradientConfig.centerContent);
preview.setAttribute('data-ambient-gradient-border-inherit', gradientConfig.borderRadiusInherit);
// Reinitialize
initAmbientGradient();
}
function generateJavaScriptCode() {
// Create an array of colors
const colors = [];
for (let i = 1; i <= 4; i++) {
colors.push(document.getElementById(`color${i}`).value);
}
// Get other configuration values
const angle = document.getElementById('gradient-angle').value;
const bgSize = document.getElementById('bg-size').value;
const autoAnimate = document.getElementById('auto-animate').checked;
const autoSpeed = document.getElementById('auto-speed').value;
const mouseInfluence = document.getElementById('mouse-influence').value / 100; // Convert to decimal
const idleSpeed = document.getElementById('idle-speed').value;
const transitionSmoothness = document.getElementById('transition-smoothness').value;
const zIndex = document.getElementById('z-index').value;
const centerContent = document.getElementById('center-content').checked;
const borderRadiusInherit = document.getElementById('border-radius-inherit').checked;
return "(function() {\n" +
" // Configuration object for ambient gradient\n" +
" const defaultConfig = {\n" +
" colors: " + JSON.stringify(colors) + ",\n" +
" angle: " + angle + ",\n" +
" backgroundSize: " + bgSize + ",\n" +
" autoAnimate: " + autoAnimate + ",\n" +
" autoSpeed: " + autoSpeed + ",\n" +
" mouseInfluence: " + mouseInfluence + ",\n" +
" idleSpeed: " + idleSpeed + ",\n" +
" transitionSmoothness: " + transitionSmoothness + ",\n" +
" zIndex: " + zIndex + ",\n" +
" centerContent: " + centerContent + ",\n" +
" borderRadiusInherit: " + borderRadiusInherit + "\n" +
" };\n" +
" \n" +
" // Gradient preset colors (can be used as alternatives)\n" +
" const gradientPresets = {\n" +
" 'sunset': ['#ee7752', '#e73c7e', '#23a6d5', '#23d5ab'],\n" +
" 'fresh': ['#ff6b6b', '#4ecdc4', '#45b7d1', '#f7fff7'],\n" +
" 'peach': ['#ff9a9e', '#fad0c4', '#ffecd2', '#fcb69f'],\n" +
" 'pastel': ['#a8edea', '#fed6e3', '#f4d4d4', '#e2ebf0'],\n" +
" 'lavender': ['#5ee7df', '#b490ca', '#8ea5eb', '#c3f5ef'],\n" +
" 'spring': ['#d4fc79', '#96e6a1', '#66a6ff', '#89f7fe'],\n" +
" 'sunrise': ['#fa709a', '#fee140', '#ffb199', '#fec84e'],\n" +
" 'ocean': ['#43e97b', '#38f9d7', '#3f51b1', '#4a00e0'],\n" +
" 'twilight': ['#8ec5fc', '#e0c3fc', '#fbc2eb', '#a6c1ee'],\n" +
" 'autumn': ['#f6d365', '#fda085', '#fbc2eb', '#a6c1ee']\n" +
" };\n" +
" \n" +
" // Initialize ambient gradient on sections with data-ambient-gradient attribute\n" +
" function initAmbientGradient() {\n" +
" const sections = document.querySelectorAll('[data-ambient-gradient]:not([data-ambient-initialized=\"true\"])');\n" +
" \n" +
" sections.forEach((section) => {\n" +
" // Get configuration from data attributes or use defaults\n" +
" const isAutoAnimate = section.hasAttribute('data-ambient-gradient-auto') || defaultConfig.autoAnimate;\n" +
" \n" +
" // Parse colors from data attributes or use defaults\n" +
" let colors = [];\n" +
" for (let i = 1; i <= 4; i++) {\n" +
" const colorAttr = 'data-ambient-gradient-color' + i;\n" +
" if (section.hasAttribute(colorAttr)) {\n" +
" colors.push(section.getAttribute(colorAttr));\n" +
" }\n" +
" }\n" +
" \n" +
" // If no colors specified, check for preset or use default colors\n" +
" if (colors.length === 0) {\n" +
" const presetName = section.getAttribute('data-ambient-gradient-preset');\n" +
" colors = presetName && gradientPresets[presetName] \n" +
" ? gradientPresets[presetName] \n" +
" : defaultConfig.colors;\n" +
" }\n" +
" \n" +
" // Get other configuration options from data attributes or use defaults\n" +
" const angle = section.hasAttribute('data-ambient-gradient-angle') \n" +
" ? parseFloat(section.getAttribute('data-ambient-gradient-angle')) \n" +
" : defaultConfig.angle;\n" +
" \n" +
" const bgSize = section.hasAttribute('data-ambient-gradient-size') \n" +
" ? parseFloat(section.getAttribute('data-ambient-gradient-size')) \n" +
" : defaultConfig.backgroundSize;\n" +
" \n" +
" const autoSpeed = section.hasAttribute('data-ambient-gradient-auto-speed') \n" +
" ? parseFloat(section.getAttribute('data-ambient-gradient-auto-speed')) \n" +
" : defaultConfig.autoSpeed;\n" +
" \n" +
" const mouseInfluence = section.hasAttribute('data-ambient-gradient-mouse-influence') \n" +
" ? parseFloat(section.getAttribute('data-ambient-gradient-mouse-influence')) \n" +
" : defaultConfig.mouseInfluence;\n" +
" \n" +
" const idleSpeed = section.hasAttribute('data-ambient-gradient-idle-speed') \n" +
" ? parseFloat(section.getAttribute('data-ambient-gradient-idle-speed')) \n" +
" : defaultConfig.idleSpeed;\n" +
" \n" +
" const transitionSmoothness = section.hasAttribute('data-ambient-gradient-transition') \n" +
" ? parseFloat(section.getAttribute('data-ambient-gradient-transition')) \n" +
" : defaultConfig.transitionSmoothness;\n" +
" \n" +
" const zIndex = section.hasAttribute('data-ambient-gradient-z-index') \n" +
" ? parseInt(section.getAttribute('data-ambient-gradient-z-index')) \n" +
" : defaultConfig.zIndex;\n" +
" \n" +
" const centerContent = section.hasAttribute('data-ambient-gradient-center') \n" +
" ? section.getAttribute('data-ambient-gradient-center') === 'true' \n" +
" : defaultConfig.centerContent;\n" +
" \n" +
" const borderRadiusInherit = section.hasAttribute('data-ambient-gradient-border-inherit') \n" +
" ? section.getAttribute('data-ambient-gradient-border-inherit') === 'true' \n" +
" : defaultConfig.borderRadiusInherit;\n" +
" \n" +
" // Create configuration object for this specific section\n" +
" const options = {\n" +
" colors,\n" +
" angle,\n" +
" backgroundSize: bgSize,\n" +
" autoAnimate: isAutoAnimate,\n" +
" autoSpeed,\n" +
" mouseInfluence,\n" +
" idleSpeed,\n" +
" transitionSmoothness,\n" +
" zIndex,\n" +
" centerContent,\n" +
" borderRadiusInherit\n" +
" };\n" +
" \n" +
" // Setup state object for tracking mouse position and animation\n" +
" const state = {\n" +
" mouseX: 0.5,\n" +
" mouseY: 0.5,\n" +
" targetX: 0.5,\n" +
" targetY: 0.5,\n" +
" isMouseInside: false,\n" +
" isAutoAnimate: isAutoAnimate\n" +
" };\n" +
" \n" +
" // Setup the gradient element\n" +
" setupGradient(section, options);\n" +
" \n" +
" // Add event listeners for mouse interaction if not in auto mode\n" +
" if (!isAutoAnimate) {\n" +
" setupEventListeners(section, state, options);\n" +
" }\n" +
" \n" +
" // Start the animation\n" +
" startAnimation(section, state, options);\n" +
" \n" +
" // Mark as initialized\n" +
" section.dataset.ambientInitialized = 'true';\n" +
" });\n" +
" }\n" +
" \n" +
" function setupGradient(element, options) {\n" +
" // Create gradient element\n" +
" const gradientElement = document.createElement('div');\n" +
" \n" +
" // Set positioning styles\n" +
" gradientElement.style.position = 'absolute';\n" +
" gradientElement.style.top = '0';\n" +
" gradientElement.style.left = '0';\n" +
" gradientElement.style.width = '100%';\n" +
" gradientElement.style.height = '100%';\n" +
" \n" +
" // Set gradient styles\n" +
" gradientElement.style.background = 'linear-gradient(' + options.angle + 'deg, ' + options.colors.join(', ') + ')';\n" +
" gradientElement.style.backgroundSize = options.backgroundSize + '% ' + options.backgroundSize + '%';\n" +
" gradientElement.style.transition = 'background-position ' + options.transitionSmoothness + 's ease-out';\n" +
" gradientElement.style.zIndex = options.zIndex;\n" +
" \n" +
" // Set border radius if inherit is enabled\n" +
" if (options.borderRadiusInherit) {\n" +
" gradientElement.style.borderRadius = 'inherit';\n" +
" }\n" +
" \n" +
" // Ensure parent element has the right positioning context\n" +
" const computedStyle = window.getComputedStyle(element);\n" +
" if (computedStyle.position === 'static') {\n" +
" element.style.position = 'relative';\n" +
" }\n" +
" \n" +
" // Insert gradient element as first child\n" +
" if (element.firstChild) {\n" +
" element.insertBefore(gradientElement, element.firstChild);\n" +
" } else {\n" +
" element.appendChild(gradientElement);\n" +
" }\n" +
" \n" +
" // Store reference to gradient element\n" +
" element.gradientElement = gradientElement;\n" +
" \n" +
" // If centerContent is true, ensure all child elements are above the gradient\n" +
" if (options.centerContent) {\n" +
" Array.from(element.children).forEach(child => {\n" +
" if (child !== gradientElement) {\n" +
" if (getComputedStyle(child).position === 'static') {\n" +
" child.style.position = 'relative';\n" +
" }\n" +
" child.style.zIndex = '1';\n" +
" }\n" +
" });\n" +
" }\n" +
" }\n" +
" \n" +
" function setupEventListeners(element, state, options) {\n" +
" // Mouse move event\n" +
" element.addEventListener('mousemove', (e) => {\n" +
" const rect = element.getBoundingClientRect();\n" +
" state.mouseX = (e.clientX - rect.left) / rect.width;\n" +
" state.mouseY = (e.clientY - rect.top) / rect.height;\n" +
" state.isMouseInside = true;\n" +
" });\n" +
" \n" +
" // Mouse leave event\n" +
" element.addEventListener('mouseleave', () => {\n" +
" state.isMouseInside = false;\n" +
" state.targetX = Math.random();\n" +
" state.targetY = Math.random();\n" +
" });\n" +
" \n" +
" // Mouse enter event\n" +
" element.addEventListener('mouseenter', () => {\n" +
" state.isMouseInside = true;\n" +
" });\n" +
" \n" +
" // Touch events for mobile\n" +
" element.addEventListener('touchmove', (e) => {\n" +
" e.preventDefault();\n" +
" const touch = e.touches[0];\n" +
" const rect = element.getBoundingClientRect();\n" +
" state.mouseX = (touch.clientX - rect.left) / rect.width;\n" +
" state.mouseY = (touch.clientY - rect.top) / rect.height;\n" +
" state.isMouseInside = true;\n" +
" }, { passive: false });\n" +
" \n" +
" element.addEventListener('touchend', () => {\n" +
" state.isMouseInside = false;\n" +
" state.targetX = Math.random();\n" +
" state.targetY = Math.random();\n" +
" });\n" +
" \n" +
" // Focus events for accessibility\n" +
" element.addEventListener('focus', () => {\n" +
" state.isMouseInside = true;\n" +
" });\n" +
" \n" +
" element.addEventListener('blur', () => {\n" +
" state.isMouseInside = false;\n" +
" state.targetX = Math.random();\n" +
" state.targetY = Math.random();\n" +
" });\n" +
" }\n" +
" \n" +
" function startAnimation(element, state, options) {\n" +
" let animationFrameId;\n" +
" let autoAnimateTime = 0;\n" +
" \n" +
" function updateGradient() {\n" +
" // Handle auto animation mode\n" +
" if (state.isAutoAnimate) {\n" +
" autoAnimateTime += options.autoSpeed;\n" +
" state.mouseX = (Math.sin(autoAnimateTime) + 1) / 2;\n" +
" state.mouseY = (Math.cos(autoAnimateTime * 0.8) + 1) / 2;\n" +
" } \n" +
" // Handle mouse-based animation\n" +
" else if (!state.isMouseInside) {\n" +
" state.mouseX += (state.targetX - state.mouseX) * options.idleSpeed;\n" +
" state.mouseY += (state.targetY - state.mouseY) * options.idleSpeed;\n" +
" }\n" +
" \n" +
" // Apply mouse influence\n" +
" const effectiveX = state.mouseX * options.mouseInfluence + 0.5 * (1 - options.mouseInfluence);\n" +
" const effectiveY = state.mouseY * options.mouseInfluence + 0.5 * (1 - options.mouseInfluence);\n" +
" \n" +
" // Update background position\n" +
" const posX = effectiveX * 100;\n" +
" const posY = effectiveY * 100;\n" +
" element.gradientElement.style.backgroundPosition = posX + '% ' + posY + '%';\n" +
" }\n" +
" \n" +
" // Animation loop\n" +
" function smoothUpdate() {\n" +
" updateGradient();\n" +
" animationFrameId = requestAnimationFrame(smoothUpdate);\n" +
" }\n" +
" \n" +
" // Start animation\n" +
" smoothUpdate();\n" +
" \n" +
" // Add intersection observer to pause animation when not visible\n" +
" const observer = new IntersectionObserver((entries) => {\n" +
" entries.forEach(entry => {\n" +
" if (entry.isIntersecting) {\n" +
" // Resume animation when visible\n" +
" if (!animationFrameId) {\n" +
" smoothUpdate();\n" +
" }\n" +
" } else {\n" +
" // Pause animation when not visible\n" +
" if (animationFrameId) {\n" +
" cancelAnimationFrame(animationFrameId);\n" +
" animationFrameId = null;\n" +
" }\n" +
" }\n" +
" });\n" +
" }, { threshold: 0 });\n" +
" \n" +
" observer.observe(element);\n" +
" \n" +
" // Store cleanup function\n" +
" element._cleanupGradient = () => {\n" +
" if (animationFrameId) {\n" +
" cancelAnimationFrame(animationFrameId);\n" +
" }\n" +
" observer.disconnect();\n" +
" if (element.gradientElement && element.gradientElement.parentNode) {\n" +
" element.gradientElement.parentNode.removeChild(element.gradientElement);\n" +
" }\n" +
" element.dataset.ambientInitialized = 'false';\n" +
" };\n" +
" }\n" +
" \n" +
" // Initialize on DOM content loaded\n" +
" if (document.readyState === 'loading') {\n" +
" document.addEventListener('DOMContentLoaded', initAmbientGradient);\n" +
" } else {\n" +
" initAmbientGradient();\n" +
" }\n" +
" \n" +
" // Initialize when new content is loaded in Bricks Builder\n" +
" document.addEventListener('bricks-lazy-load', initAmbientGradient);\n" +
"})();\n" +
"\n" +
"/* \n" +
"To use this effect in Bricks Builder:\n" +
"1. Add this script to a Code element\n" +
"2. Add the attribute data-ambient-gradient=\"true\" to any section where you want the effect to be applied\n" +
"3. Optionally add data-ambient-gradient-auto=\"true\" for automatic animation without mouse interaction\n" +
"*/";
}
function downloadJsFile() {
const jsCode = generateJavaScriptCode();
const blob = new Blob([jsCode], { type: 'application/javascript' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'ambient-gradient.js';
a.click();
URL.revokeObjectURL(url);
}
function initializeUI() {
// Initialize the preview
initAmbientGradient();
// Setup color inputs
const colorInputs = document.querySelectorAll('input[type="color"]');
colorInputs.forEach(input => {
const textInput = document.getElementById(`${input.id}-text`);
// When color picker changes
input.addEventListener('input', () => {
textInput.value = input.value;
updateColorConfiguration();
});
// When text input changes
if (textInput) {
textInput.addEventListener('input', () => {
const isValidHex = /^#[0-9A-F]{6}$/i.test(textInput.value);
if (isValidHex) {
input.value = textInput.value;
updateColorConfiguration();
}
});
textInput.addEventListener('blur', () => {
const isValidHex = /^#[0-9A-F]{6}$/i.test(textInput.value);
if (!isValidHex) {
textInput.value = input.value;
}
});
}
});
// Setup range inputs
const rangeInputs = document.querySelectorAll('input[type="range"]');
rangeInputs.forEach(input => {
const valueElement = document.getElementById(`${input.id}-value`);
// Set initial value
if (valueElement) {
valueElement.textContent = input.value;
}
input.addEventListener('input', () => {
if (valueElement) {
valueElement.textContent = input.value;
}
// Update configuration based on the slider
switch (input.id) {
case 'gradient-angle':
gradientConfig.angle = parseFloat(input.value);
break;
case 'bg-size':
gradientConfig.backgroundSize = parseFloat(input.value);
break;
case 'auto-speed':
gradientConfig.autoSpeed = parseFloat(input.value);
break;
case 'mouse-influence':
gradientConfig.mouseInfluence = parseFloat(input.value);
break;
case 'idle-speed':
gradientConfig.idleSpeed = parseFloat(input.value);
break;
case 'transition-smoothness':
gradientConfig.transitionSmoothness = parseFloat(input.value);
break;
case 'z-index':
gradientConfig.zIndex = parseInt(input.value);
break;
}
// Update the preview
updateGradientPreview();
});
});
// Setup toggle switches
document.getElementById('auto-animate').addEventListener('change', function() {
gradientConfig.autoAnimate = this.checked;
// Toggle visibility of animation settings
document.getElementById('auto-animation-settings').style.display =
this.checked ? 'block' : 'none';
document.getElementById('mouse-animation-settings').style.display =
this.checked ? 'none' : 'block';
// Update the preview
updateGradientPreview();
});
document.getElementById('center-content').addEventListener('change', function() {
gradientConfig.centerContent = this.checked;
updateGradientPreview();
});
document.getElementById('border-radius-inherit').addEventListener('change', function() {
gradientConfig.borderRadiusInherit = this.checked;
updateGradientPreview();
});
// Setup initial visibility of animation settings
document.getElementById('auto-animation-settings').style.display =
document.getElementById('auto-animate').checked ? 'block' : 'none';
document.getElementById('mouse-animation-settings').style.display =
document.getElementById('auto-animate').checked ? 'none' : 'block';
// Setup modal and download functionality
const downloadBtn = document.getElementById('download-btn');
const codeModal = document.getElementById('code-modal');
const closeModal = document.getElementById('close-modal');
const copyBtn = document.getElementById('copy-btn');
const downloadFileBtn = document.getElementById('download-file-btn');
if (downloadBtn) {
downloadBtn.addEventListener('click', () => {
const jsCode = generateJavaScriptCode();
document.getElementById('js-code').textContent = jsCode;
codeModal.classList.add('active');
});
}
if (closeModal) {
closeModal.addEventListener('click', () => {
codeModal.classList.remove('active');
});
}
if (copyBtn) {
copyBtn.addEventListener('click', () => {
const jsCode = document.getElementById('js-code').textContent;
copyAttributeToClipboard(jsCode, null);
});
}
if (downloadFileBtn) {
downloadFileBtn.addEventListener('click', downloadJsFile);
}
if (codeModal) {
codeModal.addEventListener('click', (e) => {
if (e.target === codeModal) {
codeModal.classList.remove('active');
}
});
}
// Inicializar los event listeners para los elementos copiables
initCopyAttributeListeners();
}
// Nueva función para inicializar los event listeners de los elementos copiables
function initCopyAttributeListeners() {
// Quitamos los console.log
const copyAttributes = document.querySelectorAll('.copy-attribute');
copyAttributes.forEach(el => {
el.addEventListener('click', function(e) {
e.preventDefault();
const attribute = this.getAttribute('data-attribute');
// También quitar este console.log si existe:
// console.log("Copy attribute clicked: " + attribute);
copyAttributeToClipboard(attribute, this);
});
});
}
function updateColorConfiguration() {
// Update the colors in the configuration
const colors = [];
for (let i = 1; i <= 4; i++) {
colors.push(document.getElementById(`color${i}`).value);
}
gradientConfig.colors = colors;
// Update the preview
updateGradientPreview();
}
// Función para copiar atributos al portapapeles
function copyAttributeToClipboard(attribute, element) {
// Solo limpiar el atributo si es un atributo HTML y no el código JavaScript completo
let textToCopy = attribute;
// Si es un atributo HTML (comienza con "data-") y tiene element, extraer solo el nombre del atributo
if (element && typeof attribute === 'string' && attribute.startsWith('data-')) {
textToCopy = attribute.split('=')[0];
}
navigator.clipboard.writeText(textToCopy)
.then(() => {
// Añadir animación directamente sobre el elemento copiado
if (element) {
// Guardar el texto original
const originalText = element.innerHTML;
// Crear un span para el mensaje de "Copied!"
const copiedSpan = document.createElement('span');
copiedSpan.textContent = "Copied!";
copiedSpan.style.cssText = 'position:absolute; top:0; left:0; width:100%; height:100%; display:flex; align-items:center; justify-content:center; background-color:rgba(239, 96, 19, 0.9); color:white; border-radius:4px; font-weight:bold; animation: fadeInOut 1.5s ease forwards;';
// Añadir estilos de animación
const style = document.createElement('style');
style.textContent = `
@keyframes fadeInOut {
0% { opacity: 0; }
10% { opacity: 1; }
80% { opacity: 1; }
100% { opacity: 0; }
}
`;
document.head.appendChild(style);
// Asegurar que el elemento tenga posición relativa para el posicionamiento absoluto
element.style.position = 'relative';
element.style.overflow = 'hidden';
// Añadir el span al elemento
element.appendChild(copiedSpan);
// Eliminar el span después de que termine la animación
setTimeout(() => {
element.removeChild(copiedSpan);
document.head.removeChild(style);
}, 1500);
}
// Animación mejorada de feedback visual adicional (tooltip)
const tooltip = document.createElement('div');
tooltip.textContent = 'Copied to clipboard!';
tooltip.style.cssText = 'position:fixed;padding:8px 16px;background:#333;color:white;border-radius:6px;font-size:14px;z-index:9999;transition:all 0.3s ease;opacity:0;transform:translateY(10px);box-shadow:0 4px 12px rgba(0,0,0,0.2);';
document.body.appendChild(tooltip);
// Position near cursor
tooltip.style.top = (event.clientY + 20) + 'px';
tooltip.style.left = (event.clientX + 10) + 'px';
// Show with animation
setTimeout(() => {
tooltip.style.opacity = '1';
tooltip.style.transform = 'translateY(0)';
// Hide with animation
setTimeout(() => {
tooltip.style.opacity = '0';
tooltip.style.transform = 'translateY(-10px)';
// Remove from DOM
setTimeout(() => {
document.body.removeChild(tooltip);
}, 300);
}, 1500);
}, 10);
})
.catch(err => {
console.error('Error copying: ', err);
});
}
// Initialize the UI
initializeUI();
});
</script>
</body>
</html>