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>Button Reveal Effect 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;
}
* {
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;
}
.how-to-use ol {
padding-left: 1.5rem;
}
.how-to-use li {
margin-bottom: 0.75rem;
font-size: var(--text-xs);
color: var(--text-secondary);
line-height: 1.6;
}
.how-to-use strong {
color: var(--text-primary);
font-weight: 600;
}
.how-to-use code {
background-color: rgba(50, 50, 50, 0.5);
padding: 0.2rem 0.5rem;
border-radius: 4px;
font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
font-size: var(--text-xs);
color: #ff8c51;
}
.content {
display: grid;
grid-template-columns: 1fr 500px;
gap: 2rem;
align-items: start;
}
.preview-section {
position: sticky;
top: 2rem;
}
.controls-section {
max-width: 500px;
}
.card {
background-color: var(--card-bg);
border-radius: var(--card-radius);
box-shadow: var(--shadow);
overflow: hidden;
margin-bottom: 1.5rem;
border: 1px solid var(--border);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 6px 24px rgba(0, 0, 0, 0.4);
}
.preview-container {
height: 400px;
width: 100%;
position: relative;
overflow: hidden;
border-radius: var(--card-radius);
background-color: #252525;
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
border: 1px solid var(--border);
box-shadow: var(--shadow);
}
.preview-wrapper {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 1.5rem;
z-index: 2;
}
.preview-content {
text-align: center;
color: white;
font-weight: bold;
font-size: var(--text-s);
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
margin: 0;
}
.preview-controls {
position: absolute;
top: 1rem;
right: 1rem;
display: flex;
gap: 0.5rem;
z-index: 100;
}
.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);
}
.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;
}
.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;
}
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);
}
input[type="range"]::-moz-range-thumb {
width: 20px;
height: 20px;
background: var(--thumb);
border: none;
border-radius: 50%;
cursor: pointer;
transition: var(--transition);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
}
input[type="range"]::-moz-range-thumb:hover {
transform: scale(1.2);
box-shadow: 0 0 10px rgba(239, 96, 19, 0.5);
}
input[type="text"],
input[type="url"],
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: rgba(30, 30, 30, 0.7);
outline: none;
transition: var(--transition);
}
input[type="text"]:focus,
input[type="url"]:focus,
select:focus {
border-color: var(--accent);
box-shadow: 0 0 0 2px rgba(239, 96, 19, 0.2);
}
.switch-container {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 1.2rem;
padding: 0.5rem 0;
}
.switch-label {
font-size: var(--text-xs);
font-weight: 500;
letter-spacing: 0.2px;
display: flex;
align-items: center;
gap: 0.5rem;
}
.switch {
position: relative;
display: inline-block;
width: 52px;
height: 28px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: var(--track);
transition: var(--transition);
border-radius: 34px;
}
.slider:before {
position: absolute;
content: "";
height: 20px;
width: 20px;
left: 4px;
bottom: 4px;
background-color: #f2f2f7;
transition: var(--transition);
border-radius: 50%;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}
input:checked + .slider {
background-color: var(--accent);
}
input:checked + .slider:before {
transform: translateX(24px);
}
.radio-group {
display: flex;
gap: 10px;
margin-bottom: 1rem;
flex-wrap: wrap;
}
.radio-option {
flex: 1;
min-width: 80px;
text-align: center;
}
.radio-option input[type="radio"] {
display: none;
}
.radio-option label {
display: block;
padding: 8px;
background-color: var(--card-bg);
border: 1px solid var(--border);
border-radius: var(--input-radius);
color: var(--text-secondary);
font-size: var(--text-xs);
cursor: pointer;
transition: var(--transition);
}
.radio-option input[type="radio"]:checked + label {
background-color: var(--accent);
color: white;
border-color: var(--accent);
}
.radio-option label:hover {
border-color: var(--accent);
background-color: rgba(239, 96, 19, 0.1);
}
.notification {
position: fixed;
bottom: calc(var(--action-bar-height) + 1rem);
left: 50%;
background-color: #28a745;
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;
}
.demo-btn {
background-color: var(--accent);
color: white;
border: none;
border-radius: 8px;
padding: 10px 20px;
font-size: var(--text-xs);
font-weight: 500;
cursor: pointer;
transition: var(--transition);
position: relative;
z-index: 3;
}
.demo-btn:hover {
background-color: var(--accent-hover);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(239, 96, 19, 0.3);
}
.video-container {
position: fixed;
width: 0;
height: 0;
display: none;
z-index: 10000;
overflow: hidden;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
background-color: #000;
}
.video-preview {
width: 100% !important;
height: 100% !important;
object-fit: cover;
display: block;
}
.backdrop-preview {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0);
backdrop-filter: blur(0px);
z-index: 9999;
opacity: 0;
transition: opacity 0.5s ease, backdrop-filter 0.5s ease, background-color 0.5s ease;
pointer-events: none;
}
.backdrop-preview.active {
opacity: 1;
pointer-events: auto;
}
@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);
}
.preview-container {
height: 300px;
}
.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);
}
</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/visual-effects/" class="breadcrumb-item">Visual effects</a>
<span class="breadcrumb-separator">›</span>
<span class="breadcrumb-item active">Button Reveal Effect</span>
</nav>
<div class="action-buttons">
<div class="data-attribute-display" id="quick-attribute" title="Click to copy data attribute">
data-button-reveal
</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">Button Reveal Effect</h1>
<p class="page-subtitle">Interactive video reveal 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="how-to-use">
<ol>
<li>Customize your button reveal effect 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 into the code element</li>
<li>To add the effect to any button: go to <strong>Section → Style → Attributes</strong>, add <code>data-button-reveal</code> as attribute name (leave value empty)</li>
</ol>
</div>
</div>
</div>
</div>
<div class="content">
<section class="preview-section">
<div class="preview-container" id="button-reveal-preview">
<div class="backdrop-preview" id="backdrop-preview"></div>
<div class="video-container" id="video-container"></div>
<div class="preview-wrapper">
<div class="preview-content">Interactive Button Reveal Preview</div>
<button class="demo-btn" id="demo-button" data-button-reveal>
Click to Open Video
</button>
</div>
</div>
</section>
<section class="controls-section">
<div class="card">
<div class="card-heading">
Video Settings
<div class="card-actions">
<button class="card-action-btn" id="reset-video" title="Reset Video Settings">↺</button>
</div>
</div>
<div class="card-content">
<div class="control-group">
<div class="control-label">
<span class="label-text">Video URL</span>
</div>
<input type="url" id="video-url" placeholder="Enter video URL" value="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-576p.mp4">
</div>
<div class="switch-container">
<span class="switch-label">
Show Video Controls
<span class="help-tooltip" title="Enable playback controls on the video">ℹ</span>
</span>
<label class="switch">
<input type="checkbox" id="video-controls">
<span class="slider"></span>
</label>
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">Video Fit</span>
</div>
<div class="radio-group">
<div class="radio-option">
<input type="radio" id="fit-cover" name="fit" value="cover" checked>
<label for="fit-cover">Cover</label>
</div>
<div class="radio-option">
<input type="radio" id="fit-contain" name="fit" value="contain">
<label for="fit-contain">Contain</label>
</div>
<div class="radio-option">
<input type="radio" id="fit-fill" name="fit" value="fill">
<label for="fit-fill">Fill</label>
</div>
</div>
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">Default Video Size</span>
</div>
<div class="radio-group">
<div class="radio-option">
<input type="radio" id="size-small" name="size" value="small">
<label for="size-small">Small</label>
</div>
<div class="radio-option">
<input type="radio" id="size-medium" name="size" value="medium">
<label for="size-medium">Medium</label>
</div>
<div class="radio-option">
<input type="radio" id="size-large" name="size" value="large">
<label for="size-large">Large</label>
</div>
<div class="radio-option">
<input type="radio" id="size-full" name="size" value="full" checked>
<label for="size-full">Full</label>
</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">
Animation Speed
<span class="help-tooltip" title="Overall speed multiplier for animations">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="animation-speed-value">1</span>x</span>
<button class="reset-btn" onclick="resetParameter('animation-speed', 1)">↺</button>
</div>
</div>
<input type="range" id="animation-speed" min="0.5" max="2" step="0.1" value="1">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">
Base Duration
<span class="help-tooltip" title="Base animation duration in seconds">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="animation-duration-value">1</span>s</span>
<button class="reset-btn" onclick="resetParameter('animation-duration', 1)">↺</button>
</div>
</div>
<input type="range" id="animation-duration" min="0.5" max="2" step="0.1" value="1">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">
Mobile Animation Duration
<span class="help-tooltip" title="Animation duration for mobile devices">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="mobile-duration-value">0.8</span>s</span>
<button class="reset-btn" onclick="resetParameter('mobile-duration', 0.8)">↺</button>
</div>
</div>
<input type="range" id="mobile-duration" min="0.3" max="1.5" step="0.1" value="0.8">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">
Mobile Scale Factor
<span class="help-tooltip" title="Scale factor for mobile animations">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="mobile-scale-value">0.9</span>x</span>
<button class="reset-btn" onclick="resetParameter('mobile-scale', 0.9)">↺</button>
</div>
</div>
<input type="range" id="mobile-scale" min="0.7" max="1" step="0.05" value="0.9">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">Easing Animation</span>
</div>
<div class="radio-group">
<div class="radio-option">
<input type="radio" id="easing-power2" name="easing" value="power2.inOut" checked>
<label for="easing-power2">Smooth</label>
</div>
<div class="radio-option">
<input type="radio" id="easing-power3" name="easing" value="power3.inOut">
<label for="easing-power3">Strong</label>
</div>
<div class="radio-option">
<input type="radio" id="easing-back" name="easing" value="back.out">
<label for="easing-back">Bounce</label>
</div>
<div class="radio-option">
<input type="radio" id="easing-elastic" name="easing" value="elastic.out">
<label for="easing-elastic">Elastic</label>
</div>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-heading">
Visual Settings
<div class="card-actions">
<button class="card-action-btn" id="reset-visual" title="Reset Visual Settings">↺</button>
</div>
</div>
<div class="card-content">
<div class="control-group">
<div class="control-label">
<span class="label-text">
Border Radius
<span class="help-tooltip" title="Corner radius of the video container">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="border-radius-value">20</span>px</span>
<button class="reset-btn" onclick="resetParameter('border-radius', 20)">↺</button>
</div>
</div>
<input type="range" id="border-radius" min="0" max="40" step="1" value="20">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">
Backdrop Blur Amount
<span class="help-tooltip" title="Blur effect for the background overlay">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="backdrop-blur-value">10</span>px</span>
<button class="reset-btn" onclick="resetParameter('backdrop-blur', 10)">↺</button>
</div>
</div>
<input type="range" id="backdrop-blur" min="0" max="20" step="1" value="10">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">
Backdrop Opacity
<span class="help-tooltip" title="Opacity of the background overlay">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="backdrop-opacity-value">0.5</span></span>
<button class="reset-btn" onclick="resetParameter('backdrop-opacity', 0.5)">↺</button>
</div>
</div>
<input type="range" id="backdrop-opacity" min="0" max="1" step="0.05" value="0.5">
</div>
</div>
</div>
</section>
</div>
</div>
<div class="notification" id="notification"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.11.4/gsap.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
let brConfig = {
animation: {
defaultSpeed: 1,
duration: 1,
mobileDuration: 0.8,
mobileScale: 0.9,
easingIn: 'power2.inOut',
easingOut: 'power2.inOut',
},
video: {
url: 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-576p.mp4',
controls: false,
fit: 'cover',
size: 'full',
},
container: {
borderRadius: 20,
backdropBlur: 10,
backdropOpacity: 0.5,
}
};
const defaultConfig = JSON.parse(JSON.stringify(brConfig));
let previewVideo = null;
let previewState = 'closed';
let activeAnimation = null;
function initializeUI() {
setupRangeSliders();
setupInputs();
setupRadioButtons();
setupSwitches();
setupButtons();
setupInstructions();
setupDemoButton();
setupKeyboardShortcuts();
loadConfiguration();
updatePreview();
setTimeout(() => {
showNotification('Button Reveal Effect Configurator loaded!');
}, 500);
}
function setupRangeSliders() {
const rangeInputs = [
{ id: 'animation-speed', key: 'animation.defaultSpeed' },
{ id: 'animation-duration', key: 'animation.duration' },
{ id: 'mobile-duration', key: 'animation.mobileDuration' },
{ id: 'mobile-scale', key: 'animation.mobileScale' },
{ id: 'border-radius', key: 'container.borderRadius' },
{ id: 'backdrop-blur', key: 'container.backdropBlur' },
{ id: 'backdrop-opacity', key: 'container.backdropOpacity' }
];
rangeInputs.forEach(({ id, key }) => {
const slider = document.getElementById(id);
const valueDisplay = document.getElementById(`${id}-value`);
if (slider && valueDisplay) {
valueDisplay.textContent = slider.value;
slider.addEventListener('input', function() {
valueDisplay.textContent = this.value;
updateConfigValue(key, parseFloat(this.value));
updateConfig();
});
}
});
}
function setupInputs() {
const videoUrlInput = document.getElementById('video-url');
['input', 'change', 'paste'].forEach(eventType => {
videoUrlInput.addEventListener(eventType, function() {
setTimeout(() => {
brConfig.video.url = this.value.trim();
updateConfig();
}, 100);
});
});
}
function setupRadioButtons() {
document.querySelectorAll('input[name="easing"]').forEach(radio => {
radio.addEventListener('change', function() {
if (this.checked) {
brConfig.animation.easingIn = this.value;
brConfig.animation.easingOut = this.value;
updateConfig();
}
});
});
document.querySelectorAll('input[name="size"]').forEach(radio => {
radio.addEventListener('change', function() {
if (this.checked) {
brConfig.video.size = this.value;
updateConfig();
}
});
});
document.querySelectorAll('input[name="fit"]').forEach(radio => {
radio.addEventListener('change', function() {
if (this.checked) {
brConfig.video.fit = this.value;
updateConfig();
}
});
});
}
function setupSwitches() {
const videoControlsSwitch = document.getElementById('video-controls');
videoControlsSwitch.addEventListener('change', function() {
brConfig.video.controls = this.checked;
updateConfig();
});
}
function setupButtons() {
document.getElementById('quick-attribute').addEventListener('click', () => {
copyToClipboard('data-button-reveal');
});
document.getElementById('download-config').addEventListener('click', function() {
const downloadBtn = this;
if (downloadBtn.hasAttribute('data-protection-animation')) {
copyJsToClipboard();
} else {
copyJsToClipboard();
}
});
document.getElementById('copy-full-section').addEventListener('click', function() {
const fullSectionBtn = this;
if (fullSectionBtn.hasAttribute('data-protection-animation')) {
copyFullSectionToClipboard();
} else {
copyFullSectionToClipboard();
}
});
document.getElementById('reset-video').addEventListener('click', () => {
brConfig.video = JSON.parse(JSON.stringify(defaultConfig.video));
updateUIFromConfig();
updateConfig();
showNotification('Video settings reset to default');
});
document.getElementById('reset-animation').addEventListener('click', () => {
brConfig.animation = JSON.parse(JSON.stringify(defaultConfig.animation));
updateUIFromConfig();
updateConfig();
showNotification('Animation settings reset to default');
});
document.getElementById('reset-visual').addEventListener('click', () => {
brConfig.container = JSON.parse(JSON.stringify(defaultConfig.container));
updateUIFromConfig();
updateConfig();
showNotification('Visual settings reset to default');
});
}
function setupInstructions() {
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');
}
});
}
function setupDemoButton() {
const demoButton = document.getElementById('demo-button');
const videoContainer = document.getElementById('video-container');
const backdropPreview = document.getElementById('backdrop-preview');
demoButton.addEventListener('click', function() {
if (previewState === 'closed') {
openPreview();
} else if (previewState === 'open') {
closePreview();
}
});
backdropPreview.addEventListener('click', function() {
if (previewState === 'open') {
closePreview();
}
});
videoContainer.addEventListener('click', function(e) {
if (previewState === 'open' && (!brConfig.video.controls || e.target !== previewVideo)) {
e.stopPropagation();
closePreview();
}
});
}
function setupKeyboardShortcuts() {
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.click();
}
break;
case 's':
e.preventDefault();
const fullSectionBtn = document.getElementById('copy-full-section');
if (fullSectionBtn) {
fullSectionBtn.click();
}
break;
}
}
});
}
function updateConfigValue(path, value) {
const keys = path.split('.');
let obj = brConfig;
for (let i = 0; i < keys.length - 1; i++) {
obj = obj[keys[i]];
}
obj[keys[keys.length - 1]] = value;
}
function updateConfig() {
if (previewVideo && previewState === 'open') {
if (previewVideo.tagName === 'VIDEO') {
previewVideo.controls = brConfig.video.controls;
previewVideo.muted = !brConfig.video.controls;
previewVideo.style.objectFit = brConfig.video.fit;
}
const videoContainer = document.getElementById('video-container');
const backdropPreview = document.getElementById('backdrop-preview');
if (videoContainer) {
videoContainer.style.borderRadius = `${brConfig.container.borderRadius}px`;
}
if (backdropPreview) {
backdropPreview.style.backdropFilter = `blur(${brConfig.container.backdropBlur}px)`;
backdropPreview.style.backgroundColor = `rgba(0, 0, 0, ${brConfig.container.backdropOpacity})`;
}
}
saveConfiguration();
}
function updatePreview() {
updateConfig();
}
function updateUIFromConfig() {
document.getElementById('video-url').value = brConfig.video.url;
document.getElementById('video-controls').checked = brConfig.video.controls;
document.querySelector(`input[name="fit"][value="${brConfig.video.fit}"]`).checked = true;
document.querySelector(`input[name="size"][value="${brConfig.video.size}"]`).checked = true;
document.querySelector(`input[name="easing"][value="${brConfig.animation.easingIn}"]`).checked = true;
const sliders = [
{ id: 'animation-speed', value: brConfig.animation.defaultSpeed },
{ id: 'animation-duration', value: brConfig.animation.duration },
{ id: 'mobile-duration', value: brConfig.animation.mobileDuration },
{ id: 'mobile-scale', value: brConfig.animation.mobileScale },
{ id: 'border-radius', value: brConfig.container.borderRadius },
{ id: 'backdrop-blur', value: brConfig.container.backdropBlur },
{ id: 'backdrop-opacity', value: brConfig.container.backdropOpacity }
];
sliders.forEach(({ id, value }) => {
const slider = document.getElementById(id);
const valueDisplay = document.getElementById(`${id}-value`);
if (slider && valueDisplay) {
slider.value = value;
valueDisplay.textContent = value;
}
});
}
function createVideoElement() {
const videoContainer = document.getElementById('video-container');
videoContainer.innerHTML = '';
if (previewVideo) {
if (previewVideo.tagName === 'VIDEO') {
previewVideo.pause();
previewVideo.removeAttribute('src');
previewVideo.load();
}
previewVideo = null;
}
const latestUrl = document.getElementById('video-url').value.trim();
brConfig.video.url = latestUrl;
const fallbackUrl = "https://storage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4";
if (!latestUrl) {
brConfig.video.url = fallbackUrl;
}
const isStreamingService = (url) => {
return url.includes('youtube.com') || url.includes('youtu.be') ||
url.includes('vimeo.com') || url.includes('player.vimeo.com');
};
if (isStreamingService(brConfig.video.url)) {
const iframe = document.createElement('iframe');
let embedUrl = brConfig.video.url;
if (brConfig.video.url.includes('youtube.com') || brConfig.video.url.includes('youtu.be')) {
const videoId = brConfig.video.url.includes('youtu.be/')
? brConfig.video.url.split('youtu.be/')[1].split('?')[0]
: brConfig.video.url.includes('v=')
? brConfig.video.url.split('v=')[1].split('&')[0]
: '';
if (videoId) {
embedUrl = `https://www.youtube.com/embed/${videoId}?autoplay=1&mute=${!brConfig.video.controls ? 1 : 0}&controls=${brConfig.video.controls ? 1 : 0}`;
}
} else if (brConfig.video.url.includes('vimeo.com')) {
const vimeoId = brConfig.video.url.split('vimeo.com/')[1]?.split('/')[0]?.split('?')[0];
if (vimeoId) {
embedUrl = `https://player.vimeo.com/video/${vimeoId}?autoplay=1&muted=${!brConfig.video.controls ? 1 : 0}`;
}
}
iframe.src = embedUrl;
iframe.style.width = '100%';
iframe.style.height = '100%';
iframe.style.border = 'none';
iframe.allow = 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture';
iframe.allowFullscreen = true;
videoContainer.appendChild(iframe);
return iframe;
}
previewVideo = document.createElement('video');
previewVideo.className = 'video-preview';
previewVideo.muted = !brConfig.video.controls;
previewVideo.controls = brConfig.video.controls;
previewVideo.loop = true;
previewVideo.playsInline = true;
previewVideo.setAttribute('playsinline', '');
previewVideo.style.objectFit = brConfig.video.fit;
previewVideo.style.width = "100%";
previewVideo.style.height = "100%";
previewVideo.addEventListener('error', function(e) {
if (previewVideo.src !== fallbackUrl) {
previewVideo.src = fallbackUrl;
previewVideo.load();
}
});
videoContainer.appendChild(previewVideo);
try {
previewVideo.src = brConfig.video.url;
previewVideo.load();
} catch (err) {
previewVideo.src = fallbackUrl;
previewVideo.load();
}
return previewVideo;
}
function openPreview() {
if (activeAnimation) {
activeAnimation.kill();
}
previewState = 'opening';
const demoButton = document.getElementById('demo-button');
const videoContainer = document.getElementById('video-container');
const backdropPreview = document.getElementById('backdrop-preview');
const latestUrl = document.getElementById('video-url').value.trim();
brConfig.video.url = latestUrl;
backdropPreview.style.position = 'fixed';
backdropPreview.style.top = '0';
backdropPreview.style.left = '0';
backdropPreview.style.width = '100%';
backdropPreview.style.height = '100%';
backdropPreview.style.zIndex = '9999';
backdropPreview.style.backdropFilter = `blur(${brConfig.container.backdropBlur}px)`;
backdropPreview.style.backgroundColor = `rgba(0, 0, 0, ${brConfig.container.backdropOpacity})`;
backdropPreview.classList.add('active');
const btnRect = demoButton.getBoundingClientRect();
videoContainer.style.position = 'fixed';
videoContainer.style.zIndex = '10000';
videoContainer.style.display = 'block';
videoContainer.style.borderRadius = `${brConfig.container.borderRadius}px`;
videoContainer.style.overflow = 'hidden';
const newVideo = createVideoElement();
const duration = brConfig.animation.duration / brConfig.animation.defaultSpeed;
activeAnimation = gsap.timeline();
activeAnimation.to(demoButton, {
opacity: 0,
scale: 0.8,
duration: duration * 0.5,
ease: brConfig.animation.easingOut
});
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;
const sizeInPx = getVideoSizeInPx(brConfig.video.size);
const finalWidth = sizeInPx.width === '100%' ? windowWidth * 0.9 : Math.min(sizeInPx.width, windowWidth * 0.9);
const finalHeight = sizeInPx.height === '100%' ? windowHeight * 0.9 : Math.min(sizeInPx.height, windowHeight * 0.9);
gsap.set(videoContainer, {
left: btnRect.left,
top: btnRect.top,
width: btnRect.width,
height: btnRect.height,
opacity: 0,
xPercent: 0,
yPercent: 0,
scale: 1,
transformOrigin: "center center"
});
activeAnimation.to(videoContainer, {
left: (windowWidth - finalWidth) / 2,
top: (windowHeight - finalHeight) / 2,
width: finalWidth,
height: finalHeight,
opacity: 1,
duration: duration,
ease: brConfig.animation.easingIn,
onComplete: () => {
previewState = 'open';
if (newVideo && newVideo.tagName === 'VIDEO') {
newVideo.play().catch(err => {
const fallbackUrl = "https://storage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4";
if (newVideo.src !== fallbackUrl) {
newVideo.src = fallbackUrl;
newVideo.load();
newVideo.play().catch(secondErr => {});
}
});
}
}
}, "-=0.3");
}
function closePreview() {
if (activeAnimation) {
activeAnimation.kill();
}
previewState = 'closing';
const demoButton = document.getElementById('demo-button');
const videoContainer = document.getElementById('video-container');
const backdropPreview = document.getElementById('backdrop-preview');
if (previewVideo && previewVideo.tagName === 'VIDEO') {
previewVideo.pause();
}
backdropPreview.classList.remove('active');
const duration = brConfig.animation.duration / brConfig.animation.defaultSpeed;
const btnRect = demoButton.getBoundingClientRect();
activeAnimation = gsap.timeline();
activeAnimation.to(videoContainer, {
left: btnRect.left,
top: btnRect.top,
width: btnRect.width,
height: btnRect.height,
opacity: 0,
duration: duration,
ease: brConfig.animation.easingOut,
onComplete: () => {
videoContainer.style.display = 'none';
videoContainer.innerHTML = '';
previewVideo = null;
previewState = 'closed';
}
});
activeAnimation.to(demoButton, {
opacity: 1,
scale: 1,
duration: duration * 0.5,
ease: brConfig.animation.easingIn
}, "-=0.3");
}
function getVideoSizeInPx(size) {
switch(size) {
case 'small': return { width: 300, height: 300 };
case 'medium': return { width: 600, height: 400 };
case 'large': return { width: 800, height: 600 };
default: return { width: '100%', height: '100%' };
}
}
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;
}
const pathMap = {
'animation-speed': 'animation.defaultSpeed',
'animation-duration': 'animation.duration',
'mobile-duration': 'animation.mobileDuration',
'mobile-scale': 'animation.mobileScale',
'border-radius': 'container.borderRadius',
'backdrop-blur': 'container.backdropBlur',
'backdrop-opacity': 'container.backdropOpacity'
};
if (pathMap[parameterId]) {
updateConfigValue(pathMap[parameterId], defaultValue);
updateConfig();
}
showNotification(`${parameterId.replace(/-/g, ' ')} reset to default`);
}
};
function generateUniqueId() {
return Math.random().toString(36).substr(2, 9) + Date.now().toString(36);
}
function generateFullSectionJSON() {
// Generar IDs únicos para todos los elementos
const sectionId = generateUniqueId();
const containerId = generateUniqueId();
const divId = generateUniqueId();
const buttonId = 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",
"_background": {
"color": {
"hex": "#ffffff"
}
}
}
},
{
"id": containerId,
"name": "container",
"parent": sectionId,
"children": [divId],
"settings": {
"_alignItems": "center"
},
"label": "Container"
},
{
"id": divId,
"name": "div",
"parent": containerId,
"children": [buttonId],
"settings": {
"_width": "300",
"_height": "300",
"_border": {
"radius": {
"top": "15",
"right": "15",
"bottom": "15",
"left": "15"
}
},
"_display": "flex",
"_alignItems": "center",
"_justifyContent": "center",
"_background": {
"color": {
"hex": "#000000"
}
}
}
},
{
"id": buttonId,
"name": "button",
"parent": divId,
"children": [],
"settings": {
"style": "primary",
"text": "Reveal",
"_border": {
"radius": {
"top": "15",
"right": "15",
"bottom": "15",
"left": "15"
}
},
"_attributes": [
{
"id": attributeId,
"name": "data-button-reveal"
}
]
},
"label": "Button Reveal"
},
{
"id": codeId,
"name": "code",
"parent": sectionId,
"children": [],
"settings": {
"javascriptCode": jsCode,
"executeCode": true,
"_display": "none"
},
"label": "Button Reveal 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 AnimationManager {
constructor() {
this.animationQueue = [];
this.isAnimating = false;
}
queue(animationFunction) {
this.animationQueue.push(animationFunction);
if (!this.isAnimating) {
this.processQueue();
}
}
async processQueue() {
if (this.animationQueue.length === 0) {
this.isAnimating = false;
return;
}
this.isAnimating = true;
const nextAnimation = this.animationQueue.shift();
await nextAnimation();
this.processQueue();
}
}
const CONFIG = {
ATTRIBUTES: {
button: 'data-button-reveal',
videoUrl: null
},
SIZES: {
small: { width: 300, height: 300 },
medium: { width: 600, height: 400 },
large: { width: 800, height: 600 },
full: { width: '100%', height: '100%' }
},
ANIMATION: {
defaultSpeed: ${brConfig.animation.defaultSpeed},
duration: ${brConfig.animation.duration},
easingIn: '${brConfig.animation.easingIn}',
easingOut: '${brConfig.animation.easingOut}',
mobileScale: ${brConfig.animation.mobileScale},
mobileDuration: ${brConfig.animation.mobileDuration}
},
DEFAULTS: {
objectFit: '${brConfig.video.fit}',
videoUrl: '${brConfig.video.url}',
fallbackUrl: 'https://storage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
showControls: ${brConfig.video.controls}
},
CONTAINER: {
borderRadius: ${brConfig.container.borderRadius},
backdropBlur: ${brConfig.container.backdropBlur},
backdropColor: 'rgba(0, 0, 0, ${brConfig.container.backdropOpacity})'
}
};
class DOMUtils {
static createOverlay() {
const overlay = document.createElement('div');
overlay.className = 'br-overlay';
return overlay;
}
static createVideoContainer() {
const container = document.createElement('div');
container.className = 'br-container';
return container;
}
static createVideo(url, isMuted = !CONFIG.DEFAULTS.showControls, objectFit = CONFIG.DEFAULTS.objectFit) {
const isStreamingService = (url) => {
return url.includes('youtube.com') || url.includes('youtu.be') ||
url.includes('vimeo.com') || url.includes('player.vimeo.com');
};
if (isStreamingService(url)) {
const iframe = document.createElement('iframe');
if (url.includes('youtube.com') || url.includes('youtu.be')) {
const videoId = url.includes('youtu.be/')
? url.split('youtu.be/')[1].split('?')[0]
: url.includes('v=')
? url.split('v=')[1].split('&')[0]
: '';
if (videoId) {
iframe.src = \`https://www.youtube.com/embed/\${videoId}?autoplay=1&mute=\${isMuted ? 1 : 0}&controls=\${!isMuted ? 1 : 0}\`;
} else {
iframe.src = url;
}
} else if (url.includes('vimeo.com')) {
const vimeoId = url.split('vimeo.com/')[1]?.split('/')[0]?.split('?')[0];
if (vimeoId) {
iframe.src = \`https://player.vimeo.com/video/\${vimeoId}?autoplay=1&muted=\${isMuted ? 1 : 0}\`;
} else {
iframe.src = url;
}
} else {
iframe.src = url;
}
iframe.className = 'br-video';
iframe.style.width = '100%';
iframe.style.height = '100%';
iframe.style.border = 'none';
iframe.style.objectFit = objectFit;
iframe.allow = 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture';
iframe.allowFullscreen = true;
return iframe;
}
const isBubblesVideo = url.includes('Bubbles02.mp4');
if (isBubblesVideo) {
url = CONFIG.DEFAULTS.fallbackUrl;
}
const video = document.createElement('video');
video.className = 'br-video';
video.muted = isMuted;
video.loop = true;
video.playsInline = true;
video.setAttribute('playsinline', '');
video.style.objectFit = objectFit;
video.style.backgroundColor = '#000';
video.controls = CONFIG.DEFAULTS.showControls;
const videoExtensions = ['.mp4', '.webm', '.ogg', '.mov', '.avi', '.wmv', '.flv', '.mkv'];
let urlLower = url.toLowerCase();
let isLikelyVideo = videoExtensions.some(ext => urlLower.includes(ext));
let errorAttempts = 0;
const maxErrorAttempts = 2;
video.addEventListener('error', function(e) {
errorAttempts++;
if (errorAttempts <= maxErrorAttempts && url !== CONFIG.DEFAULTS.fallbackUrl) {
video.src = CONFIG.DEFAULTS.fallbackUrl;
video.load();
}
});
if (!isLikelyVideo && !isStreamingService(url) && url !== CONFIG.DEFAULTS.fallbackUrl) {
video.src = CONFIG.DEFAULTS.fallbackUrl;
} else {
try {
video.src = url;
} catch (err) {
video.src = CONFIG.DEFAULTS.fallbackUrl;
}
}
return video;
}
static getVideoSize(button, config, isMobile) {
let size = config.SIZES['${brConfig.video.size}'];
if (isMobile && size.width !== '100%') {
const scale = CONFIG.ANIMATION.mobileScale;
return {
width: typeof size.width === 'number' ? size.width * scale : size.width,
height: typeof size.height === 'number' ? size.height * scale : size.height
};
}
return size;
}
static isTouchDevice() {
return ('ontouchstart' in window) || (navigator.maxTouchPoints > 0);
}
}
class VideoContainerManager {
constructor() {
this.currentContainer = null;
this.initStyles();
}
initStyles() {
const styles = \`
.br-overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0);
backdrop-filter: blur(0px);
transition: backdrop-filter 0.5s ease-in-out, background-color 0.5s ease-in-out;
z-index: 9998;
}
.br-container {
position: fixed;
z-index: 9999;
overflow: hidden;
transform: none !important;
border-radius: \${CONFIG.CONTAINER.borderRadius}px;
transform-origin: center center;
touch-action: none;
-webkit-tap-highlight-color: transparent;
background-color: #000;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
}
.br-video {
width: 100% !important;
height: 100% !important;
display: block;
margin: 0;
padding: 0;
background-color: #000;
transform: none !important;
}
.br-video[controls] {
pointer-events: auto;
}
.br-video:not([controls]) {
pointer-events: none;
}
.br-overlay.active {
background-color: \${CONFIG.CONTAINER.backdropColor};
backdrop-filter: blur(\${CONFIG.CONTAINER.backdropBlur}px);
}
@media (max-width: 768px) {
.br-container {
border-radius: \${Math.max(15, CONFIG.CONTAINER.borderRadius - 5)}px;
}
}
\`;
if (!document.querySelector('#br-styles')) {
const styleSheet = document.createElement('style');
styleSheet.id = 'br-styles';
styleSheet.textContent = styles;
document.head.appendChild(styleSheet);
}
}
setupContainer(button) {
const container = DOMUtils.createVideoContainer();
const videoUrl = CONFIG.DEFAULTS.videoUrl;
const objectFit = CONFIG.DEFAULTS.objectFit;
const isMuted = !CONFIG.DEFAULTS.showControls;
const mediaElement = DOMUtils.createVideo(videoUrl, isMuted, objectFit);
if (mediaElement.tagName.toLowerCase() === 'video') {
mediaElement.controls = CONFIG.DEFAULTS.showControls;
mediaElement.muted = !CONFIG.DEFAULTS.showControls;
}
mediaElement.style.width = "100%";
mediaElement.style.height = "100%";
const overlay = DOMUtils.createOverlay();
container.appendChild(mediaElement);
document.body.appendChild(overlay);
document.body.appendChild(container);
if (mediaElement.tagName.toLowerCase() === 'video') {
mediaElement.load();
}
return { container, video: mediaElement, overlay };
}
cleanup() {
if (this.currentContainer) {
const overlay = document.querySelector('.br-overlay');
if (overlay) document.body.removeChild(overlay);
document.body.removeChild(this.currentContainer);
this.currentContainer = null;
}
}
}
class ButtonReveal {
constructor() {
this.config = CONFIG;
this.animationManager = new AnimationManager();
this.containerManager = new VideoContainerManager();
this.isMobile = DOMUtils.isTouchDevice();
}
init() {
const buttons = document.querySelectorAll(\`[\${this.config.ATTRIBUTES.button}]\`);
buttons.forEach(button => this.setupButton(button));
}
setupButton(button) {
button.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
this.animationManager.queue(() => this.revealVideo(button));
});
}
getInitialPosition(button) {
const rect = button.getBoundingClientRect();
return {
left: rect.left,
top: rect.top,
width: rect.width,
height: rect.height,
borderRadius: getComputedStyle(button).borderRadius
};
}
getFinalPosition(videoSize) {
const margin = this.isMobile ? 16 : 20;
const maxWidth = window.innerWidth - (margin * 2);
const maxHeight = window.innerHeight - (margin * 2);
const finalWidth = Math.min(videoSize.width === '100%' ? maxWidth : videoSize.width, maxWidth);
const finalHeight = Math.min(videoSize.height === '100%' ? maxHeight : videoSize.height, maxHeight);
return {
left: (window.innerWidth - finalWidth) / 2,
top: (window.innerHeight - finalHeight) / 2,
width: finalWidth,
height: finalHeight,
borderRadius: this.isMobile ?
Math.max(15, CONFIG.CONTAINER.borderRadius - 5) + "px" :
CONFIG.CONTAINER.borderRadius + "px"
};
}
async revealVideo(button) {
this.containerManager.cleanup();
const { container, video, overlay } = this.containerManager.setupContainer(button);
const speed = this.config.ANIMATION.defaultSpeed;
const videoSize = DOMUtils.getVideoSize(button, this.config, this.isMobile);
const duration = this.isMobile ?
this.config.ANIMATION.mobileDuration / speed :
this.config.ANIMATION.duration / speed;
const initialPos = this.getInitialPosition(button);
gsap.set(container, {
left: initialPos.left,
top: initialPos.top,
width: initialPos.width,
height: initialPos.height,
borderRadius: initialPos.borderRadius,
opacity: 0,
transformOrigin: "center center"
});
gsap.set(video, { opacity: 0 });
const tl = gsap.timeline({
onComplete: () => {
container.style.touchAction = 'auto';
if (video.tagName.toLowerCase() === 'video') {
video.play().catch(err => {
if (video.src !== CONFIG.DEFAULTS.fallbackUrl) {
video.src = CONFIG.DEFAULTS.fallbackUrl;
video.load();
video.play().catch(secondErr => {});
}
});
}
}
});
tl.to(button, {
opacity: 0,
scale: 0.8,
duration: duration * 0.5,
ease: this.config.ANIMATION.easingOut
})
.to(container, {
...this.getFinalPosition(videoSize),
opacity: 1,
duration: duration,
ease: this.config.ANIMATION.easingIn
}, "-=0.2")
.to(video, {
opacity: 1,
duration: duration * 0.5,
onStart: () => {
overlay.classList.add('active');
},
onComplete: () => {
if (video.tagName.toLowerCase() === 'video') {
video.play().catch(err => {
if (video.src !== CONFIG.DEFAULTS.fallbackUrl) {
video.src = CONFIG.DEFAULTS.fallbackUrl;
video.load();
video.play().catch(secondErr => {});
}
});
}
}
}, "-=0.3");
const closeHandler = (e) => {
if (!video.controls || (e.target !== video)) {
if (this.containerManager.currentContainer === container) {
this.animationManager.queue(() => this.closeVideo(container, button, overlay));
}
}
};
container.addEventListener('click', closeHandler);
overlay.addEventListener('click', closeHandler);
this.containerManager.currentContainer = container;
}
async closeVideo(container, button, overlay) {
const video = container.querySelector('video');
if (video) video.pause();
overlay.classList.remove('active');
container.style.touchAction = 'none';
const duration = this.isMobile ?
this.config.ANIMATION.mobileDuration / 2 :
this.config.ANIMATION.duration / 2;
const initialPos = this.getInitialPosition(button);
const tl = gsap.timeline({
onComplete: () => this.containerManager.cleanup()
});
tl.to(video, {
opacity: 0,
duration: duration * 0.5,
ease: this.config.ANIMATION.easingOut
})
.to(container, {
left: initialPos.left,
top: initialPos.top,
width: initialPos.width,
height: initialPos.height,
borderRadius: initialPos.borderRadius,
opacity: 0,
duration: duration,
ease: this.config.ANIMATION.easingOut
}, "-=0.2")
.to(button, {
opacity: 1,
scale: 1,
duration: duration,
ease: this.config.ANIMATION.easingIn
}, "-=0.3");
}
}
async function initializeButtonReveal() {
if (!window.gsap) {
try {
await new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = "https://cdnjs.cloudflare.com/ajax/libs/gsap/3.11.4/gsap.min.js";
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
} catch (error) {
return;
}
}
const buttonReveal = new ButtonReveal();
buttonReveal.init();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeButtonReveal);
} else {
initializeButtonReveal();
}
})();`;
}
function copyJsToClipboard() {
const jsCode = generateJavaScriptCode();
navigator.clipboard.writeText(jsCode)
.then(() => {
showNotification('JavaScript code copied to clipboard!');
})
.catch(err => {
try {
const textArea = document.createElement('textarea');
textArea.value = jsCode;
textArea.style.position = 'fixed';
textArea.style.opacity = '0';
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
showNotification('JavaScript code copied to clipboard!');
} catch (fallbackErr) {
showNotification('Failed to copy to clipboard. Please try again.', 'error');
}
});
}
function copyFullSectionToClipboard() {
const sectionJSON = generateFullSectionJSON();
navigator.clipboard.writeText(sectionJSON)
.then(() => {
showNotification('Full section JSON copied to clipboard!');
})
.catch(err => {
try {
const textArea = document.createElement('textarea');
textArea.value = sectionJSON;
textArea.style.position = 'fixed';
textArea.style.opacity = '0';
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
showNotification('Full section JSON copied to clipboard!');
} catch (fallbackErr) {
showNotification('Failed to copy to clipboard. Please try again.', 'error');
}
});
}
function copyToClipboard(text) {
navigator.clipboard.writeText(text)
.then(() => {
showNotification('Copied to clipboard!');
})
.catch(err => {
showNotification('Failed to copy to clipboard', 'error');
});
}
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 saveConfiguration() {
try {
localStorage.setItem('bricksfusion-button-reveal-config', JSON.stringify(brConfig));
} catch (e) {}
}
function loadConfiguration() {
try {
const saved = localStorage.getItem('bricksfusion-button-reveal-config');
if (saved) {
const savedConfig = JSON.parse(saved);
Object.assign(brConfig, savedConfig);
updateUIFromConfig();
}
} catch (e) {}
}
initializeUI();
});
</script>
</body>
</html>
Button Reveal
Transforms a button into a fullscreen video player with smooth animations. Click the button and watch it expand into a video. Perfect for video showcases, product demos, and hero sections.
Video
Link to your video file. Supports direct video links, YouTube, and Vimeo. Just paste the URL and it works automatically.
Default: Demo video
Display video controls like play, pause, and volume. Turn off for autoplay videos without controls.
Default: Off
How the video fills the space. Cover fills the entire area and crops if needed. Contain shows the full video with possible black bars.
Default: Cover
Size of the expanded video. Small is 300x300, medium is 600x400, large is 800x600, and full takes the entire screen with margins.
Default: Full
Animation
Overall speed multiplier for all animations. Higher values make everything faster, lower values slow it down.
Default: 1.0
How long the button takes to expand into the video. Shorter is snappy, longer is smooth and cinematic.
Default: 1.0 seconds
Duration specifically for mobile devices. Usually faster than desktop for better mobile experience.
Default: 0.8 seconds
The motion curve of the animation. Power2 is smooth and natural, elastic bounces slightly. Add .in, .out, or .inOut after the name.
Default: power2.inOut
Appearance
Roundness of the video container corners. 0 is sharp corners, higher values create more rounded edges.
Default: 20
Amount of blur applied to the background when the video is open. Higher values create a stronger focus effect.
Default: 10
Darkness of the background overlay. 0 is transparent, 1 is completely black. Use around 0.5-0.7 for best results.
Default: 0.5
Mobile
Scales down the video size on mobile devices. 0.9 means 90% of the chosen size. Helps fit videos better on small screens.
Default: 0.9
Performance
This element requires GSAP library for smooth animations. Video playback depends on user's device capabilities. Use optimized video files for best performance. The effect automatically adjusts for mobile devices.