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">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Video Background 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: 800px;
padding: 1.5rem;
}
.instructions-grid {
display: grid;
grid-template-columns: 1fr;
gap: 1.5rem;
}
.how-to-use ol {
padding-left: 1.5rem;
}
.how-to-use li {
margin-bottom: 0.75rem;
font-size: var(--text-xs);
color: var(--text-secondary);
line-height: 1.6;
}
.how-to-use strong {
color: var(--text-primary);
font-weight: 600;
}
.how-to-use code {
background-color: rgba(50, 50, 50, 0.5);
padding: 0.2rem 0.5rem;
border-radius: 4px;
font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
font-size: var(--text-xs);
color: #ff8c51;
}
.content {
display: grid;
grid-template-columns: 1fr 500px;
gap: 2rem;
align-items: start;
}
.preview-section {
position: sticky;
top: 2rem;
}
.controls-section {
max-width: 500px;
}
.card {
background-color: var(--card-bg);
border-radius: var(--card-radius);
box-shadow: var(--shadow);
overflow: hidden;
margin-bottom: 1.5rem;
border: 1px solid var(--border);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 6px 24px rgba(0, 0, 0, 0.4);
}
.preview-container {
height: 400px;
width: 100%;
position: relative;
overflow: hidden;
border-radius: var(--card-radius);
background-color: #000000;
border: 1px solid var(--border);
box-shadow: var(--shadow);
}
.preview-content {
color: white;
text-align: center;
font-weight: bold;
font-size: var(--text-s);
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 2;
}
.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;
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);
}
input[type="text"],
input[type="number"],
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);
}
input[type="text"]:focus,
input[type="number"]:focus,
select:focus {
border-color: var(--accent);
box-shadow: 0 0 0 2px rgba(239, 96, 19, 0.2);
}
.color-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);
margin-bottom: 1rem;
}
.color-row:hover {
border-color: var(--accent);
box-shadow: 0 0 0 1px rgba(239, 96, 19, 0.1);
}
.color-picker-container {
position: relative;
width: 40px;
height: 40px;
border-radius: 8px;
overflow: hidden;
border: 2px solid var(--border);
cursor: pointer;
transition: var(--transition);
flex-shrink: 0;
background: linear-gradient(45deg, #f0f0f0 25%, transparent 25%),
linear-gradient(-45deg, #f0f0f0 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, #f0f0f0 75%),
linear-gradient(-45deg, transparent 75%, #f0f0f0 75%);
background-size: 8px 8px;
background-position: 0 0, 0 4px, 4px -4px, -4px 0px;
}
.color-picker-container::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: var(--current-color, #ef6013);
border-radius: 6px;
z-index: 1;
}
.color-picker-container:hover {
border-color: var(--accent);
transform: scale(1.05);
}
input[type="color"] {
position: absolute;
top: -2px;
left: -2px;
width: calc(100% + 4px);
height: calc(100% + 4px);
border: none;
cursor: pointer;
background: transparent;
z-index: 2;
opacity: 0;
}
.color-input-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.color-label {
font-size: 10px;
font-weight: 500;
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 0.5px;
margin-left: 0.25rem;
}
.color-input {
padding: 0.5rem 0.75rem;
background-color: rgba(0, 0, 0, 0.3);
border: 1px solid var(--border);
border-radius: 6px;
color: var(--text-primary);
font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
font-size: var(--text-xs);
transition: var(--transition);
min-width: 0;
}
.color-input:focus {
border-color: var(--accent);
box-shadow: 0 0 0 1px rgba(239, 96, 19, 0.2);
outline: none;
}
.color-input.invalid {
border-color: var(--danger);
box-shadow: 0 0 0 1px rgba(220, 53, 69, 0.2);
}
.hex-input,
.hsl-input {
width: 100%;
}
.color-input-group:nth-child(2) {
flex: 0.3;
}
.color-input-group:nth-child(3) {
flex: 0.7;
}
.switch-container {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 1.2rem;
padding: 0.5rem 0;
}
.switch-label {
font-size: var(--text-xs);
font-weight: 500;
letter-spacing: 0.2px;
display: flex;
align-items: center;
gap: 0.5rem;
}
.switch {
position: relative;
display: inline-block;
width: 52px;
height: 28px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: var(--track);
transition: var(--transition);
border-radius: 34px;
}
.slider:before {
position: absolute;
content: "";
height: 20px;
width: 20px;
left: 4px;
bottom: 4px;
background-color: #f2f2f7;
transition: var(--transition);
border-radius: 50%;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}
input:checked + .slider {
background-color: var(--accent);
}
input:checked + .slider:before {
transform: translateX(24px);
}
.video-control {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
z-index: 5;
background-color: rgba(30, 30, 30, 0.6);
color: white;
border: none;
border-radius: 50%;
width: 50px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: var(--transition);
font-size: 24px;
}
.video-control:hover {
background-color: var(--accent);
transform: translateX(-50%) scale(1.1);
}
.video-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
pointer-events: none;
background: rgba(0, 0, 0, 0.3);
transition: var(--transition);
}
.video-controls-container {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
padding: 8px 0;
box-sizing: border-box;
display: flex;
align-items: center;
z-index: 5;
background: rgba(0, 0, 0, 0.2);
transition: opacity 0.3s ease;
opacity: 0;
}
.video-progress-container {
flex: 1;
margin: 0 12px;
position: relative;
height: 3px;
background-color: rgba(255, 255, 255, 0.2);
border-radius: 3px;
overflow: hidden;
cursor: pointer;
}
.video-progress-container:hover {
height: 5px;
}
.video-progress-indicator {
width: 0%;
height: 100%;
background-color: var(--accent);
transition: width 0.1s linear;
position: absolute;
left: 0;
top: 0;
}
.video-time-display {
margin-right: 12px;
font-size: var(--text-xs);
color: white;
font-family: Arial, sans-serif;
font-weight: 500;
letter-spacing: 0.5px;
}
.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;
}
.filter-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
}
.url-list {
margin-top: 1rem;
}
.url-item {
display: flex;
align-items: center;
gap: 1rem;
padding: 1rem;
background-color: rgba(30, 30, 30, 0.5);
border: 1px solid var(--border);
border-radius: var(--input-radius);
margin-bottom: 0.75rem;
transition: var(--transition);
}
.url-item:hover {
border-color: var(--accent);
}
.url-number {
font-size: var(--text-xs);
font-weight: 600;
color: var(--accent);
min-width: 60px;
}
.url-input-wrapper {
flex: 1;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.url-input-wrapper input {
margin-bottom: 0;
}
.url-attribute-badge {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.4rem 0.8rem;
background-color: rgba(239, 96, 19, 0.15);
border: 1px solid var(--accent);
border-radius: 6px;
font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
font-size: 11px;
color: var(--accent);
cursor: pointer;
transition: var(--transition);
user-select: all;
}
.url-attribute-badge:hover {
background-color: rgba(239, 96, 19, 0.25);
transform: translateY(-1px);
}
.url-attribute-badge i {
font-size: 10px;
}
.url-remove-btn {
padding: 0.5rem;
background-color: transparent;
color: var(--text-secondary);
border: 1px solid var(--border);
border-radius: 6px;
cursor: pointer;
transition: var(--transition);
display: flex;
align-items: center;
justify-content: center;
min-width: 36px;
height: 36px;
}
.url-remove-btn:hover {
color: var(--danger);
border-color: var(--danger);
background-color: rgba(220, 53, 69, 0.1);
}
.add-url-btn {
width: 100%;
padding: 0.75rem;
background-color: transparent;
color: var(--text-secondary);
border: 2px dashed var(--border);
border-radius: var(--input-radius);
cursor: pointer;
font-size: var(--text-xs);
font-weight: 500;
transition: var(--transition);
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
margin-top: 0.75rem;
}
.add-url-btn:hover {
border-color: var(--accent);
color: var(--accent);
background-color: rgba(239, 96, 19, 0.05);
}
.info-box {
padding: 1rem;
background-color: rgba(239, 96, 19, 0.1);
border: 1px solid rgba(239, 96, 19, 0.3);
border-radius: var(--input-radius);
margin-bottom: 1rem;
}
.info-box-title {
font-size: var(--text-xs);
font-weight: 600;
color: var(--accent);
margin-bottom: 0.5rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.info-box-content {
font-size: var(--text-xs);
color: var(--text-secondary);
line-height: 1.6;
}
@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;
}
.filter-grid {
grid-template-columns: 1fr;
}
.url-item {
flex-direction: column;
align-items: stretch;
}
.url-number {
min-width: auto;
}
}
@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/corebackground/" class="breadcrumb-item">Core Backgrounds</a>
<span class="breadcrumb-separator">›</span>
<span class="breadcrumb-item active">Video Background</span>
</nav>
<div class="action-buttons">
<div class="data-attribute-display" id="quick-attribute" title="Click to copy base attribute">
data-video-background
</div>
<button class="action-btn primary" id="download-config" title="Copy JavaScript code (Ctrl+D)">
<span>📋</span>
Copy JS
</button>
<button class="action-btn" id="copy-full-section" title="Copy complete section JSON for Bricks Builder (Ctrl+S)">
<span>📦</span>
Copy Full Section
</button>
</div>
</div>
<div class="container">
<div class="page-header">
<h1 class="page-title">Video Background</h1>
<p class="page-subtitle">Interactive video backgrounds 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 video background using the controls below</li>
<li>Add your <strong>default video URL</strong> (this will be used for all divs without specific attributes)</li>
<li><strong>Optional:</strong> Add additional video URLs - each will generate a unique attribute like <code>data-video-1</code>, <code>data-video-2</code>, etc.</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 and paste the JavaScript code</li>
<li>For each section where you want a video:
<ul>
<li>Go to <strong>Section → Style → Attributes</strong></li>
<li>To use the default video: add only <code>data-video-background</code></li>
<li>To use a specific video: add the corresponding attribute (e.g., <code>data-video-1</code>, <code>data-video-2</code>)</li>
<li>Leave the attribute value field empty</li>
</ul>
</li>
</ol>
</div>
</div>
</div>
</div>
</div>
<div class="content">
<section class="preview-section">
<div class="preview-container" id="video-preview" data-video-background="true">
<div class="video-overlay" id="video-overlay"></div>
<button class="video-control" id="play-toggle" data-video-control="toggle"><i class="fa-solid fa-play"></i></button>
<div class="preview-content">Interactive Video Preview</div>
</div>
</section>
<section class="controls-section">
<div class="card">
<div class="card-heading">
Video URLs
<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">
Default Video URL
<span class="help-tooltip" title="This URL will be used for all divs with only data-video-background attribute">ℹ</span>
</span>
</div>
<input type="text" id="video-url" placeholder="Enter your default video URL" value="https://cdn.pixabay.com/video/2024/03/13/204006-923133925_large.mp4">
</div>
<div class="info-box">
<div class="info-box-title">
<i class="fa-solid fa-lightbulb"></i>
Multiple Videos
</div>
<div class="info-box-content">
Add additional video URLs below. Each one gets a unique attribute you can copy and use in Bricks Builder.
</div>
</div>
<div class="url-list" id="url-list"></div>
<button class="add-url-btn" id="add-url-btn">
<i class="fa-solid fa-plus"></i>
Add Additional Video URL
</button>
<div class="switch-container">
<span class="switch-label">
Autoplay on Load
<span class="help-tooltip" title="Start playing video automatically when page loads">ℹ</span>
</span>
<label class="switch">
<input type="checkbox" id="autoplay">
<span class="slider"></span>
</label>
</div>
<div class="switch-container">
<span class="switch-label">
Loop Video
<span class="help-tooltip" title="Restart video when it reaches the end">ℹ</span>
</span>
<label class="switch">
<input type="checkbox" id="loop-video" checked>
<span class="slider"></span>
</label>
</div>
</div>
</div>
<div class="card">
<div class="card-heading">
Overlay Settings
<div class="card-actions">
<button class="card-action-btn" id="reset-overlay" title="Reset Overlay Settings">↺</button>
</div>
</div>
<div class="card-content">
<div class="control-group">
<div class="control-label">
<span class="label-text">Overlay Color</span>
</div>
<div class="color-row">
<div class="color-picker-container">
<input type="color" id="overlay-color" value="#000000">
</div>
<div class="color-input-group">
<span class="color-label">HEX</span>
<input type="text" class="color-input hex-input" id="overlay-color-hex" value="#000000" placeholder="#FFFFFF">
</div>
<div class="color-input-group">
<span class="color-label">HSL</span>
<input type="text" class="color-input hsl-input" id="overlay-color-hsl" placeholder="hsl(0, 100%, 50%)">
</div>
</div>
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">
Overlay Opacity
<span class="help-tooltip" title="Controls how transparent the overlay is">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="overlay-opacity-value">0.3</span></span>
<button class="reset-btn" onclick="resetParameter('overlay-opacity', 0.3)">↺</button>
</div>
</div>
<input type="range" id="overlay-opacity" min="0" max="1" step="0.05" value="0.3">
</div>
</div>
</div>
<div class="card">
<div class="card-heading">
Video Filters
<div class="card-actions">
<button class="card-action-btn" id="reset-filters" title="Reset All Filters">↺</button>
</div>
</div>
<div class="card-content">
<div class="filter-grid">
<div class="control-group">
<div class="control-label">
<span class="label-text">
Brightness
<span class="help-tooltip" title="Controls video brightness">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="brightness-value">100</span>%</span>
<button class="reset-btn" onclick="resetParameter('brightness', 100)">↺</button>
</div>
</div>
<input type="range" id="brightness" min="0" max="200" step="5" value="100">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">
Contrast
<span class="help-tooltip" title="Controls video contrast">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="contrast-value">100</span>%</span>
<button class="reset-btn" onclick="resetParameter('contrast', 100)">↺</button>
</div>
</div>
<input type="range" id="contrast" min="0" max="200" step="5" value="100">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">
Saturation
<span class="help-tooltip" title="Controls video color saturation">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="saturation-value">100</span>%</span>
<button class="reset-btn" onclick="resetParameter('saturation', 100)">↺</button>
</div>
</div>
<input type="range" id="saturation" min="0" max="200" step="5" value="100">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">
Blur
<span class="help-tooltip" title="Applies blur effect to video">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="blur-value">0</span>px</span>
<button class="reset-btn" onclick="resetParameter('blur', 0)">↺</button>
</div>
</div>
<input type="range" id="blur" min="0" max="20" step="0.5" value="0">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">
Hue Rotate
<span class="help-tooltip" title="Rotates video colors">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="hue-rotate-value">0</span>°</span>
<button class="reset-btn" onclick="resetParameter('hue-rotate', 0)">↺</button>
</div>
</div>
<input type="range" id="hue-rotate" min="0" max="360" step="5" value="0">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">
Sepia
<span class="help-tooltip" title="Applies sepia effect">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="sepia-value">0</span>%</span>
<button class="reset-btn" onclick="resetParameter('sepia', 0)">↺</button>
</div>
</div>
<input type="range" id="sepia" min="0" max="100" step="5" value="0">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">
Grayscale
<span class="help-tooltip" title="Converts video to grayscale">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="grayscale-value">0</span>%</span>
<button class="reset-btn" onclick="resetParameter('grayscale', 0)">↺</button>
</div>
</div>
<input type="range" id="grayscale" min="0" max="100" step="5" value="0">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">
Invert
<span class="help-tooltip" title="Inverts video colors">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="invert-value">0</span>%</span>
<button class="reset-btn" onclick="resetParameter('invert', 0)">↺</button>
</div>
</div>
<input type="range" id="invert" min="0" max="100" step="5" value="0">
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-heading">
Control Settings
<div class="card-actions">
<button class="card-action-btn" id="reset-controls" title="Reset Control Settings">↺</button>
</div>
</div>
<div class="card-content">
<div class="switch-container">
<span class="switch-label">
Show Controls
<span class="help-tooltip" title="Display play/pause button">ℹ</span>
</span>
<label class="switch">
<input type="checkbox" id="show-controls" checked>
<span class="slider"></span>
</label>
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">Play Button Style</span>
</div>
<select id="control-style">
<option value="circle" selected>Circle</option>
<option value="minimal">Minimal</option>
<option value="square">Square</option>
</select>
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">Button Position</span>
</div>
<select id="button-position">
<option value="bottom-center" selected>Bottom Center</option>
<option value="center-center">Center</option>
<option value="top-center">Top Center</option>
<option value="bottom-left">Bottom Left</option>
<option value="bottom-right">Bottom Right</option>
<option value="top-left">Top Left</option>
<option value="top-right">Top Right</option>
</select>
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">Button Color</span>
</div>
<div class="color-row">
<div class="color-picker-container">
<input type="color" id="button-color" value="#ef6013">
</div>
<div class="color-input-group">
<span class="color-label">HEX</span>
<input type="text" class="color-input hex-input" id="button-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="button-color-hsl" placeholder="hsl(0, 100%, 50%)">
</div>
</div>
</div>
<div class="switch-container">
<span class="switch-label">
Show Progress Bar
<span class="help-tooltip" title="Display video progress indicator">ℹ</span>
</span>
<label class="switch">
<input type="checkbox" id="show-progress">
<span class="slider"></span>
</label>
</div>
<div class="switch-container">
<span class="switch-label">
Show Time Remaining
<span class="help-tooltip" title="Display current time and duration">ℹ</span>
</span>
<label class="switch">
<input type="checkbox" id="show-time">
<span class="slider"></span>
</label>
</div>
</div>
</div>
<div class="card">
<div class="card-heading">
Playback Options
<div class="card-actions">
<button class="card-action-btn" id="reset-playback" title="Reset Playback Settings">↺</button>
</div>
</div>
<div class="card-content">
<div class="control-group">
<div class="control-label">
<span class="label-text">
Playback Speed
<span class="help-tooltip" title="Speed multiplier for video playback">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="playback-speed-value">1</span>x</span>
<button class="reset-btn" onclick="resetParameter('playback-speed', 1)">↺</button>
</div>
</div>
<input type="range" id="playback-speed" min="0.25" max="2" step="0.25" value="1">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">
Start Time (seconds)
<span class="help-tooltip" title="Time in seconds where video should start">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="start-time-value">0</span>s</span>
<button class="reset-btn" onclick="resetParameter('start-time', 0)">↺</button>
</div>
</div>
<input type="number" id="start-time" min="0" step="0.5" value="0">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">
Fade In Duration (seconds)
<span class="help-tooltip" title="Time for video to fade in when starting">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="fade-in-value">0</span>s</span>
<button class="reset-btn" onclick="resetParameter('fade-in', 0)">↺</button>
</div>
</div>
<input type="number" id="fade-in" min="0" max="5" step="0.1" value="0">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">
Fade Out Duration (seconds)
<span class="help-tooltip" title="Time for video to fade out when ending">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="fade-out-value">0</span>s</span>
<button class="reset-btn" onclick="resetParameter('fade-out', 0)">↺</button>
</div>
</div>
<input type="number" id="fade-out" min="0" max="5" step="0.1" value="0">
</div>
<div class="switch-container">
<span class="switch-label">
Pause When Out of View
<span class="help-tooltip" title="Pause video when not visible on screen">ℹ</span>
</span>
<label class="switch">
<input type="checkbox" id="pause-when-invisible">
<span class="slider"></span>
</label>
</div>
</div>
</div>
</section>
</div>
</div>
<div class="notification" id="notification"></div>
<script>
const VideoUtils = {
hexToRgba(hex, opacity) {
const r = parseInt(hex.slice(1, 3), 16);
const g = parseInt(hex.slice(3, 5), 16);
const b = parseInt(hex.slice(5, 7), 16);
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
},
hexToHsl(hex) {
const r = parseInt(hex.slice(1, 3), 16) / 255;
const g = parseInt(hex.slice(3, 5), 16) / 255;
const b = parseInt(hex.slice(5, 7), 16) / 255;
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
let h, s, l = (max + min) / 2;
if (max === min) {
h = s = 0;
} else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return `hsl(${Math.round(h * 360)}, ${Math.round(s * 100)}%, ${Math.round(l * 100)}%)`;
},
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)}`;
},
isValidHex(hex) {
return /^#[0-9A-F]{6}$/i.test(hex);
},
isValidHsl(hsl) {
return /^hsl\(\s*(\d{1,3})\s*,\s*(\d{1,3})%\s*,\s*(\d{1,3})%\s*\)$/i.test(hsl);
},
applyButtonPosition(button, position) {
let posStyles = {};
switch(position) {
case 'center-center':
posStyles = {
top: '50%',
left: '50%',
bottom: 'auto',
right: 'auto',
transform: 'translate(-50%, -50%)'
};
break;
case 'top-center':
posStyles = {
top: '20px',
left: '50%',
bottom: 'auto',
right: 'auto',
transform: 'translateX(-50%)'
};
break;
case 'bottom-left':
posStyles = {
bottom: '20px',
left: '20px',
top: 'auto',
right: 'auto',
transform: 'none'
};
break;
case 'bottom-right':
posStyles = {
bottom: '20px',
right: '20px',
left: 'auto',
top: 'auto',
transform: 'none'
};
break;
case 'top-left':
posStyles = {
top: '20px',
left: '20px',
bottom: 'auto',
right: 'auto',
transform: 'none'
};
break;
case 'top-right':
posStyles = {
top: '20px',
right: '20px',
left: 'auto',
bottom: 'auto',
transform: 'none'
};
break;
default:
posStyles = {
bottom: '20px',
left: '50%',
top: 'auto',
right: 'auto',
transform: 'translateX(-50%)'
};
}
Object.assign(button.style, posStyles);
},
applyButtonStyle(button, style, color) {
switch(style) {
case 'minimal':
Object.assign(button.style, {
borderRadius: '4px',
width: '36px',
height: '36px',
backgroundColor: color || 'rgba(0, 0, 0, 0.5)'
});
break;
case 'square':
Object.assign(button.style, {
borderRadius: '6px',
width: '46px',
height: '46px',
backgroundColor: color || 'rgba(30, 30, 30, 0.8)'
});
break;
default:
Object.assign(button.style, {
borderRadius: '50%',
width: '46px',
height: '46px',
backgroundColor: color || 'rgba(30, 30, 30, 0.6)'
});
}
},
generateFilter(filters) {
const filterArray = [];
if (filters.brightness !== 100) filterArray.push(`brightness(${filters.brightness}%)`);
if (filters.contrast !== 100) filterArray.push(`contrast(${filters.contrast}%)`);
if (filters.saturation !== 100) filterArray.push(`saturate(${filters.saturation}%)`);
if (filters.blur !== 0) filterArray.push(`blur(${filters.blur}px)`);
if (filters.hueRotate !== 0) filterArray.push(`hue-rotate(${filters.hueRotate}deg)`);
if (filters.sepia !== 0) filterArray.push(`sepia(${filters.sepia}%)`);
if (filters.grayscale !== 0) filterArray.push(`grayscale(${filters.grayscale}%)`);
if (filters.invert !== 0) filterArray.push(`invert(${filters.invert}%)`);
return filterArray.join(' ');
}
};
document.addEventListener('DOMContentLoaded', function() {
let videoConfig = {
videoUrl: 'https://cdn.pixabay.com/video/2024/03/13/204006-923133925_large.mp4',
additionalUrls: [],
autoplay: false,
loopVideo: true,
overlayColor: '#000000',
overlayOpacity: 0.3,
showControls: true,
controlStyle: 'circle',
buttonPosition: 'bottom-center',
buttonColor: '#ef6013',
showProgress: false,
showTime: false,
playbackSpeed: 1,
startTime: 0,
fadeIn: 0,
fadeOut: 0,
pauseWhenInvisible: false,
filters: {
brightness: 100,
contrast: 100,
saturation: 100,
blur: 0,
hueRotate: 0,
sepia: 0,
grayscale: 0,
invert: 0
}
};
const defaultConfig = JSON.parse(JSON.stringify(videoConfig));
let activeVideo = null;
let updateTimer = null;
function addUrlItem(url = '', index = null) {
const urlList = document.getElementById('url-list');
const actualIndex = index !== null ? index : videoConfig.additionalUrls.length + 1;
const urlItem = document.createElement('div');
urlItem.className = 'url-item';
urlItem.dataset.index = actualIndex;
urlItem.innerHTML = `
<div class="url-number">Video ${actualIndex}</div>
<div class="url-input-wrapper">
<input type="text" placeholder="Enter video URL" value="${url}" data-url-index="${actualIndex}">
<div class="url-attribute-badge" title="Click to copy attribute">
<span>data-video-${actualIndex}</span>
<i class="fa-solid fa-copy"></i>
</div>
</div>
<button class="url-remove-btn" title="Remove this URL">
<i class="fa-solid fa-trash"></i>
</button>
`;
urlList.appendChild(urlItem);
const input = urlItem.querySelector('input');
input.addEventListener('input', function() {
const idx = parseInt(this.dataset.urlIndex) - 1;
videoConfig.additionalUrls[idx] = this.value;
});
const badge = urlItem.querySelector('.url-attribute-badge');
badge.addEventListener('click', function() {
const attr = `data-video-${actualIndex}`;
copyToClipboard(attr);
});
const removeBtn = urlItem.querySelector('.url-remove-btn');
removeBtn.addEventListener('click', function() {
const idx = parseInt(urlItem.dataset.index) - 1;
videoConfig.additionalUrls.splice(idx, 1);
reorderUrlItems();
});
if (index === null) {
videoConfig.additionalUrls.push(url);
}
}
function reorderUrlItems() {
const urlList = document.getElementById('url-list');
urlList.innerHTML = '';
videoConfig.additionalUrls.forEach((url, index) => {
addUrlItem(url, index + 1);
});
}
document.getElementById('add-url-btn').addEventListener('click', function() {
addUrlItem();
});
function debounce(func, wait) {
return function executedFunction(...args) {
const later = () => {
clearTimeout(updateTimer);
func(...args);
};
clearTimeout(updateTimer);
updateTimer = setTimeout(later, wait);
};
}
function initVideoBackground() {
const containers = document.querySelectorAll('[data-video-background]:not([data-video-initialized="true"])');
containers.forEach((container) => {
const videoUrl = container.getAttribute('data-video-url') || videoConfig.videoUrl;
const isAutoplay = container.hasAttribute('data-video-autoplay') || videoConfig.autoplay;
const isLoop = container.hasAttribute('data-video-loop') || videoConfig.loopVideo;
const overlayColor = container.getAttribute('data-video-overlay-color') || videoConfig.overlayColor;
const overlayOpacity = container.getAttribute('data-video-overlay-opacity') || videoConfig.overlayOpacity;
const showControls = container.hasAttribute('data-video-show-controls') || videoConfig.showControls;
const controlStyle = container.getAttribute('data-video-control-style') || videoConfig.controlStyle;
const buttonPosition = container.getAttribute('data-video-button-position') || videoConfig.buttonPosition;
const buttonColor = container.getAttribute('data-video-button-color') || videoConfig.buttonColor;
const showProgress = container.hasAttribute('data-video-show-progress') || videoConfig.showProgress;
const showTime = container.hasAttribute('data-video-show-time') || videoConfig.showTime;
const playbackSpeed = parseFloat(container.getAttribute('data-video-playback-speed')) || videoConfig.playbackSpeed;
const startTime = parseFloat(container.getAttribute('data-video-start-time')) || videoConfig.startTime;
const fadeIn = parseFloat(container.getAttribute('data-video-fade-in')) || videoConfig.fadeIn;
const fadeOut = parseFloat(container.getAttribute('data-video-fade-out')) || videoConfig.fadeOut;
const pauseWhenInvisible = container.hasAttribute('data-video-pause-invisible') || videoConfig.pauseWhenInvisible;
const options = {
videoUrl,
autoplay: isAutoplay,
loopVideo: isLoop,
overlayColor,
overlayOpacity,
showControls,
controlStyle,
buttonPosition,
buttonColor,
showProgress,
showTime,
playbackSpeed,
startTime,
fadeIn,
fadeOut,
pauseWhenInvisible,
filters: videoConfig.filters
};
setupVideoBackground(container, options);
container.dataset.videoInitialized = 'true';
if (container.id === 'video-preview') {
activeVideo = { element: container, options };
videoConfig = { ...options, additionalUrls: videoConfig.additionalUrls };
}
});
}
function cleanupContainer(container) {
if (container._videoBackground) {
if (container._videoBackground.resizeHandler) {
window.removeEventListener('resize', container._videoBackground.resizeHandler);
}
if (container._videoBackground.playbackManager && container._videoBackground.playbackManager._viewportObserver) {
container._videoBackground.playbackManager._viewportObserver.disconnect();
}
if (container._videoBackground.videos.preview) {
container._videoBackground.videos.preview.pause();
container._videoBackground.videos.preview.removeAttribute('src');
container._videoBackground.videos.preview.load();
}
if (container._videoBackground.videos.main) {
container._videoBackground.videos.main.pause();
container._videoBackground.videos.main.removeAttribute('src');
container._videoBackground.videos.main.load();
}
}
const existingVideo = container.querySelector('.video-background-wrapper');
if (existingVideo) existingVideo.remove();
const existingOverlay = container.querySelector('.video-background-overlay');
if (existingOverlay) existingOverlay.remove();
const existingToggle = container.querySelector('[data-video-control="toggle"]');
if (existingToggle) existingToggle.remove();
const existingControls = container.querySelector('.video-controls-container');
if (existingControls) existingControls.remove();
}
function setupVideoBackground(container, options) {
if (getComputedStyle(container).position === 'static') {
container.style.position = 'relative';
}
if (parseInt(getComputedStyle(container).height) < 100) {
container.style.minHeight = '200px';
}
cleanupContainer(container);
const overlay = document.createElement('div');
overlay.className = 'video-background-overlay';
Object.assign(overlay.style, {
position: 'absolute',
top: '0',
left: '0',
width: '100%',
height: '100%',
zIndex: '1',
pointerEvents: 'none',
backgroundColor: VideoUtils.hexToRgba(options.overlayColor, options.overlayOpacity),
transition: 'background-color 0.3s ease'
});
container.appendChild(overlay);
let playToggle = null;
if (options.showControls) {
playToggle = document.createElement('button');
playToggle.setAttribute('data-video-control', 'toggle');
Object.assign(playToggle.style, {
position: 'absolute',
zIndex: '5',
color: 'white',
border: 'none',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
cursor: 'pointer',
transition: 'all 0.25s ease',
backgroundColor: options.buttonColor || 'rgba(30, 30, 30, 0.6)',
fontSize: '18px',
overflow: 'hidden'
});
VideoUtils.applyButtonPosition(playToggle, options.buttonPosition);
VideoUtils.applyButtonStyle(playToggle, options.controlStyle, options.buttonColor);
const iconWrapper = document.createElement('div');
iconWrapper.className = 'video-control-icon';
iconWrapper.innerHTML = '<i class="fa-solid fa-play"></i>';
Object.assign(iconWrapper.style, {
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
lineHeight: '1'
});
playToggle.appendChild(iconWrapper);
container.appendChild(playToggle);
}
const videoWrapper = document.createElement('div');
videoWrapper.className = 'video-background-wrapper';
Object.assign(videoWrapper.style, {
position: 'absolute',
top: '0',
left: '0',
width: '100%',
height: '100%',
overflow: 'hidden',
zIndex: '0'
});
const previewVideo = document.createElement('video');
const mainVideo = document.createElement('video');
[previewVideo, mainVideo].forEach(video => {
video.src = options.videoUrl;
video.muted = true;
video.playsInline = true;
video.setAttribute('playsinline', '');
video.playbackRate = options.playbackSpeed;
if (options.startTime > 0) {
video.currentTime = options.startTime;
}
Object.assign(video.style, {
position: 'absolute',
top: '50%',
left: '50%',
minWidth: '101%',
minHeight: '101%',
width: 'auto',
height: 'auto',
transform: 'translate(-50%, -50%)',
objectFit: 'cover',
filter: VideoUtils.generateFilter(options.filters)
});
});
previewVideo.loop = false;
mainVideo.loop = options.loopVideo;
mainVideo.style.display = 'none';
if (options.fadeIn > 0) {
mainVideo.style.opacity = '0';
mainVideo.style.transition = `opacity ${options.fadeIn}s ease-in`;
}
videoWrapper.appendChild(previewVideo);
videoWrapper.appendChild(mainVideo);
container.insertBefore(videoWrapper, container.firstChild);
Array.from(container.children).forEach(child => {
if (child !== videoWrapper && child !== overlay && child !== playToggle) {
if (!child.style.position || child.style.position === 'static') {
child.style.position = 'relative';
}
if (!child.style.zIndex) {
child.style.zIndex = '2';
}
}
});
let progressContainer = null;
let progressIndicator = null;
let timeDisplay = null;
let controlsContainer = null;
if (options.showProgress || options.showTime) {
controlsContainer = document.createElement('div');
controlsContainer.className = 'video-controls-container';
Object.assign(controlsContainer.style, {
position: 'absolute',
bottom: '0',
left: '0',
width: '100%',
padding: '8px 0',
boxSizing: 'border-box',
display: 'flex',
alignItems: 'center',
zIndex: '5',
background: 'rgba(0, 0, 0, 0.2)',
transition: 'opacity 0.3s ease',
opacity: '0'
});
if (options.showProgress) {
progressContainer = document.createElement('div');
progressContainer.className = 'video-progress-container';
Object.assign(progressContainer.style, {
flex: '1',
margin: '0 12px',
position: 'relative',
height: '3px',
backgroundColor: 'rgba(255, 255, 255, 0.2)',
borderRadius: '3px',
overflow: 'hidden',
cursor: 'pointer'
});
progressIndicator = document.createElement('div');
progressIndicator.className = 'video-progress-indicator';
Object.assign(progressIndicator.style, {
width: '0%',
height: '100%',
backgroundColor: options.buttonColor || '#ef6013',
transition: 'width 0.1s linear',
position: 'absolute',
left: '0',
top: '0'
});
progressContainer.appendChild(progressIndicator);
controlsContainer.appendChild(progressContainer);
progressContainer.addEventListener('mouseover', () => {
progressContainer.style.height = '5px';
});
progressContainer.addEventListener('mouseout', () => {
progressContainer.style.height = '3px';
});
progressContainer.addEventListener('click', (e) => {
const rect = progressContainer.getBoundingClientRect();
const pos = (e.clientX - rect.left) / rect.width;
mainVideo.currentTime = pos * mainVideo.duration;
});
}
if (options.showTime) {
timeDisplay = document.createElement('div');
timeDisplay.className = 'video-time-display';
Object.assign(timeDisplay.style, {
marginRight: '12px',
fontSize: '12px',
color: 'white',
fontFamily: 'Arial, sans-serif',
fontWeight: '500',
letterSpacing: '0.5px'
});
timeDisplay.textContent = '0:00 / 0:00';
controlsContainer.appendChild(timeDisplay);
}
container.appendChild(controlsContainer);
}
const playbackManager = createPlaybackManager(container, options, {
previewVideo,
mainVideo,
playToggle,
progressIndicator,
timeDisplay,
controlsContainer
});
playbackManager.init();
const resizeHandler = () => {
playbackManager.resizeVideo(previewVideo);
playbackManager.resizeVideo(mainVideo);
};
window.addEventListener('resize', resizeHandler);
container._videoBackground = {
videos: { preview: previewVideo, main: mainVideo },
overlay: overlay,
playToggle: playToggle,
playbackManager: playbackManager,
progressIndicator: progressIndicator,
timeDisplay: timeDisplay,
controlsContainer: controlsContainer,
resizeHandler: resizeHandler
};
}
function createPlaybackManager(container, options, elements) {
const { previewVideo, mainVideo, playToggle, progressIndicator, timeDisplay, controlsContainer } = elements;
return {
isPlaying: false,
init: function() {
this.setupEventListeners();
this.loadPreview();
if (options.pauseWhenInvisible) {
this._viewportObserver = new IntersectionObserver((entries) => {
const entry = entries[0];
if (entry.isIntersecting) {
if (this._wasPaused === false) {
this.play();
}
} else if (this.isPlaying) {
this._wasPaused = false;
this.pause();
} else {
this._wasPaused = true;
}
}, { threshold: 0.1 });
this._viewportObserver.observe(container);
}
previewVideo.addEventListener('playing', () => {
previewVideo.playbackRate = options.playbackSpeed;
});
mainVideo.addEventListener('playing', () => {
mainVideo.playbackRate = options.playbackSpeed;
});
mainVideo.addEventListener('timeupdate', () => {
if (progressIndicator) {
const progress = (mainVideo.currentTime / mainVideo.duration) * 100;
progressIndicator.style.width = `${progress}%`;
}
if (timeDisplay) {
const formatTime = (seconds) => {
const minutes = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${minutes}:${secs < 10 ? '0' : ''}${secs}`;
};
const currentTime = formatTime(mainVideo.currentTime);
const duration = formatTime(mainVideo.duration);
timeDisplay.textContent = `${currentTime} / ${duration}`;
}
if (controlsContainer && this.isPlaying) {
controlsContainer.style.opacity = '1';
}
});
if (options.autoplay) {
setTimeout(() => this.play(), 100);
}
},
setupEventListeners: function() {
previewVideo.addEventListener('loadedmetadata', () => this.handleLoadedMetadata(previewVideo));
mainVideo.addEventListener('loadedmetadata', () => this.handleLoadedMetadata(mainVideo));
mainVideo.addEventListener('play', () => this.handlePlay());
mainVideo.addEventListener('pause', () => this.handlePause());
if (playToggle) {
playToggle.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
if (this.isPlaying) {
this.pause();
} else {
this.play();
}
});
}
},
loadPreview: function() {
previewVideo.load();
previewVideo.addEventListener('canplay', () => {
previewVideo.currentTime = 0;
this.resizeVideo(previewVideo);
}, { once: true });
mainVideo.load();
},
handleLoadedMetadata: function(video) {
this.resizeVideo(video);
},
handlePlay: function() {
this.isPlaying = true;
previewVideo.style.display = 'none';
mainVideo.style.display = 'block';
mainVideo.playbackRate = options.playbackSpeed;
if (options.fadeIn > 0) {
setTimeout(() => {
mainVideo.style.opacity = '1';
}, 50);
}
this.updatePlayToggle();
if (controlsContainer) {
controlsContainer.style.opacity = '1';
}
},
handlePause: function() {
this.isPlaying = false;
this.updatePlayToggle();
if (controlsContainer) {
controlsContainer.style.opacity = '0';
}
},
play: function() {
if (options.fadeIn > 0) {
mainVideo.style.opacity = '0';
mainVideo.style.transition = `opacity ${options.fadeIn}s ease-in`;
}
mainVideo.playbackRate = options.playbackSpeed;
mainVideo.play().catch(e => {
const clickHandler = () => {
mainVideo.playbackRate = options.playbackSpeed;
mainVideo.play();
document.removeEventListener('click', clickHandler);
};
document.addEventListener('click', clickHandler, { once: true });
});
},
pause: function() {
mainVideo.pause();
},
resizeVideo: function(video) {
if (!video.videoWidth) return;
const { width: cw, height: ch } = container.getBoundingClientRect();
const videoAspect = video.videoWidth / video.videoHeight;
const containerAspect = cw / ch;
let newWidth, newHeight;
if (containerAspect > videoAspect) {
newWidth = cw;
newHeight = cw / videoAspect;
} else {
newHeight = ch;
newWidth = ch * videoAspect;
}
Object.assign(video.style, {
width: `${newWidth}px`,
height: `${newHeight}px`,
top: '50%',
left: '50%',
transform: `translate(-50%, -50%)`
});
},
updatePlayToggle: function() {
if (playToggle) {
const iconElement = playToggle.querySelector('.video-control-icon');
if (iconElement) {
iconElement.innerHTML = this.isPlaying ?
'<i class="fa-solid fa-pause"></i>' :
'<i class="fa-solid fa-play"></i>';
}
}
}
};
}
function updateVideoStyles() {
const container = document.getElementById('video-preview');
if (!container || !container._videoBackground) return;
const { overlay, playToggle, progressIndicator, videos, playbackManager } = container._videoBackground;
if (overlay) {
overlay.style.backgroundColor = VideoUtils.hexToRgba(videoConfig.overlayColor, videoConfig.overlayOpacity);
}
if (playToggle) {
VideoUtils.applyButtonPosition(playToggle, videoConfig.buttonPosition);
VideoUtils.applyButtonStyle(playToggle, videoConfig.controlStyle, videoConfig.buttonColor);
playToggle.style.display = videoConfig.showControls ? 'flex' : 'none';
}
if (progressIndicator) {
progressIndicator.style.backgroundColor = videoConfig.buttonColor;
}
if (videos) {
const filter = VideoUtils.generateFilter(videoConfig.filters);
videos.preview.style.filter = filter;
videos.main.style.filter = filter;
}
const needsControls = videoConfig.showProgress || videoConfig.showTime;
const hasControls = !!container._videoBackground.controlsContainer;
if (needsControls !== hasControls) {
container.dataset.videoInitialized = 'false';
initVideoBackground();
return;
}
if (playbackManager && videos) {
videos.preview.playbackRate = videoConfig.playbackSpeed;
videos.main.playbackRate = videoConfig.playbackSpeed;
videos.main.loop = videoConfig.loopVideo;
if (videoConfig.startTime > 0) {
videos.preview.currentTime = videoConfig.startTime;
videos.main.currentTime = videoConfig.startTime;
}
}
}
const debouncedUpdateVideoPreview = debounce(() => {
const container = document.getElementById('video-preview');
if (!container) return;
const currentUrl = container._videoBackground?.videos?.main?.src;
const newUrl = videoConfig.videoUrl;
if (currentUrl && currentUrl !== newUrl) {
container.dataset.videoInitialized = 'false';
initVideoBackground();
} else {
updateVideoStyles();
}
}, 300);
function updateVideoPreview() {
debouncedUpdateVideoPreview();
}
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() {
const sectionId = generateUniqueId();
const containerId = generateUniqueId();
const divId = generateUniqueId();
const codeId = generateUniqueId();
const attributeId = generateUniqueId();
const jsCode = generateJavaScriptCode();
const fullSectionJSON = {
"content": [
{
"id": sectionId,
"name": "section",
"parent": 0,
"children": [containerId, codeId],
"settings": {
"_height": "500",
"_justifyContent": "center",
"_background": {
"color": {
"hex": "#ffffff"
}
}
}
},
{
"id": containerId,
"name": "container",
"parent": sectionId,
"children": [divId],
"settings": {
"_alignItems": "center"
}
},
{
"id": divId,
"name": "div",
"parent": containerId,
"children": [],
"settings": {
"_width": "400",
"_height": "300",
"_border": {
"radius": {
"top": "15",
"right": "15",
"left": "15",
"bottom": "15"
}
},
"_attributes": [
{
"id": attributeId,
"name": "data-video-background"
}
],
"_overflow": "hidden"
},
"label": "Video Background Div"
},
{
"id": codeId,
"name": "code",
"parent": sectionId,
"children": [],
"settings": {
"javascriptCode": jsCode,
"executeCode": true,
"_display": "none"
},
"label": "Video Background JS"
}
],
"source": "bricksCopiedElements",
"sourceUrl": "https://test.bricksfusion.com",
"version": "2.0.1",
"globalClasses": [],
"globalElements": []
};
return JSON.stringify(fullSectionJSON, null, 2);
}
function generateJavaScriptCode() {
const urlMapping = videoConfig.additionalUrls.map((url, index) => {
return ` 'data-video-${index + 1}': '${url}'`;
}).join(',\n');
return `(function() {
const videoConfig = {
autoplay: ${videoConfig.autoplay},
loopVideo: ${videoConfig.loopVideo},
overlayColor: "${videoConfig.overlayColor}",
overlayOpacity: ${videoConfig.overlayOpacity},
showControls: ${videoConfig.showControls},
controlStyle: "${videoConfig.controlStyle}",
buttonPosition: "${videoConfig.buttonPosition}",
buttonColor: "${videoConfig.buttonColor}",
showProgress: ${videoConfig.showProgress},
showTime: ${videoConfig.showTime},
playbackSpeed: ${videoConfig.playbackSpeed},
startTime: ${videoConfig.startTime},
fadeIn: ${videoConfig.fadeIn},
fadeOut: ${videoConfig.fadeOut},
pauseWhenInvisible: ${videoConfig.pauseWhenInvisible},
filters: ${JSON.stringify(videoConfig.filters)},
defaultVideoUrl: "${videoConfig.videoUrl}",
videoUrlMapping: {
${urlMapping}
}
};
const VideoUtils = {
hexToRgba(hex, opacity) {
const r = parseInt(hex.slice(1, 3), 16);
const g = parseInt(hex.slice(3, 5), 16);
const b = parseInt(hex.slice(5, 7), 16);
return \`rgba(\${r}, \${g}, \${b}, \${opacity})\`;
},
applyButtonPosition(button, position) {
let posStyles = {};
switch(position) {
case 'center-center':
posStyles = {
top: '50%',
left: '50%',
bottom: 'auto',
transform: 'translate(-50%, -50%)'
};
break;
case 'top-center':
posStyles = {
top: '20px',
left: '50%',
bottom: 'auto',
transform: 'translateX(-50%)'
};
break;
case 'bottom-left':
posStyles = {
bottom: '20px',
left: '20px',
top: 'auto',
transform: 'none'
};
break;
case 'bottom-right':
posStyles = {
bottom: '20px',
right: '20px',
left: 'auto',
top: 'auto',
transform: 'none'
};
break;
case 'top-left':
posStyles = {
top: '20px',
left: '20px',
bottom: 'auto',
transform: 'none'
};
break;
case 'top-right':
posStyles = {
top: '20px',
right: '20px',
left: 'auto',
bottom: 'auto',
transform: 'none'
};
break;
default:
posStyles = {
bottom: '20px',
left: '50%',
top: 'auto',
transform: 'translateX(-50%)'
};
}
Object.assign(button.style, posStyles);
},
applyButtonStyle(button, style, color) {
switch(style) {
case 'minimal':
Object.assign(button.style, {
borderRadius: '4px',
width: '36px',
height: '36px',
backgroundColor: color || 'rgba(0, 0, 0, 0.5)'
});
break;
case 'square':
Object.assign(button.style, {
borderRadius: '6px',
width: '46px',
height: '46px',
backgroundColor: color || 'rgba(30, 30, 30, 0.8)'
});
break;
default:
Object.assign(button.style, {
borderRadius: '50%',
width: '46px',
height: '46px',
backgroundColor: color || 'rgba(30, 30, 30, 0.6)'
});
}
},
generateFilter(filters) {
const filterArray = [];
if (filters.brightness !== 100) filterArray.push(\`brightness(\${filters.brightness}%)\`);
if (filters.contrast !== 100) filterArray.push(\`contrast(\${filters.contrast}%)\`);
if (filters.saturation !== 100) filterArray.push(\`saturate(\${filters.saturation}%)\`);
if (filters.blur !== 0) filterArray.push(\`blur(\${filters.blur}px)\`);
if (filters.hueRotate !== 0) filterArray.push(\`hue-rotate(\${filters.hueRotate}deg)\`);
if (filters.sepia !== 0) filterArray.push(\`sepia(\${filters.sepia}%)\`);
if (filters.grayscale !== 0) filterArray.push(\`grayscale(\${filters.grayscale}%)\`);
if (filters.invert !== 0) filterArray.push(\`invert(\${filters.invert}%)\`);
return filterArray.join(' ');
}
};
function initVideoBackground(container) {
if (getComputedStyle(container).position === 'static') {
container.style.position = 'relative';
}
if (parseInt(getComputedStyle(container).height) < 100) {
container.style.minHeight = '200px';
}
const existingVideo = container.querySelector('.video-background-wrapper');
if (existingVideo) {
existingVideo.remove();
}
const existingOverlay = container.querySelector('.video-background-overlay');
if (existingOverlay) {
existingOverlay.remove();
}
const existingToggle = container.querySelector('[data-video-control="toggle"]');
if (existingToggle) {
existingToggle.remove();
}
const existingControls = container.querySelector('.video-controls-container');
if (existingControls) {
existingControls.remove();
}
let videoSrc = videoConfig.defaultVideoUrl;
for (const [attr, url] of Object.entries(videoConfig.videoUrlMapping)) {
if (container.hasAttribute(attr)) {
videoSrc = url;
break;
}
}
const overlay = document.createElement('div');
overlay.className = 'video-background-overlay';
Object.assign(overlay.style, {
position: 'absolute',
top: '0',
left: '0',
width: '100%',
height: '100%',
zIndex: '1',
pointerEvents: 'none',
backgroundColor: VideoUtils.hexToRgba(videoConfig.overlayColor, videoConfig.overlayOpacity),
transition: 'background-color 0.3s ease'
});
container.appendChild(overlay);
let playToggle = null;
if (videoConfig.showControls) {
playToggle = document.createElement('button');
playToggle.setAttribute('data-video-control', 'toggle');
Object.assign(playToggle.style, {
position: 'absolute',
zIndex: '5',
color: 'white',
border: 'none',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
cursor: 'pointer',
transition: 'all 0.25s ease',
backgroundColor: videoConfig.buttonColor || 'rgba(30, 30, 30, 0.6)',
fontSize: '18px',
overflow: 'hidden'
});
VideoUtils.applyButtonPosition(playToggle, videoConfig.buttonPosition);
VideoUtils.applyButtonStyle(playToggle, videoConfig.controlStyle, videoConfig.buttonColor);
const iconWrapper = document.createElement('div');
iconWrapper.className = 'video-control-icon';
iconWrapper.innerHTML = \`
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" fill="currentColor" style="width: 20px; height: 20px;">
<path d="M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80V432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z"/>
</svg>
\`;
Object.assign(iconWrapper.style, {
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
lineHeight: '1'
});
playToggle.appendChild(iconWrapper);
container.appendChild(playToggle);
}
const videoWrapper = document.createElement('div');
videoWrapper.className = 'video-background-wrapper';
Object.assign(videoWrapper.style, {
position: 'absolute',
top: '0',
left: '0',
width: '100%',
height: '100%',
overflow: 'hidden',
zIndex: '0'
});
const previewVideo = document.createElement('video');
const mainVideo = document.createElement('video');
[previewVideo, mainVideo].forEach(video => {
video.src = videoSrc;
video.muted = true;
video.playsInline = true;
video.setAttribute('playsinline', '');
video.playbackRate = videoConfig.playbackSpeed;
if (videoConfig.startTime > 0) {
video.currentTime = videoConfig.startTime;
}
Object.assign(video.style, {
position: 'absolute',
top: '50%',
left: '50%',
minWidth: '101%',
minHeight: '101%',
width: 'auto',
height: 'auto',
transform: 'translate(-50%, -50%)',
objectFit: 'cover',
filter: VideoUtils.generateFilter(videoConfig.filters)
});
});
previewVideo.loop = false;
mainVideo.loop = videoConfig.loopVideo;
mainVideo.style.display = 'none';
if (videoConfig.fadeIn > 0) {
mainVideo.style.opacity = '0';
mainVideo.style.transition = \`opacity \${videoConfig.fadeIn}s ease-in\`;
}
videoWrapper.appendChild(previewVideo);
videoWrapper.appendChild(mainVideo);
container.insertBefore(videoWrapper, container.firstChild);
Array.from(container.children).forEach(child => {
if (child !== videoWrapper && child !== overlay && child !== playToggle) {
if (!child.style.position || child.style.position === 'static') {
child.style.position = 'relative';
}
if (!child.style.zIndex) {
child.style.zIndex = '2';
}
}
});
let progressContainer = null;
let progressIndicator = null;
let timeDisplay = null;
let controlsContainer = null;
if (videoConfig.showProgress || videoConfig.showTime) {
controlsContainer = document.createElement('div');
controlsContainer.className = 'video-controls-container';
Object.assign(controlsContainer.style, {
position: 'absolute',
bottom: '0',
left: '0',
width: '100%',
padding: '8px 0',
boxSizing: 'border-box',
display: 'flex',
alignItems: 'center',
zIndex: '5',
background: 'rgba(0, 0, 0, 0.2)',
transition: 'opacity 0.3s ease',
opacity: '0'
});
if (videoConfig.showProgress) {
progressContainer = document.createElement('div');
progressContainer.className = 'video-progress-container';
Object.assign(progressContainer.style, {
flex: '1',
margin: '0 12px',
position: 'relative',
height: '3px',
backgroundColor: 'rgba(255, 255, 255, 0.2)',
borderRadius: '3px',
overflow: 'hidden',
cursor: 'pointer'
});
progressIndicator = document.createElement('div');
progressIndicator.className = 'video-progress-indicator';
Object.assign(progressIndicator.style, {
width: '0%',
height: '100%',
backgroundColor: videoConfig.buttonColor || '#ef6013',
transition: 'width 0.1s linear',
position: 'absolute',
left: '0',
top: '0'
});
progressContainer.appendChild(progressIndicator);
controlsContainer.appendChild(progressContainer);
progressContainer.addEventListener('mouseover', () => {
progressContainer.style.height = '5px';
});
progressContainer.addEventListener('mouseout', () => {
progressContainer.style.height = '3px';
});
progressContainer.addEventListener('click', (e) => {
const rect = progressContainer.getBoundingClientRect();
const pos = (e.clientX - rect.left) / rect.width;
mainVideo.currentTime = pos * mainVideo.duration;
});
}
if (videoConfig.showTime) {
timeDisplay = document.createElement('div');
timeDisplay.className = 'video-time-display';
Object.assign(timeDisplay.style, {
marginRight: '12px',
fontSize: '12px',
color: 'white',
fontFamily: 'Arial, sans-serif',
fontWeight: '500',
letterSpacing: '0.5px'
});
timeDisplay.textContent = '0:00 / 0:00';
controlsContainer.appendChild(timeDisplay);
}
container.appendChild(controlsContainer);
}
const playbackManager = {
isPlaying: false,
init: function() {
this.setupEventListeners();
this.loadPreview();
if (videoConfig.pauseWhenInvisible) {
this._viewportObserver = new IntersectionObserver((entries) => {
const entry = entries[0];
if (entry.isIntersecting) {
if (this._wasPaused === false) {
this.play();
}
} else if (this.isPlaying) {
this._wasPaused = false;
this.pause();
} else {
this._wasPaused = true;
}
}, { threshold: 0.1 });
this._viewportObserver.observe(container);
}
previewVideo.addEventListener('playing', () => {
previewVideo.playbackRate = videoConfig.playbackSpeed;
});
mainVideo.addEventListener('playing', () => {
mainVideo.playbackRate = videoConfig.playbackSpeed;
});
mainVideo.addEventListener('timeupdate', () => {
if (progressIndicator) {
const progress = (mainVideo.currentTime / mainVideo.duration) * 100;
progressIndicator.style.width = \`\${progress}%\`;
}
if (timeDisplay) {
const formatTime = (seconds) => {
const minutes = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return \`\${minutes}:\${secs < 10 ? '0' : ''}\${secs}\`;
};
const currentTime = formatTime(mainVideo.currentTime);
const duration = formatTime(mainVideo.duration);
timeDisplay.textContent = \`\${currentTime} / \${duration}\`;
}
if (controlsContainer && this.isPlaying) {
controlsContainer.style.opacity = '1';
}
});
if (videoConfig.autoplay) {
setTimeout(() => this.play(), 100);
}
},
setupEventListeners: function() {
previewVideo.addEventListener('loadedmetadata', () => this.handleLoadedMetadata(previewVideo));
mainVideo.addEventListener('loadedmetadata', () => this.handleLoadedMetadata(mainVideo));
mainVideo.addEventListener('play', () => this.handlePlay());
mainVideo.addEventListener('pause', () => this.handlePause());
if (playToggle) {
playToggle.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
if (this.isPlaying) {
this.pause();
} else {
this.play();
}
});
}
},
loadPreview: function() {
previewVideo.load();
previewVideo.addEventListener('canplay', () => {
previewVideo.currentTime = 0;
this.resizeVideo(previewVideo);
}, { once: true });
mainVideo.load();
},
handleLoadedMetadata: function(video) {
this.resizeVideo(video);
},
handlePlay: function() {
this.isPlaying = true;
previewVideo.style.display = 'none';
mainVideo.style.display = 'block';
mainVideo.playbackRate = videoConfig.playbackSpeed;
if (videoConfig.fadeIn > 0) {
setTimeout(() => {
mainVideo.style.opacity = '1';
}, 50);
}
this.updatePlayToggle();
if (controlsContainer) {
controlsContainer.style.opacity = '1';
}
},
handlePause: function() {
this.isPlaying = false;
this.updatePlayToggle();
if (controlsContainer) {
controlsContainer.style.opacity = '0';
}
},
play: function() {
if (videoConfig.fadeIn > 0) {
mainVideo.style.opacity = '0';
mainVideo.style.transition = \`opacity \${videoConfig.fadeIn}s ease-in\`;
}
mainVideo.playbackRate = videoConfig.playbackSpeed;
mainVideo.play().catch(e => {
const clickHandler = () => {
mainVideo.playbackRate = videoConfig.playbackSpeed;
mainVideo.play();
document.removeEventListener('click', clickHandler);
};
document.addEventListener('click', clickHandler, { once: true });
});
},
pause: function() {
mainVideo.pause();
},
resizeVideo: function(video) {
if (!video.videoWidth) return;
const { width: cw, height: ch } = container.getBoundingClientRect();
const videoAspect = video.videoWidth / video.videoHeight;
const containerAspect = cw / ch;
let newWidth, newHeight;
if (containerAspect > videoAspect) {
newWidth = cw;
newHeight = cw / videoAspect;
} else {
newHeight = ch;
newWidth = ch * videoAspect;
}
Object.assign(video.style, {
width: \`\${newWidth}px\`,
height: \`\${newHeight}px\`,
top: '50%',
left: '50%',
transform: \`translate(-50%, -50%)\`
});
},
updatePlayToggle: function() {
if (playToggle) {
const iconElement = playToggle.querySelector('.video-control-icon');
if (iconElement) {
if (this.isPlaying) {
iconElement.innerHTML = \`
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512" fill="currentColor" style="width: 20px; height: 20px;">
<path d="M48 64C21.5 64 0 85.5 0 112V400c0 26.5 21.5 48 48 48H80c26.5 0 48-21.5 48-48V112c0-26.5-21.5-48-48-48H48zm192 0c-26.5 0-48 21.5-48 48V400c0 26.5 21.5 48 48 48h32c26.5 0 48-21.5 48-48V112c0-26.5-21.5-48-48-48H240z"/>
</svg>
\`;
} else {
iconElement.innerHTML = \`
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" fill="currentColor" style="width: 20px; height: 20px;">
<path d="M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80V432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z"/>
</svg>
\`;
}
}
}
}
};
playbackManager.init();
const resizeHandler = () => {
playbackManager.resizeVideo(previewVideo);
playbackManager.resizeVideo(mainVideo);
};
window.addEventListener('resize', resizeHandler);
container._videoBackground = {
videos: { preview: previewVideo, main: mainVideo },
overlay: overlay,
playToggle: playToggle,
playbackManager: playbackManager,
resizeHandler: resizeHandler
};
}
function initAllVideoBackgrounds() {
const selector = '[data-video-background]' +
Object.keys(videoConfig.videoUrlMapping)
.map(attr => \`, [\${attr}]\`)
.join('');
document.querySelectorAll(selector).forEach(initVideoBackground);
}
const initWithDelay = () => {
setTimeout(initAllVideoBackgrounds, 100);
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initWithDelay);
} else {
initWithDelay();
}
window.addEventListener('load', initAllVideoBackgrounds);
const observer = new MutationObserver((mutations) => {
let shouldInit = false;
mutations.forEach((mutation) => {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach(node => {
if (node.nodeType === 1) {
if (node.hasAttribute) {
if (node.hasAttribute('data-video-background') ||
Object.keys(videoConfig.videoUrlMapping).some(attr => node.hasAttribute(attr))) {
shouldInit = true;
}
} else if (node.querySelectorAll) {
const selector = '[data-video-background]' +
Object.keys(videoConfig.videoUrlMapping)
.map(attr => \`, [\${attr}]\`)
.join('');
const videoElements = node.querySelectorAll(selector);
if (videoElements.length > 0) {
shouldInit = true;
}
}
}
});
} else if (mutation.type === 'attributes') {
const attrName = mutation.attributeName;
if (attrName === 'data-video-background' ||
Object.keys(videoConfig.videoUrlMapping).includes(attrName)) {
shouldInit = true;
}
}
});
if (shouldInit) {
setTimeout(initAllVideoBackgrounds, 100);
}
});
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['data-video-background', ...Object.keys(videoConfig.videoUrlMapping)]
});
})();`;
}
function copyJsToClipboard() {
const jsCode = generateJavaScriptCode();
navigator.clipboard.writeText(jsCode)
.then(() => {
showNotification('JavaScript code copied to clipboard!');
})
.catch(err => {
try {
const textArea = document.createElement('textarea');
textArea.value = jsCode;
textArea.style.position = 'fixed';
textArea.style.opacity = '0';
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
showNotification('JavaScript code copied to clipboard!');
} catch (fallbackErr) {
showNotification('Failed to copy to clipboard. Please try again.', 'error');
}
});
}
function copyFullSectionToClipboard() {
const sectionJSON = generateFullSectionJSON();
navigator.clipboard.writeText(sectionJSON)
.then(() => {
showNotification('Full section JSON copied to clipboard!');
})
.catch(err => {
try {
const textArea = document.createElement('textarea');
textArea.value = sectionJSON;
textArea.style.position = 'fixed';
textArea.style.opacity = '0';
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
showNotification('Full section JSON copied to clipboard!');
} catch (fallbackErr) {
showNotification('Failed to copy to clipboard. Please try again.', 'error');
}
});
}
function copyToClipboard(text) {
navigator.clipboard.writeText(text)
.then(() => {
showNotification('Copied to clipboard!');
})
.catch(err => {
showNotification('Failed to copy to clipboard', 'error');
});
}
window.resetParameter = function(parameterId, defaultValue) {
const element = document.getElementById(parameterId);
if (element) {
element.value = defaultValue;
const valueElement = document.getElementById(`${parameterId}-value`);
if (valueElement) {
valueElement.textContent = defaultValue;
}
switch (parameterId) {
case 'overlay-opacity':
videoConfig.overlayOpacity = defaultValue;
break;
case 'playback-speed':
videoConfig.playbackSpeed = defaultValue;
break;
case 'start-time':
videoConfig.startTime = defaultValue;
break;
case 'fade-in':
videoConfig.fadeIn = defaultValue;
break;
case 'fade-out':
videoConfig.fadeOut = defaultValue;
break;
case 'brightness':
videoConfig.filters.brightness = defaultValue;
break;
case 'contrast':
videoConfig.filters.contrast = defaultValue;
break;
case 'saturation':
videoConfig.filters.saturation = defaultValue;
break;
case 'blur':
videoConfig.filters.blur = defaultValue;
break;
case 'hue-rotate':
videoConfig.filters.hueRotate = defaultValue;
break;
case 'sepia':
videoConfig.filters.sepia = defaultValue;
break;
case 'grayscale':
videoConfig.filters.grayscale = defaultValue;
break;
case 'invert':
videoConfig.filters.invert = defaultValue;
break;
}
updateVideoPreview();
showNotification(`${parameterId.replace(/-/g, ' ')} reset to default`);
}
};
function updateColorInputs(colorType) {
const colorMap = {
'overlayColor': 'overlay-color',
'buttonColor': 'button-color'
};
const inputId = colorMap[colorType] || colorType;
const color = videoConfig[colorType];
const colorInput = document.getElementById(inputId);
const hexInput = document.getElementById(`${inputId}-hex`);
const hslInput = document.getElementById(`${inputId}-hsl`);
if (colorInput && hexInput && hslInput) {
colorInput.value = color;
hexInput.value = color;
hslInput.value = VideoUtils.hexToHsl(color);
const container = colorInput.closest('.color-picker-container');
if (container) {
container.style.setProperty('--current-color', color);
}
hexInput.classList.remove('invalid');
hslInput.classList.remove('invalid');
}
}
function initializeUI() {
initVideoBackground();
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-video-background');
});
document.getElementById('download-config').addEventListener('click', () => {
copyJsToClipboard();
});
document.getElementById('copy-full-section').addEventListener('click', () => {
copyFullSectionToClipboard();
});
document.getElementById('reset-video').addEventListener('click', () => {
videoConfig.videoUrl = defaultConfig.videoUrl;
videoConfig.autoplay = defaultConfig.autoplay;
videoConfig.loopVideo = defaultConfig.loopVideo;
videoConfig.additionalUrls = [];
document.getElementById('video-url').value = defaultConfig.videoUrl;
document.getElementById('autoplay').checked = defaultConfig.autoplay;
document.getElementById('loop-video').checked = defaultConfig.loopVideo;
document.getElementById('url-list').innerHTML = '';
updateVideoPreview();
showNotification('Video settings reset');
});
document.getElementById('reset-overlay').addEventListener('click', () => {
videoConfig.overlayColor = defaultConfig.overlayColor;
videoConfig.overlayOpacity = defaultConfig.overlayOpacity;
document.getElementById('overlay-opacity').value = defaultConfig.overlayOpacity;
document.getElementById('overlay-opacity-value').textContent = defaultConfig.overlayOpacity;
updateColorInputs('overlayColor');
updateVideoPreview();
showNotification('Overlay settings reset');
});
document.getElementById('reset-filters').addEventListener('click', () => {
videoConfig.filters = { ...defaultConfig.filters };
Object.keys(defaultConfig.filters).forEach(filter => {
const inputId = filter.replace(/([A-Z])/g, '-$1').toLowerCase();
const input = document.getElementById(inputId);
const valueElement = document.getElementById(`${inputId}-value`);
if (input) input.value = defaultConfig.filters[filter];
if (valueElement) {
const suffix = filter === 'blur' ? 'px' :
filter === 'hueRotate' ? '°' : '%';
valueElement.textContent = defaultConfig.filters[filter] + suffix;
}
});
updateVideoPreview();
showNotification('Filters reset');
});
document.getElementById('reset-controls').addEventListener('click', () => {
videoConfig.showControls = defaultConfig.showControls;
videoConfig.controlStyle = defaultConfig.controlStyle;
videoConfig.buttonPosition = defaultConfig.buttonPosition;
videoConfig.buttonColor = defaultConfig.buttonColor;
videoConfig.showProgress = defaultConfig.showProgress;
videoConfig.showTime = defaultConfig.showTime;
document.getElementById('show-controls').checked = defaultConfig.showControls;
document.getElementById('control-style').value = defaultConfig.controlStyle;
document.getElementById('button-position').value = defaultConfig.buttonPosition;
document.getElementById('show-progress').checked = defaultConfig.showProgress;
document.getElementById('show-time').checked = defaultConfig.showTime;
updateColorInputs('buttonColor');
updateVideoPreview();
showNotification('Control settings reset');
});
document.getElementById('reset-playback').addEventListener('click', () => {
videoConfig.playbackSpeed = defaultConfig.playbackSpeed;
videoConfig.startTime = defaultConfig.startTime;
videoConfig.fadeIn = defaultConfig.fadeIn;
videoConfig.fadeOut = defaultConfig.fadeOut;
videoConfig.pauseWhenInvisible = defaultConfig.pauseWhenInvisible;
document.getElementById('playback-speed').value = defaultConfig.playbackSpeed;
document.getElementById('start-time').value = defaultConfig.startTime;
document.getElementById('fade-in').value = defaultConfig.fadeIn;
document.getElementById('fade-out').value = defaultConfig.fadeOut;
document.getElementById('pause-when-invisible').checked = defaultConfig.pauseWhenInvisible;
document.getElementById('playback-speed-value').textContent = defaultConfig.playbackSpeed;
document.getElementById('start-time-value').textContent = defaultConfig.startTime;
document.getElementById('fade-in-value').textContent = defaultConfig.fadeIn;
document.getElementById('fade-out-value').textContent = defaultConfig.fadeOut;
updateVideoPreview();
showNotification('Playback settings reset');
});
const colorInputs = [
{ id: 'overlay-color', configKey: 'overlayColor' },
{ id: 'button-color', configKey: 'buttonColor' }
];
colorInputs.forEach((colorConfig) => {
const input = document.getElementById(colorConfig.id);
const hexInput = document.getElementById(`${colorConfig.id}-hex`);
const hslInput = document.getElementById(`${colorConfig.id}-hsl`);
hslInput.value = VideoUtils.hexToHsl(input.value);
const container = input.closest('.color-picker-container');
if (container) {
container.style.setProperty('--current-color', input.value);
}
input.addEventListener('input', () => {
const color = input.value;
hexInput.value = color;
hslInput.value = VideoUtils.hexToHsl(color);
hexInput.classList.remove('invalid');
hslInput.classList.remove('invalid');
const container = input.closest('.color-picker-container');
if (container) {
container.style.setProperty('--current-color', color);
}
videoConfig[colorConfig.configKey] = color;
updateVideoPreview();
});
hexInput.addEventListener('input', (e) => {
let hex = e.target.value;
if (!hex.startsWith('#')) {
hex = '#' + hex;
e.target.value = hex;
}
hex = hex.toUpperCase();
e.target.value = hex;
if (VideoUtils.isValidHex(hex)) {
input.value = hex;
hslInput.value = VideoUtils.hexToHsl(hex);
const container = input.closest('.color-picker-container');
if (container) {
container.style.setProperty('--current-color', hex);
}
videoConfig[colorConfig.configKey] = hex;
e.target.classList.remove('invalid');
hslInput.classList.remove('invalid');
updateVideoPreview();
} else {
e.target.classList.add('invalid');
}
});
hexInput.addEventListener('blur', (e) => {
if (!VideoUtils.isValidHex(e.target.value)) {
e.target.value = input.value;
e.target.classList.remove('invalid');
}
});
hslInput.addEventListener('input', (e) => {
let hsl = e.target.value;
if (VideoUtils.isValidHsl(hsl)) {
const hex = VideoUtils.hslToHex(hsl);
if (hex) {
input.value = hex;
hexInput.value = hex;
const container = input.closest('.color-picker-container');
if (container) {
container.style.setProperty('--current-color', hex);
}
videoConfig[colorConfig.configKey] = hex;
e.target.classList.remove('invalid');
hexInput.classList.remove('invalid');
updateVideoPreview();
return;
}
}
e.target.classList.add('invalid');
});
hslInput.addEventListener('blur', (e) => {
if (!VideoUtils.isValidHsl(e.target.value)) {
e.target.value = VideoUtils.hexToHsl(input.value);
e.target.classList.remove('invalid');
}
});
});
document.getElementById('video-url').addEventListener('input', function() {
videoConfig.videoUrl = this.value;
updateVideoPreview();
});
document.getElementById('autoplay').addEventListener('change', function() {
videoConfig.autoplay = this.checked;
updateVideoPreview();
});
document.getElementById('loop-video').addEventListener('change', function() {
videoConfig.loopVideo = this.checked;
updateVideoPreview();
});
document.getElementById('show-controls').addEventListener('change', function() {
videoConfig.showControls = this.checked;
updateVideoPreview();
});
document.getElementById('control-style').addEventListener('change', function() {
videoConfig.controlStyle = this.value;
updateVideoPreview();
});
document.getElementById('button-position').addEventListener('change', function() {
videoConfig.buttonPosition = this.value;
updateVideoPreview();
});
document.getElementById('show-progress').addEventListener('change', function() {
videoConfig.showProgress = this.checked;
updateVideoPreview();
});
document.getElementById('show-time').addEventListener('change', function() {
videoConfig.showTime = this.checked;
updateVideoPreview();
});
document.getElementById('pause-when-invisible').addEventListener('change', function() {
videoConfig.pauseWhenInvisible = this.checked;
updateVideoPreview();
});
const rangeInputs = ['overlay-opacity', 'playback-speed'];
rangeInputs.forEach(inputId => {
const input = document.getElementById(inputId);
const valueElement = document.getElementById(`${inputId}-value`);
if (valueElement) {
valueElement.textContent = input.value;
}
input.addEventListener('input', function() {
if (valueElement) {
valueElement.textContent = this.value;
}
switch (inputId) {
case 'overlay-opacity':
videoConfig.overlayOpacity = parseFloat(this.value);
break;
case 'playback-speed':
videoConfig.playbackSpeed = parseFloat(this.value);
break;
}
updateVideoPreview();
});
});
const numberInputs = ['start-time', 'fade-in', 'fade-out'];
numberInputs.forEach(inputId => {
const input = document.getElementById(inputId);
const valueElement = document.getElementById(`${inputId}-value`);
if (valueElement) {
valueElement.textContent = input.value;
}
input.addEventListener('input', function() {
if (valueElement) {
valueElement.textContent = this.value;
}
switch (inputId) {
case 'start-time':
videoConfig.startTime = parseFloat(this.value);
break;
case 'fade-in':
videoConfig.fadeIn = parseFloat(this.value);
break;
case 'fade-out':
videoConfig.fadeOut = parseFloat(this.value);
break;
}
updateVideoPreview();
});
});
const filterInputs = [
'brightness', 'contrast', 'saturation', 'blur',
'hue-rotate', 'sepia', 'grayscale', 'invert'
];
filterInputs.forEach(inputId => {
const input = document.getElementById(inputId);
const valueElement = document.getElementById(`${inputId}-value`);
if (input && valueElement) {
const suffix = inputId === 'blur' ? 'px' :
inputId === 'hue-rotate' ? '°' : '%';
valueElement.textContent = input.value + suffix;
input.addEventListener('input', function() {
valueElement.textContent = this.value + suffix;
const filterKey = inputId.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
videoConfig.filters[filterKey] = parseFloat(this.value);
updateVideoPreview();
});
}
});
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();
copyJsToClipboard();
break;
case 's':
e.preventDefault();
copyFullSectionToClipboard();
break;
}
}
});
updateColorInputs('overlayColor');
updateColorInputs('buttonColor');
setTimeout(() => {
showNotification('BricksFusion Video Background Configurator loaded!');
}, 500);
}
initializeUI();
});
</script>
</body>
</html>
Video Backdrop
Creates stunning video background with customizable overlay, playback controls, and visual filters. Features auto-sizing video with object-fit cover, custom play/pause buttons with multiple styles and positions, progress bar with seek functionality, and time display. Includes fade effects, visibility-based pause, and comprehensive filters. Uses ResizeObserver and IntersectionObserver for optimal performance. Perfect for hero sections, immersive backgrounds, or feature showcases.
Stunning Video Backgrounds
Your content seamlessly overlays the video backdrop
Video Settings
Source URL for the background video. Supports MP4, WebM, and other HTML5 video formats.
Required
Automatically starts playing when page loads. Works best with muted enabled for browser compatibility.
Default: off
Repeats video continuously from the beginning when it reaches the end.
Default: on
Starts video without sound. Recommended for background videos and required for autoplay in most browsers.
Default: on
Controls video playback rate. 1.0 is normal speed, lower is slower, higher is faster.
Default: 1.0
Starting position in seconds. Useful for skipping intros or starting at specific moments.
Default: 0
Overlay
Color tint applied over the video. Helps improve text readability and set mood.
Default: #000000 (black)
Transparency of the color overlay. 0 is fully transparent, 1 is fully opaque.
Default: 0.3
Controls
Displays play/pause button for user interaction with the video.
Default: on
Visual style of the play/pause button. Choose from minimal, square, or circular designs.
Default: circle
Location of the play button. Options: center-center, top-center, bottom-left, bottom-right, top-left, top-right, bottom-center.
Default: bottom-center
Background color of the play/pause button and progress bar indicator.
Default: #ef6013
Progress & Time
Displays progress bar showing current playback position. Clicking the bar seeks to that position.
Default: off
Displays current time and total duration in minutes and seconds format.
Default: off
Effects
Duration of fade-in transition when video starts playing. Creates smooth appearance.
Default: 0
Duration of fade-out transition when video ends or loops. Creates smooth transition.
Default: 0
Automatically pauses video when scrolled out of view and resumes when visible. Saves bandwidth and resources.
Default: off
Video Filters
Video brightness level. 100 is normal, lower is darker, higher is brighter.
Default: 100
Video contrast level. 100 is normal, lower is flatter, higher is more dramatic.
Default: 100
Color saturation level. 0 is grayscale, 100 is normal, higher is more vibrant.
Default: 100
Gaussian blur intensity. Creates soft focus or depth-of-field effect on video.
Default: 0
Rotates color hue around the color wheel. Creates artistic color shifts and mood changes.
Default: 0
Sepia tone intensity. Creates vintage, warm brown tinted effect. 0 is no effect, 100 is full sepia.
Default: 0
Grayscale intensity. Removes color from video. 0 is full color, 100 is complete black and white.
Default: 0
Inverts video colors. 0 is normal colors, 100 is completely inverted like a negative.
Default: 0
Performance
This element uses HTML5 video with object-fit cover for automatic sizing. Features ResizeObserver for responsive adjustments and IntersectionObserver for visibility-based pause optimization. Custom controls use SVG icons for sharp display at any size. Video filters use CSS filter property with hardware acceleration. Progress bar uses requestAnimationFrame for smooth updates. Medium weight - suitable for hero sections and feature backgrounds. Use with caution on mobile devices and consider enabling pause when invisible for better performance. Video loading is lazy and optimized.