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>Word Rotate 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: linear-gradient(135deg, #333 0%, #222 100%);
display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
text-align: center;
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);
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);
}
.preview-btn svg {
width: 18px;
height: 18px;
stroke: currentColor;
}
.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: var(--card-bg);
display: flex;
align-items: center;
justify-content: center;
--selected-color: #ef6013;
}
.color-picker-container:hover {
border-color: var(--accent);
transform: scale(1.05);
box-shadow: 0 0 12px rgba(239, 96, 19, 0.3);
}
.color-picker-container::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: var(--selected-color, #ef6013);
border-radius: 6px;
transition: var(--transition);
}
input[type="color"] {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: none;
cursor: pointer;
background: transparent;
opacity: 0;
z-index: 2;
}
.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;
}
select {
width: 100%;
padding: 0.75rem;
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: var(--card-bg);
margin-bottom: 0.75rem;
outline: none;
transition: var(--transition);
}
select:focus {
border-color: var(--accent);
box-shadow: 0 0 0 2px rgba(239, 96, 19, 0.2);
}
input[type="text"],
input[type="number"],
textarea {
width: 100%;
padding: 0.75rem;
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: var(--card-bg);
margin-bottom: 0.75rem;
outline: none;
transition: var(--transition);
}
input[type="text"]:focus,
input[type="number"]:focus,
textarea:focus {
border-color: var(--accent);
box-shadow: 0 0 0 2px rgba(239, 96, 19, 0.2);
}
textarea {
min-height: 100px;
resize: vertical;
}
.tag-area {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-bottom: 1rem;
}
.text-tag {
background-color: rgba(50, 50, 50, 0.5);
color: var(--text-primary);
padding: 0.4rem 0.8rem;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: space-between;
font-size: var(--text-xs);
border: 1px solid var(--border);
max-width: 100%;
word-break: break-all;
}
.remove-tag {
margin-left: 0.5rem;
color: var(--text-secondary);
cursor: pointer;
background: none;
border: none;
font-size: 1.2em;
display: inline-flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
border-radius: 50%;
transition: var(--transition);
}
.remove-tag:hover {
color: white;
background-color: rgba(255, 255, 255, 0.1);
}
.toggle-switch {
position: relative;
display: inline-block;
width: 46px;
height: 24px;
margin-right: 10px;
}
.toggle-switch input {
opacity: 0;
width: 0;
height: 0;
}
.toggle-slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(120, 120, 128, 0.32);
transition: all 0.3s ease;
border-radius: 30px;
}
.toggle-slider:before {
position: absolute;
content: "";
height: 20px;
width: 20px;
left: 2px;
bottom: 2px;
background-color: white;
transition: all 0.3s ease;
border-radius: 50%;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
input:checked + .toggle-slider {
background-color: var(--accent);
}
input:checked + .toggle-slider:before {
transform: translateX(22px);
}
.toggle-label {
font-size: var(--text-xs);
color: var(--text-secondary);
cursor: pointer;
}
.toggle-container {
display: flex;
align-items: center;
margin-bottom: 15px;
}
.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); }
}
/* CSS básico para estructura - las transiciones las maneja JavaScript */
.word-rotate-wrapper {
display: inline-block;
position: relative;
overflow: hidden;
vertical-align: top;
}
.word-rotate-item {
display: block;
position: absolute;
left: 0;
top: 0;
white-space: nowrap;
opacity: 0;
width: 100%;
}
.word-rotate-item.active {
position: relative;
opacity: 1;
}
.preview-text {
font-size: calc(var(--text-s) * 1.5);
font-weight: 600;
color: var(--text-primary);
text-align: center;
}
</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/animated-text/" class="breadcrumb-item">Animated text</a>
<span class="breadcrumb-separator">›</span>
<span class="breadcrumb-item active">Word Rotate</span>
</nav>
<div class="action-buttons">
<div class="data-attribute-display" id="quick-attribute" title="Click to copy data attribute">
data-word-rotate
</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">Word Rotate</h1>
<p class="page-subtitle">Interactive text 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 word rotate 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 the JavaScript code</li>
<li>To add the effect to any text element: go to <strong>Section → Style → Attributes</strong>, add <code>data-word-rotate</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="word-rotate-preview">
<div class="preview-text">
We create <span data-word-rotate>amazing</span> experiences
</div>
<div class="preview-controls">
<button class="preview-btn" id="randomize-word-rotate" 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="#333333" title="Change Preview Background (B)">
</div>
</div>
</div>
</section>
<section class="controls-section">
<div class="card">
<div class="card-heading">
Word Settings
<div class="card-actions">
<button class="card-action-btn" id="reset-words" title="Reset Words">↺</button>
</div>
</div>
<div class="card-content">
<div class="control-group">
<div class="control-label">
<span class="label-text">Rotating Words</span>
</div>
<div class="tag-area" id="words-container">
<div class="text-tag">
amazing
<button class="remove-tag">×</button>
</div>
<div class="text-tag">
stunning
<button class="remove-tag">×</button>
</div>
<div class="text-tag">
powerful
<button class="remove-tag">×</button>
</div>
<div class="text-tag">
beautiful
<button class="remove-tag">×</button>
</div>
</div>
<input type="text" id="new-word" placeholder="Add new word...">
<p style="font-size: 13px; color: var(--text-secondary); margin-top: 5px;">
Enter words to rotate through in your animation
</p>
</div>
</div>
</div>
<div class="card">
<div class="card-heading">
Text Color
<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="text-color" value="#ef6013">
</div>
<div class="color-input-group">
<span class="color-label">HEX</span>
<input type="text" class="color-input hex-input" id="text-color-hex" value="#ef6013" placeholder="#FFFFFF">
</div>
<div class="color-input-group">
<span class="color-label">HSL</span>
<input type="text" class="color-input hsl-input" id="text-color-hsl" placeholder="hsl(0, 100%, 50%)">
</div>
</div>
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">Use Gradient</span>
</div>
<div class="toggle-container">
<label class="toggle-switch">
<input type="checkbox" id="use-gradient">
<span class="toggle-slider"></span>
</label>
<span class="toggle-label" for="use-gradient">
Apply a gradient effect to the rotating words
</span>
</div>
</div>
<div class="color-list" id="gradient-end-group" style="display: none;">
<div class="color-row">
<div class="color-picker-container">
<input type="color" id="gradient-end-color" value="#ff8c51">
</div>
<div class="color-input-group">
<span class="color-label">HEX</span>
<input type="text" class="color-input hex-input" id="gradient-end-color-hex" value="#ff8c51" placeholder="#FFFFFF">
</div>
<div class="color-input-group">
<span class="color-label">HSL</span>
<input type="text" class="color-input hsl-input" id="gradient-end-color-hsl" placeholder="hsl(0, 100%, 50%)">
</div>
</div>
</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">
Display Duration
<span class="help-tooltip" title="How long each word is displayed before rotating">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="duration-value">2500</span>ms</span>
<button class="reset-btn" onclick="resetParameter('duration', 2500)">↺</button>
</div>
</div>
<input type="range" id="duration" min="1000" max="5000" step="250" value="2500">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">
Animation Speed
<span class="help-tooltip" title="Speed of the transition between words">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="speed-value">350</span>ms</span>
<button class="reset-btn" onclick="resetParameter('speed', 350)">↺</button>
</div>
</div>
<input type="range" id="speed" min="150" max="1000" step="50" value="350">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">Animation Type</span>
</div>
<select id="animation-type">
<option value="fade" selected>Fade</option>
<option value="slide-up">Slide Up</option>
<option value="slide-down">Slide Down</option>
<option value="slide-left">Slide Left</option>
<option value="slide-right">Slide Right</option>
<option value="zoom">Zoom</option>
<option value="flip">3D Flip</option>
</select>
</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="control-group">
<div class="control-label">
<span class="label-text">Pause on Hover</span>
</div>
<div class="toggle-container">
<label class="toggle-switch">
<input type="checkbox" id="pause-on-hover">
<span class="toggle-slider"></span>
</label>
<span class="toggle-label" for="pause-on-hover">
Pause rotation when hovering over the element
</span>
</div>
</div>
</div>
</div>
</section>
</div>
</div>
<div class="notification" id="notification"></div>
<script>
document.addEventListener('DOMContentLoaded', function() {
let wordRotateConfig = {
words: ['amazing', 'stunning', 'powerful', 'beautiful'],
duration: 2500,
speed: 350,
type: 'fade',
textColor: '#ef6013',
useGradient: false,
gradientEndColor: '#ff8c51',
pauseOnHover: false
};
const defaultConfig = { ...wordRotateConfig };
let activeWordRotate = null;
function initWordRotate() {
const elements = document.querySelectorAll('[data-word-rotate]:not([data-word-rotate-initialized="true"])');
elements.forEach((element) => {
const words = element.hasAttribute('data-word-rotate-words')
? element.getAttribute('data-word-rotate-words').split(',').map(word => word.trim())
: wordRotateConfig.words;
const duration = element.hasAttribute('data-word-rotate-duration')
? parseInt(element.getAttribute('data-word-rotate-duration'))
: wordRotateConfig.duration;
const speed = element.hasAttribute('data-word-rotate-speed')
? parseInt(element.getAttribute('data-word-rotate-speed'))
: wordRotateConfig.speed;
const type = element.hasAttribute('data-word-rotate-type')
? element.getAttribute('data-word-rotate-type')
: wordRotateConfig.type;
const textColor = element.hasAttribute('data-word-rotate-color')
? element.getAttribute('data-word-rotate-color')
: wordRotateConfig.textColor;
const useGradient = element.hasAttribute('data-word-rotate-gradient')
? element.getAttribute('data-word-rotate-gradient') === 'true'
: wordRotateConfig.useGradient;
const gradientEndColor = element.hasAttribute('data-word-rotate-gradient-end')
? element.getAttribute('data-word-rotate-gradient-end')
: wordRotateConfig.gradientEndColor;
const pauseOnHover = element.hasAttribute('data-word-rotate-pause-hover')
? element.getAttribute('data-word-rotate-pause-hover') === 'true'
: wordRotateConfig.pauseOnHover;
const options = {
words,
duration,
speed,
type,
textColor,
useGradient,
gradientEndColor,
pauseOnHover
};
setupWordRotate(element, options);
element.dataset.wordRotateInitialized = 'true';
if (element.closest('#word-rotate-preview')) {
activeWordRotate = { element, options };
wordRotateConfig = {
words: options.words,
duration: options.duration,
speed: options.speed,
type: options.type,
textColor: options.textColor,
useGradient: options.useGradient,
gradientEndColor: options.gradientEndColor,
pauseOnHover: options.pauseOnHover
};
}
});
}
function setupWordRotate(element, options) {
if (element._wordRotateInstance) {
element._wordRotateInstance.destroy();
}
element._wordRotateInstance = new WordRotate(element, options);
}
function updateWordRotatePreview() {
const preview = document.querySelector('#word-rotate-preview [data-word-rotate]');
if (!preview) return;
if (preview._wordRotateInstance) {
preview._wordRotateInstance.updateConfig(wordRotateConfig);
} else {
setupWordRotate(preview, wordRotateConfig);
}
}
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 generateUniqueId() {
return Math.random().toString(36).substring(2, 8);
}
function generateFullSectionJSON() {
// Generar IDs únicos para todos los elementos
const sectionId = generateUniqueId();
const containerId = generateUniqueId();
const divId = generateUniqueId();
const textBasicId1 = generateUniqueId();
const divRotateId = generateUniqueId();
const textBasicId2 = generateUniqueId();
const codeId = generateUniqueId();
const attributeId = generateUniqueId();
// Obtener el JavaScript actual con la configuración del usuario
const jsCode = generateJavaScriptCode();
// Crear el objeto JSON completo de Bricks Builder
const bricksJSON = {
"content": [
{
"id": sectionId,
"name": "section",
"parent": 0,
"children": [containerId, codeId],
"settings": {
"_justifyContent": "center",
"_height": "500"
}
},
{
"id": containerId,
"name": "container",
"parent": sectionId,
"children": [divId],
"settings": {
"_alignSelf": "center",
"_justifyContent": "center",
"_alignItems": "center",
"_direction": "row"
}
},
{
"id": divId,
"name": "div",
"parent": containerId,
"children": [textBasicId1, divRotateId, textBasicId2],
"settings": {
"_display": "flex",
"_alignSelf": "center",
"_justifyContent": "center",
"_alignItems": "center",
"_width": "100%",
"_height": "200",
"_columnGap": "10"
}
},
{
"id": textBasicId1,
"name": "text-basic",
"parent": divId,
"children": [],
"settings": {
"text": "We create",
"tag": "p",
"_typography": {
"color": {
"hex": "#000000"
},
"font-size": "20"
}
}
},
{
"id": divRotateId,
"name": "div",
"parent": divId,
"children": [],
"settings": {
"_attributes": [
{
"id": attributeId,
"name": "data-word-rotate"
}
]
}
},
{
"id": textBasicId2,
"name": "text-basic",
"parent": divId,
"children": [],
"settings": {
"text": "experience",
"tag": "p",
"_typography": {
"color": {
"hex": "#000000"
},
"font-size": "20"
}
}
},
{
"id": codeId,
"name": "code",
"parent": sectionId,
"children": [],
"settings": {
"javascriptCode": jsCode,
"executeCode": true,
"_display": "none"
},
"label": "Word Rotate JS"
}
],
"source": "bricksCopiedElements",
"sourceUrl": "https://test.bricksfusion.com",
"version": "2.0.1",
"globalClasses": [],
"globalElements": []
};
return JSON.stringify(bricksJSON, null, 2);
}
function generateJavaScriptCode() {
return `(function() {
class WordRotate {
constructor(element, config = {}) {
this.element = element;
this.originalText = element.innerText;
this.config = {
words: config.words || ['${wordRotateConfig.words.join("', '")}'],
duration: config.duration || ${wordRotateConfig.duration},
speed: config.speed || ${wordRotateConfig.speed},
type: config.type || '${wordRotateConfig.type}',
textColor: config.textColor || '${wordRotateConfig.textColor}',
useGradient: config.useGradient !== undefined ? config.useGradient : ${wordRotateConfig.useGradient},
gradientEndColor: config.gradientEndColor || '${wordRotateConfig.gradientEndColor}',
pauseOnHover: config.pauseOnHover !== undefined ? config.pauseOnHover : ${wordRotateConfig.pauseOnHover}
};
if (this.config.words.length <= 1) return;
this.setupStyles();
this.createStructure();
this.startRotation();
if (this.config.pauseOnHover) {
this.setupHoverEvents();
}
}
setupStyles() {
const styleId = 'word-rotate-styles';
if (document.getElementById(styleId)) {
document.getElementById(styleId).remove();
}
const style = document.createElement('style');
style.id = styleId;
const baseTransition = \`\${this.config.speed}ms cubic-bezier(0.45, 0, 0.15, 1)\`;
style.textContent = \`
.word-rotate-wrapper {
display: inline-block !important;
position: relative !important;
overflow: hidden !important;
vertical-align: top !important;
}
.word-rotate-item {
display: block !important;
position: absolute !important;
left: 0 !important;
top: 0 !important;
white-space: nowrap !important;
opacity: 0 !important;
width: 100% !important;
}
.word-rotate-item.active {
position: relative !important;
opacity: 1 !important;
}
body .word-rotate-fade .word-rotate-item {
transform: translateY(0) !important;
transition: opacity \${baseTransition} !important;
}
body .word-rotate-slide-up .word-rotate-item:not(.active) {
transform: translateY(30px) !important;
transition: transform \${baseTransition}, opacity \${baseTransition} !important;
}
body .word-rotate-slide-up .word-rotate-item.active {
transform: translateY(0) !important;
transition: transform \${baseTransition}, opacity \${baseTransition} !important;
}
body .word-rotate-slide-up .word-rotate-item.previous {
transform: translateY(-30px) !important;
transition: transform \${baseTransition}, opacity \${baseTransition} !important;
}
body .word-rotate-slide-down .word-rotate-item:not(.active) {
transform: translateY(-30px) !important;
transition: transform \${baseTransition}, opacity \${baseTransition} !important;
}
body .word-rotate-slide-down .word-rotate-item.active {
transform: translateY(0) !important;
transition: transform \${baseTransition}, opacity \${baseTransition} !important;
}
body .word-rotate-slide-down .word-rotate-item.previous {
transform: translateY(30px) !important;
transition: transform \${baseTransition}, opacity \${baseTransition} !important;
}
body .word-rotate-slide-left .word-rotate-item:not(.active) {
transform: translateX(30px) !important;
transition: transform \${baseTransition}, opacity \${baseTransition} !important;
}
body .word-rotate-slide-left .word-rotate-item.active,
body .word-rotate-slide-right .word-rotate-item.active {
transform: translateX(0) !important;
transition: transform \${baseTransition}, opacity \${baseTransition} !important;
}
body .word-rotate-slide-left .word-rotate-item.previous {
transform: translateX(-30px) !important;
transition: transform \${baseTransition}, opacity \${baseTransition} !important;
}
body .word-rotate-slide-right .word-rotate-item:not(.active) {
transform: translateX(-30px) !important;
transition: transform \${baseTransition}, opacity \${baseTransition} !important;
}
body .word-rotate-slide-right .word-rotate-item.previous {
transform: translateX(30px) !important;
transition: transform \${baseTransition}, opacity \${baseTransition} !important;
}
body .word-rotate-zoom .word-rotate-item:not(.active) {
transform: scale(0.8) !important;
transition: transform \${baseTransition}, opacity \${baseTransition} !important;
}
body .word-rotate-zoom .word-rotate-item.active {
transform: scale(1) !important;
transition: transform \${baseTransition}, opacity \${baseTransition} !important;
}
body .word-rotate-zoom .word-rotate-item.previous {
transform: scale(1.2) !important;
transition: transform \${baseTransition}, opacity \${baseTransition} !important;
}
body .word-rotate-flip .word-rotate-item:not(.active) {
transform: rotateX(90deg) !important;
transition: transform \${baseTransition}, opacity \${baseTransition} !important;
}
body .word-rotate-flip .word-rotate-item.active {
transform: rotateX(0deg) !important;
transition: transform \${baseTransition}, opacity \${baseTransition} !important;
}
body .word-rotate-flip .word-rotate-item.previous {
transform: rotateX(-90deg) !important;
transition: transform \${baseTransition}, opacity \${baseTransition} !important;
}
\`;
document.head.appendChild(style);
}
createStructure() {
this.wrapper = document.createElement('span');
this.wrapper.className = \`word-rotate-wrapper word-rotate-\${this.config.type}\`;
this.config.words.forEach((word, index) => {
const wordElement = document.createElement('span');
wordElement.className = \`word-rotate-item \${index === 0 ? 'active' : ''}\`;
wordElement.innerHTML = word;
this.applyStyles(wordElement);
this.wrapper.appendChild(wordElement);
});
this.element.innerHTML = '';
this.element.appendChild(this.wrapper);
// Esperar un frame para asegurar que el elemento está en el DOM
requestAnimationFrame(() => {
this.setWrapperDimensions();
});
this.resizeHandler = this.setWrapperDimensions.bind(this);
window.addEventListener('resize', this.resizeHandler);
}
applyStyles(element) {
if (this.config.useGradient) {
element.style.color = 'transparent';
element.style.backgroundImage = \`linear-gradient(90deg, \${this.config.textColor}, \${this.config.gradientEndColor})\`;
element.style.backgroundSize = 'auto';
element.style.backgroundPosition = '0 0';
element.style.backgroundRepeat = 'no-repeat';
element.style.webkitBackgroundClip = 'text';
element.style.backgroundClip = 'text';
element.style.webkitTextFillColor = 'transparent';
} else {
element.style.color = this.config.textColor;
element.style.background = 'none';
element.style.webkitBackgroundClip = '';
element.style.backgroundClip = '';
element.style.webkitTextFillColor = '';
}
}
setWrapperDimensions() {
if (!this.wrapper || !this.wrapper.parentNode) return;
let maxWidth = 0;
let maxHeight = 0;
const wordElements = this.wrapper.querySelectorAll('.word-rotate-item');
wordElements.forEach(element => {
const prevOpacity = element.style.opacity;
const prevPosition = element.style.position;
element.style.opacity = '1';
element.style.position = 'static';
const rect = element.getBoundingClientRect();
maxWidth = Math.max(maxWidth, rect.width);
maxHeight = Math.max(maxHeight, rect.height);
element.style.opacity = prevOpacity;
element.style.position = prevPosition;
});
this.wrapper.style.minWidth = \`\${maxWidth}px\`;
this.wrapper.style.height = \`\${maxHeight}px\`;
}
startRotation() {
this.interval = setInterval(() => {
const items = this.wrapper.querySelectorAll('.word-rotate-item');
const current = this.wrapper.querySelector('.word-rotate-item.active');
const currentIndex = Array.from(items).indexOf(current);
current.classList.remove('active');
current.classList.add('previous');
setTimeout(() => {
current.classList.remove('previous');
}, this.config.speed);
const nextIndex = (currentIndex + 1) % items.length;
items[nextIndex].classList.add('active');
}, this.config.duration);
}
setupHoverEvents() {
this.element.addEventListener('mouseenter', () => {
if (this.interval) {
clearInterval(this.interval);
this.interval = null;
}
});
this.element.addEventListener('mouseleave', () => {
if (!this.interval) {
this.startRotation();
}
});
}
updateConfig(newConfig) {
Object.assign(this.config, newConfig);
this.setupStyles();
const wordElements = this.wrapper.querySelectorAll('.word-rotate-item');
wordElements.forEach(element => {
this.applyStyles(element);
});
if (this.interval) {
clearInterval(this.interval);
this.startRotation();
}
}
destroy() {
if (this.interval) {
clearInterval(this.interval);
}
this.element.innerHTML = this.originalText;
window.removeEventListener('resize', this.resizeHandler);
}
}
const initWordRotate = () => {
document.querySelectorAll('[data-word-rotate]').forEach(element => {
if (!element._wordRotate) {
const words = element.getAttribute('data-word-rotate-words')?.split(',').map(w => w.trim()) || ['${wordRotateConfig.words.join("', '")}'];
const duration = parseInt(element.getAttribute('data-word-rotate-duration')) || ${wordRotateConfig.duration};
const speed = parseInt(element.getAttribute('data-word-rotate-speed')) || ${wordRotateConfig.speed};
const type = element.getAttribute('data-word-rotate-type') || '${wordRotateConfig.type}';
const textColor = element.getAttribute('data-word-rotate-color') || '${wordRotateConfig.textColor}';
const useGradient = element.hasAttribute('data-word-rotate-gradient') ? element.getAttribute('data-word-rotate-gradient') === 'true' : ${wordRotateConfig.useGradient};
const gradientEndColor = element.getAttribute('data-word-rotate-gradient-end') || '${wordRotateConfig.gradientEndColor}';
const pauseOnHover = element.hasAttribute('data-word-rotate-pause-hover') ? element.getAttribute('data-word-rotate-pause-hover') === 'true' : ${wordRotateConfig.pauseOnHover};
element._wordRotate = new WordRotate(element, {
words,
duration,
speed,
type,
textColor,
useGradient,
gradientEndColor,
pauseOnHover
});
}
});
};
// Múltiples puntos de inicialización para asegurar compatibilidad con Bricks
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
setTimeout(initWordRotate, 100);
});
} else {
setTimeout(initWordRotate, 100);
}
// Inicialización adicional para cambios de tamaño
window.addEventListener('resize', () => setTimeout(initWordRotate, 300));
// Compatibilidad específica con Bricks Builder
if (typeof window.bricksIsFrontend !== 'undefined') {
document.addEventListener('bricks/layout_rendered', () => {
setTimeout(initWordRotate, 500);
});
// Forzar re-inicialización para Bricks Builder
setTimeout(() => {
initWordRotate();
}, 1000);
// Observer para elementos dinámicos
const observer = new MutationObserver((mutations) => {
let shouldInit = false;
mutations.forEach((mutation) => {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === 1 && (
node.hasAttribute?.('data-word-rotate') ||
node.querySelector?.('[data-word-rotate]')
)) {
shouldInit = true;
}
});
}
});
if (shouldInit) {
setTimeout(initWordRotate, 200);
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}
})();`;
}
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 => {
showNotification('Feature coming soon!', 'warning');
});
}
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 'duration':
wordRotateConfig.duration = defaultValue;
break;
case 'speed':
wordRotateConfig.speed = defaultValue;
break;
}
updateWordRotatePreview();
showNotification(`${parameterId.replace(/-/g, ' ')} reset to default`);
}
};
function generateRandomWordRotate() {
const randomWords = ['awesome', 'incredible', 'fantastic', 'brilliant', 'exceptional', 'remarkable', 'outstanding', 'magnificent'];
const selectedWords = [];
for (let i = 0; i < 4; i++) {
const randomIndex = Math.floor(Math.random() * randomWords.length);
selectedWords.push(randomWords[randomIndex]);
randomWords.splice(randomIndex, 1);
}
wordRotateConfig.words = selectedWords;
wordRotateConfig.duration = Math.floor(Math.random() * 4000) + 1000;
wordRotateConfig.speed = Math.floor(Math.random() * 850) + 150;
wordRotateConfig.textColor = generateRandomColor();
wordRotateConfig.type = ['fade', 'slide-up', 'slide-down', 'slide-left', 'slide-right', 'zoom', 'flip'][Math.floor(Math.random() * 7)];
document.getElementById('duration').value = wordRotateConfig.duration;
document.getElementById('speed').value = wordRotateConfig.speed;
document.getElementById('text-color').value = wordRotateConfig.textColor;
document.getElementById('animation-type').value = wordRotateConfig.type;
document.getElementById('duration-value').textContent = wordRotateConfig.duration;
document.getElementById('speed-value').textContent = wordRotateConfig.speed;
updateWordsDisplay();
updateColorInputs();
updateWordRotatePreview();
showNotification('Random word rotation generated!');
}
function updateColorInputs() {
const colorInput = document.getElementById('text-color');
const hexInput = document.getElementById('text-color-hex');
const hslInput = document.getElementById('text-color-hsl');
if (colorInput && hexInput && hslInput) {
colorInput.value = wordRotateConfig.textColor;
hexInput.value = wordRotateConfig.textColor;
hslInput.value = `hsl(${hexToHsl(wordRotateConfig.textColor).h}, ${hexToHsl(wordRotateConfig.textColor).s}%, ${hexToHsl(wordRotateConfig.textColor).l}%)`;
hexInput.classList.remove('invalid');
hslInput.classList.remove('invalid');
const colorPickerContainer = colorInput.closest('.color-row').querySelector('.color-picker-container');
if (colorPickerContainer) {
colorPickerContainer.style.setProperty('--selected-color', wordRotateConfig.textColor);
}
}
if (wordRotateConfig.useGradient) {
const gradientColorInput = document.getElementById('gradient-end-color');
const gradientHexInput = document.getElementById('gradient-end-color-hex');
const gradientHslInput = document.getElementById('gradient-end-color-hsl');
if (gradientColorInput && gradientHexInput && gradientHslInput) {
gradientColorInput.value = wordRotateConfig.gradientEndColor;
gradientHexInput.value = wordRotateConfig.gradientEndColor;
gradientHslInput.value = `hsl(${hexToHsl(wordRotateConfig.gradientEndColor).h}, ${hexToHsl(wordRotateConfig.gradientEndColor).s}%, ${hexToHsl(wordRotateConfig.gradientEndColor).l}%)`;
const gradientColorPickerContainer = gradientColorInput.closest('.color-row').querySelector('.color-picker-container');
if (gradientColorPickerContainer) {
gradientColorPickerContainer.style.setProperty('--selected-color', wordRotateConfig.gradientEndColor);
}
}
}
}
function updateWordsDisplay() {
const container = document.getElementById('words-container');
container.innerHTML = '';
wordRotateConfig.words.forEach((word, index) => {
const tag = document.createElement('div');
tag.className = 'text-tag';
tag.innerHTML = `
${word}
<button class="remove-tag">×</button>
`;
container.appendChild(tag);
});
}
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 {
h: Math.round(h * 360),
s: Math.round(s * 100),
l: 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}%)`;
}
class WordRotate {
constructor(element, config) {
this.element = element;
this.config = config;
this.originalText = element.innerText;
this.words = config.words;
this.duration = config.duration;
this.animationSpeed = config.speed;
this.animationType = config.type;
if (this.words.length <= 1) return;
this.setupStyles();
this.createStructure();
this.startRotation();
if (config.pauseOnHover) {
this.element.addEventListener('mouseenter', () => {
if (this.interval) {
clearInterval(this.interval);
this.interval = null;
}
});
this.element.addEventListener('mouseleave', () => {
if (!this.interval) {
this.startRotation();
}
});
}
}
setupStyles() {
const styleId = 'word-rotate-preview-styles';
if (document.getElementById(styleId)) {
document.getElementById(styleId).remove();
}
const style = document.createElement('style');
style.id = styleId;
const baseTransition = `${this.config.speed}ms cubic-bezier(0.45, 0, 0.15, 1)`;
style.textContent = `
.word-rotate-wrapper {
display: inline-block;
position: relative;
overflow: hidden;
vertical-align: top;
}
.word-rotate-item {
display: block;
position: absolute;
left: 0;
top: 0;
white-space: nowrap;
opacity: 0;
width: 100%;
}
.word-rotate-item.active {
position: relative;
opacity: 1;
}
body .word-rotate-fade .word-rotate-item {
transform: translateY(0) !important;
transition: opacity ${baseTransition} !important;
}
body .word-rotate-slide-up .word-rotate-item:not(.active) {
transform: translateY(30px) !important;
transition: transform ${baseTransition}, opacity ${baseTransition} !important;
}
body .word-rotate-slide-up .word-rotate-item.active {
transform: translateY(0) !important;
transition: transform ${baseTransition}, opacity ${baseTransition} !important;
}
body .word-rotate-slide-up .word-rotate-item.previous {
transform: translateY(-30px) !important;
transition: transform ${baseTransition}, opacity ${baseTransition} !important;
}
body .word-rotate-slide-down .word-rotate-item:not(.active) {
transform: translateY(-30px) !important;
transition: transform ${baseTransition}, opacity ${baseTransition} !important;
}
body .word-rotate-slide-down .word-rotate-item.active {
transform: translateY(0) !important;
transition: transform ${baseTransition}, opacity ${baseTransition} !important;
}
body .word-rotate-slide-down .word-rotate-item.previous {
transform: translateY(30px) !important;
transition: transform ${baseTransition}, opacity ${baseTransition} !important;
}
body .word-rotate-slide-left .word-rotate-item:not(.active) {
transform: translateX(30px) !important;
transition: transform ${baseTransition}, opacity ${baseTransition} !important;
}
body .word-rotate-slide-left .word-rotate-item.active,
body .word-rotate-slide-right .word-rotate-item.active {
transform: translateX(0) !important;
transition: transform ${baseTransition}, opacity ${baseTransition} !important;
}
body .word-rotate-slide-left .word-rotate-item.previous {
transform: translateX(-30px) !important;
transition: transform ${baseTransition}, opacity ${baseTransition} !important;
}
body .word-rotate-slide-right .word-rotate-item:not(.active) {
transform: translateX(-30px) !important;
transition: transform ${baseTransition}, opacity ${baseTransition} !important;
}
body .word-rotate-slide-right .word-rotate-item.previous {
transform: translateX(30px) !important;
transition: transform ${baseTransition}, opacity ${baseTransition} !important;
}
body .word-rotate-zoom .word-rotate-item:not(.active) {
transform: scale(0.8) !important;
transition: transform ${baseTransition}, opacity ${baseTransition} !important;
}
body .word-rotate-zoom .word-rotate-item.active {
transform: scale(1) !important;
transition: transform ${baseTransition}, opacity ${baseTransition} !important;
}
body .word-rotate-zoom .word-rotate-item.previous {
transform: scale(1.2) !important;
transition: transform ${baseTransition}, opacity ${baseTransition} !important;
}
body .word-rotate-flip .word-rotate-item:not(.active) {
transform: rotateX(90deg) !important;
transition: transform ${baseTransition}, opacity ${baseTransition} !important;
}
body .word-rotate-flip .word-rotate-item.active {
transform: rotateX(0deg) !important;
transition: transform ${baseTransition}, opacity ${baseTransition} !important;
}
body .word-rotate-flip .word-rotate-item.previous {
transform: rotateX(-90deg) !important;
transition: transform ${baseTransition}, opacity ${baseTransition} !important;
}
`;
document.head.appendChild(style);
}
createStructure() {
this.wrapper = document.createElement('span');
this.wrapper.className = `word-rotate-wrapper word-rotate-${this.animationType}`;
this.words.forEach((word, index) => {
const wordElement = document.createElement('span');
wordElement.className = `word-rotate-item ${index === 0 ? 'active' : ''}`;
wordElement.innerHTML = word;
this.applyStyles(wordElement);
this.wrapper.appendChild(wordElement);
});
this.element.innerHTML = '';
this.element.appendChild(this.wrapper);
this.setWrapperDimensions();
this.resizeHandler = this.setWrapperDimensions.bind(this);
window.addEventListener('resize', this.resizeHandler);
}
applyStyles(element) {
if (this.config.useGradient) {
element.style.color = 'transparent';
element.style.backgroundImage = `linear-gradient(90deg, ${this.config.textColor}, ${this.config.gradientEndColor})`;
element.style.backgroundSize = 'auto';
element.style.backgroundPosition = '0 0';
element.style.backgroundRepeat = 'no-repeat';
element.style.webkitBackgroundClip = 'text';
element.style.backgroundClip = 'text';
element.style.webkitTextFillColor = 'transparent';
} else {
element.style.color = this.config.textColor;
element.style.background = 'none';
element.style.webkitBackgroundClip = '';
element.style.backgroundClip = '';
element.style.webkitTextFillColor = '';
}
}
setWrapperDimensions() {
let maxWidth = 0;
let maxHeight = 0;
const wordElements = this.wrapper.querySelectorAll('.word-rotate-item');
wordElements.forEach(element => {
const prevOpacity = element.style.opacity;
const prevPosition = element.style.position;
element.style.opacity = '1';
element.style.position = 'static';
const rect = element.getBoundingClientRect();
maxWidth = Math.max(maxWidth, rect.width);
maxHeight = Math.max(maxHeight, rect.height);
element.style.opacity = prevOpacity;
element.style.position = prevPosition;
});
this.wrapper.style.minWidth = `${maxWidth}px`;
this.wrapper.style.height = `${maxHeight}px`;
}
startRotation() {
this.interval = setInterval(() => {
const items = this.wrapper.querySelectorAll('.word-rotate-item');
const current = this.wrapper.querySelector('.word-rotate-item.active');
const currentIndex = Array.from(items).indexOf(current);
current.classList.remove('active');
current.classList.add('previous');
setTimeout(() => {
current.classList.remove('previous');
}, this.animationSpeed);
const nextIndex = (currentIndex + 1) % items.length;
items[nextIndex].classList.add('active');
}, this.duration);
}
updateConfig(newConfig) {
Object.assign(this.config, newConfig);
this.words = newConfig.words;
this.duration = newConfig.duration;
this.animationSpeed = newConfig.speed;
this.animationType = newConfig.type;
// Esta es la línea clave que faltaba
this.setupStyles();
if (this.interval) {
clearInterval(this.interval);
}
this.wrapper.className = `word-rotate-wrapper word-rotate-${this.animationType}`;
const wordElements = this.wrapper.querySelectorAll('.word-rotate-item');
wordElements.forEach(element => {
this.applyStyles(element);
});
this.startRotation();
}
destroy() {
if (this.interval) {
clearInterval(this.interval);
}
this.element.innerHTML = this.originalText;
window.removeEventListener('resize', this.resizeHandler);
}
}
function initializeUI() {
initWordRotate();
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-word-rotate');
});
document.getElementById('download-config').addEventListener('click', () => {
copyJsToClipboard();
});
document.getElementById('copy-full-section').addEventListener('click', () => {
copyFullSectionToClipboard();
});
document.getElementById('randomize-word-rotate').addEventListener('click', () => {
generateRandomWordRotate();
});
const backgroundPicker = document.getElementById('preview-background-picker');
const previewContainer = document.getElementById('word-rotate-preview');
backgroundPicker.addEventListener('input', (e) => {
const selectedColor = e.target.value;
previewContainer.style.background = `linear-gradient(135deg, ${selectedColor} 0%, ${adjustColor(selectedColor, -20)} 100%)`;
showNotification(`Preview background changed to ${selectedColor}`);
});
function adjustColor(hex, percent) {
const num = parseInt(hex.replace("#", ""), 16);
const amt = Math.round(2.55 * percent);
const R = (num >> 16) + amt;
const G = (num >> 8 & 0x00FF) + amt;
const B = (num & 0x0000FF) + amt;
return "#" + (0x1000000 + (R < 255 ? R < 1 ? 0 : R : 255) * 0x10000 +
(G < 255 ? G < 1 ? 0 : G : 255) * 0x100 +
(B < 255 ? B < 1 ? 0 : B : 255)).toString(16).slice(1);
}
previewContainer.style.background = `linear-gradient(135deg, #333 0%, #222 100%)`;
document.getElementById('reset-words').addEventListener('click', () => {
wordRotateConfig.words = [...defaultConfig.words];
updateWordsDisplay();
updateWordRotatePreview();
showNotification('Words reset to default');
});
document.getElementById('reset-colors').addEventListener('click', () => {
wordRotateConfig.textColor = defaultConfig.textColor;
wordRotateConfig.useGradient = defaultConfig.useGradient;
wordRotateConfig.gradientEndColor = defaultConfig.gradientEndColor;
document.getElementById('text-color').value = defaultConfig.textColor;
document.getElementById('use-gradient').checked = defaultConfig.useGradient;
document.getElementById('gradient-end-color').value = defaultConfig.gradientEndColor;
const gradientEndGroup = document.getElementById('gradient-end-group');
gradientEndGroup.style.display = defaultConfig.useGradient ? 'block' : 'none';
updateColorInputs();
updateWordRotatePreview();
showNotification('Colors reset to default');
});
document.getElementById('reset-animation').addEventListener('click', () => {
wordRotateConfig.duration = defaultConfig.duration;
wordRotateConfig.speed = defaultConfig.speed;
wordRotateConfig.type = defaultConfig.type;
document.getElementById('duration').value = defaultConfig.duration;
document.getElementById('speed').value = defaultConfig.speed;
document.getElementById('animation-type').value = defaultConfig.type;
document.getElementById('duration-value').textContent = defaultConfig.duration;
document.getElementById('speed-value').textContent = defaultConfig.speed;
updateWordRotatePreview();
showNotification('Animation settings reset');
});
document.getElementById('reset-advanced').addEventListener('click', () => {
wordRotateConfig.pauseOnHover = defaultConfig.pauseOnHover;
document.getElementById('pause-on-hover').checked = defaultConfig.pauseOnHover;
updateWordRotatePreview();
showNotification('Advanced settings reset');
});
document.getElementById('new-word').addEventListener('keypress', function(e) {
if (e.key === 'Enter' && this.value.trim()) {
wordRotateConfig.words.push(this.value.trim());
this.value = '';
updateWordsDisplay();
updateWordRotatePreview();
}
});
document.getElementById('words-container').addEventListener('click', function(e) {
if (e.target.classList.contains('remove-tag')) {
const index = Array.from(this.children).indexOf(e.target.parentElement);
wordRotateConfig.words.splice(index, 1);
updateWordsDisplay();
updateWordRotatePreview();
}
});
const setupColorInputs = (prefix) => {
const colorInput = document.getElementById(`${prefix}-color`);
const hexInput = document.getElementById(`${prefix}-color-hex`);
const hslInput = document.getElementById(`${prefix}-color-hsl`);
if (!colorInput || !hexInput || !hslInput) return;
const configKey = prefix === 'text' ? 'textColor' : 'gradientEndColor';
hslInput.value = `hsl(${hexToHsl(colorInput.value).h}, ${hexToHsl(colorInput.value).s}%, ${hexToHsl(colorInput.value).l}%)`;
colorInput.addEventListener('input', () => {
const color = colorInput.value;
hexInput.value = color;
hslInput.value = `hsl(${hexToHsl(color).h}, ${hexToHsl(color).s}%, ${hexToHsl(color).l}%)`;
hexInput.classList.remove('invalid');
hslInput.classList.remove('invalid');
wordRotateConfig[configKey] = color;
const colorPickerContainer = colorInput.closest('.color-row').querySelector('.color-picker-container');
colorPickerContainer.style.setProperty('--selected-color', color);
updateWordRotatePreview();
});
hexInput.addEventListener('input', (e) => {
let hex = e.target.value;
hex = formatHex(hex);
e.target.value = hex;
if (isValidHex(hex)) {
colorInput.value = hex;
hslInput.value = `hsl(${hexToHsl(hex).h}, ${hexToHsl(hex).s}%, ${hexToHsl(hex).l}%)`;
wordRotateConfig[configKey] = hex;
e.target.classList.remove('invalid');
hslInput.classList.remove('invalid');
const colorPickerContainer = colorInput.closest('.color-row').querySelector('.color-picker-container');
colorPickerContainer.style.setProperty('--selected-color', hex);
updateWordRotatePreview();
} else {
e.target.classList.add('invalid');
}
});
hexInput.addEventListener('blur', (e) => {
if (!isValidHex(e.target.value)) {
e.target.value = colorInput.value;
e.target.classList.remove('invalid');
}
});
hslInput.addEventListener('input', (e) => {
let hsl = e.target.value;
if (isValidHsl(hsl)) {
const hex = hslToHex(hsl);
if (hex) {
colorInput.value = hex;
hexInput.value = hex;
wordRotateConfig[configKey] = hex;
e.target.classList.remove('invalid');
hexInput.classList.remove('invalid');
const colorPickerContainer = colorInput.closest('.color-row').querySelector('.color-picker-container');
colorPickerContainer.style.setProperty('--selected-color', hex);
updateWordRotatePreview();
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) {
colorInput.value = hex;
hexInput.value = hex;
wordRotateConfig[configKey] = hex;
e.target.classList.remove('invalid');
hexInput.classList.remove('invalid');
updateWordRotatePreview();
return;
}
}
}
if (!isValidHsl(e.target.value)) {
e.target.value = `hsl(${hexToHsl(colorInput.value).h}, ${hexToHsl(colorInput.value).s}%, ${hexToHsl(colorInput.value).l}%)`;
e.target.classList.remove('invalid');
}
});
};
setupColorInputs('text');
setupColorInputs('gradient-end');
const useGradientCheckbox = document.getElementById('use-gradient');
const gradientEndGroup = document.getElementById('gradient-end-group');
useGradientCheckbox.addEventListener('change', function() {
wordRotateConfig.useGradient = this.checked;
gradientEndGroup.style.display = this.checked ? 'block' : 'none';
updateWordRotatePreview();
});
const rangeInputs = document.querySelectorAll('input[type="range"]');
rangeInputs.forEach(input => {
const 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 'duration':
wordRotateConfig.duration = parseInt(input.value);
break;
case 'speed':
wordRotateConfig.speed = parseInt(input.value);
break;
}
updateWordRotatePreview();
});
});
document.getElementById('animation-type').addEventListener('change', function() {
wordRotateConfig.type = this.value;
updateWordRotatePreview();
});
document.getElementById('pause-on-hover').addEventListener('change', function() {
wordRotateConfig.pauseOnHover = this.checked;
updateWordRotatePreview();
});
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':
generateRandomWordRotate();
break;
case 'b':
document.getElementById('preview-background-picker').click();
break;
}
}
});
updateColorInputs();
updateWordsDisplay();
setTimeout(() => {
showNotification('BricksFusion Word Rotate Configurator loaded!');
}, 500);
function saveConfiguration() {
try {
localStorage.setItem('bricksfusion-word-rotate-config', JSON.stringify(wordRotateConfig));
} catch (e) {
}
}
function loadConfiguration() {
try {
const saved = localStorage.getItem('bricksfusion-word-rotate-config');
if (saved) {
const savedConfig = JSON.parse(saved);
Object.assign(wordRotateConfig, savedConfig);
document.getElementById('duration').value = savedConfig.duration;
document.getElementById('speed').value = savedConfig.speed;
document.getElementById('animation-type').value = savedConfig.type;
document.getElementById('text-color').value = savedConfig.textColor;
document.getElementById('use-gradient').checked = savedConfig.useGradient;
document.getElementById('gradient-end-color').value = savedConfig.gradientEndColor;
document.getElementById('pause-on-hover').checked = savedConfig.pauseOnHover;
document.getElementById('duration-value').textContent = savedConfig.duration;
document.getElementById('speed-value').textContent = savedConfig.speed;
const gradientEndGroup = document.getElementById('gradient-end-group');
gradientEndGroup.style.display = savedConfig.useGradient ? 'block' : 'none';
updateColorInputs();
updateWordsDisplay();
updateWordRotatePreview();
}
} catch (e) {
}
}
const originalUpdateWordRotatePreview = updateWordRotatePreview;
updateWordRotatePreview = function() {
originalUpdateWordRotatePreview();
saveConfiguration();
};
loadConfiguration();
}
initializeUI();
});
</script>
</body>
</html>
Rotate Words
Automatically cycles through different words with smooth animated transitions. Features multiple animation styles including fade, slide, scale, and flip effects. Choose from various easing curves for natural motion. Automatically inherits text styling including gradients. Perfect for headlines, taglines, or highlighting multiple product features in one space.
Rotate Words
Watch the words cycle automatically.
Words
Add the words or phrases you want to rotate through. The effect will cycle through them continuously in the order you list them. Add at least 2 items.
Default: Amazing, Creative, Beautiful
Timing
How long each word stays visible before changing to the next. Lower creates quick cycling. Higher gives readers more time to absorb each word.
Default: 2.5 seconds
How quickly the animation happens when changing words. Lower creates slow, graceful transitions. Higher produces quick, snappy changes.
Default: 0.25 seconds
Animation
Type of animation between words. Fade Slide combines opacity with vertical movement. Fade only changes opacity. Slide Up/Down moves vertically. Scale zooms in/out. Flip creates 3D rotation effect.
Default: Fade Slide
How the animation accelerates and decelerates. Linear maintains constant speed. Ease In starts slow, ends fast. Ease Out starts fast, ends slow. Ease In-Out creates smooth acceleration on both ends.
Default: Ease Out
Performance
This element uses CSS transitions for smooth word cycling. Features automatic dimension calculation to prevent layout shifts. Inherits all text styling including colors and gradients. Includes resize handling for responsive behavior. Very lightweight - suitable for all devices with unlimited instances per page and smooth 60fps performance.