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 href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Oswald:wght@400;500;600;700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600;700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Raleway:wght@400;500;600;700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Lato:wght@400;700&display=swap" rel="stylesheet">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Video Text 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;
--success: #28a745;
--warning: #ffc107;
--danger: #dc3545;
--text-xs;
--text-s;
--text-m;
--text-l;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: var(--font);
background-color: var(--background);
color: var(--text-primary);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
padding-bottom: var(--action-bar-height);
}
.action-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: var(--action-bar-height);
background: linear-gradient(145deg, #1a1a1a, #0f0f0f);
border-top: 1px solid var(--border);
z-index: 1000;
display: flex;
align-items: center;
padding: 0 1.5rem;
gap: 1rem;
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.3);
backdrop-filter: blur(10px);
}
.breadcrumb {
display: flex;
align-items: center;
gap: 0.5rem;
flex: 1;
}
.breadcrumb-item {
color: var(--text-secondary);
font-size: var(--text-xs);
font-weight: 500;
text-decoration: none;
transition: var(--transition);
padding: 0.5rem 0.75rem;
border-radius: 6px;
}
.breadcrumb-item:hover {
color: var(--text-primary);
background-color: rgba(255, 255, 255, 0.05);
}
.breadcrumb-item.active {
color: var(--accent);
background-color: rgba(239, 96, 19, 0.1);
}
.breadcrumb-separator {
color: var(--text-secondary);
font-size: var(--text-xs);
opacity: 0.5;
}
.action-buttons {
display: flex;
align-items: center;
gap: 0.75rem;
}
.action-btn {
padding: 0.6rem 1rem;
background-color: var(--card-bg);
color: var(--text-primary);
font-family: var(--font);
font-size: var(--text-xs);
font-weight: 500;
border: 1px solid var(--border);
border-radius: var(--button-radius);
cursor: pointer;
transition: var(--transition);
display: flex;
align-items: center;
gap: 0.5rem;
text-decoration: none;
white-space: nowrap;
}
.action-btn:hover {
background-color: var(--card-bg-hover);
border-color: var(--accent);
transform: translateY(-1px);
}
.action-btn.primary {
background: linear-gradient(90deg, var(--accent), #ff8c51);
border-color: var(--accent);
color: white;
}
.action-btn.primary:hover {
background: linear-gradient(90deg, var(--accent-hover), #e67a3f);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(239, 96, 19, 0.3);
}
.data-attribute-display {
background-color: rgba(50, 50, 50, 0.8);
border: 1px solid var(--border);
border-radius: 6px;
padding: 0.5rem 0.75rem;
font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
font-size: var(--text-xs);
color: #ff8c51;
cursor: pointer;
transition: var(--transition);
user-select: all;
}
.data-attribute-display:hover {
background-color: rgba(239, 96, 19, 0.2);
border-color: var(--accent);
}
.container {
max-width: 100%;
margin: 0 auto;
padding: 2rem 1.5rem;
}
.page-header {
text-align: center;
margin-bottom: 2rem;
}
.page-title {
font-size: 2.5rem;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 0.5rem;
background: linear-gradient(90deg, var(--accent), #ff8c51);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.page-subtitle {
font-size: var(--text-s);
color: var(--text-secondary);
font-weight: 500;
}
.instructions-toggle {
margin-bottom: 2rem;
}
.instructions-card {
background-color: var(--card-bg);
border: 1px solid var(--border);
border-radius: var(--card-radius);
box-shadow: var(--shadow);
overflow: hidden;
transition: var(--transition);
}
.instructions-header {
padding: 1rem 1.5rem;
cursor: pointer;
transition: var(--transition);
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid transparent;
}
.instructions-header:hover {
background-color: var(--card-bg-hover);
}
.instructions-card.expanded .instructions-header {
border-bottom-color: var(--border);
}
.instructions-title {
font-size: var(--text-s);
font-weight: 600;
}
.toggle-icon {
font-size: 1.2em;
transition: transform 0.3s ease;
}
.toggle-icon.expanded {
transform: rotate(180deg);
}
.instructions-content {
padding: 0 1.5rem;
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease, padding 0.3s ease;
}
.instructions-content.show {
max-height: 500px;
padding: 1.5rem;
}
.instructions-grid {
display: grid;
grid-template-columns: 1fr;
gap: 1.5rem;
}
.how-to-use ol {
padding-left: 1.5rem;
}
.how-to-use li {
margin-bottom: 0.75rem;
font-size: var(--text-xs);
color: var(--text-secondary);
line-height: 1.6;
}
.how-to-use strong {
color: var(--text-primary);
font-weight: 600;
}
.how-to-use code {
background-color: rgba(50, 50, 50, 0.5);
padding: 0.2rem 0.5rem;
border-radius: 4px;
font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
font-size: var(--text-xs);
color: #ff8c51;
}
.content {
display: grid;
grid-template-columns: 1fr 500px;
gap: 2rem;
align-items: start;
}
.preview-section {
position: sticky;
top: 2rem;
}
.controls-section {
max-width: 500px;
}
.card {
background-color: var(--card-bg);
border-radius: var(--card-radius);
box-shadow: var(--shadow);
overflow: hidden;
margin-bottom: 1.5rem;
border: 1px solid var(--border);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 6px 24px rgba(0, 0, 0, 0.4);
}
.preview-container {
height: 400px;
width: 100%;
position: relative;
overflow: hidden;
border-radius: var(--card-radius);
background-color: #000000;
border: 1px solid var(--border);
box-shadow: var(--shadow);
}
.preview-content {
color: white;
text-align: center;
font-weight: bold;
font-size: var(--text-s);
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
position: absolute;
top: 1rem;
left: 50%;
transform: translateX(-50%);
z-index: 100;
pointer-events: none;
}
.preview-controls {
position: absolute;
top: 1rem;
right: 1rem;
display: flex;
gap: 0.5rem;
z-index: 10;
}
.preview-btn {
padding: 0.5rem;
background-color: rgba(0, 0, 0, 0.7);
color: white;
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 6px;
cursor: pointer;
transition: var(--transition);
font-size: var(--text-xs);
backdrop-filter: blur(5px);
}
.preview-btn:hover {
background-color: var(--accent);
border-color: var(--accent);
}
.preview-btn svg {
width: 18px;
height: 18px;
stroke: currentColor;
}
.background-selector-wrapper {
position: relative;
display: inline-block;
}
.background-selector-btn {
position: relative;
}
.background-selector-btn:hover {
background-color: rgba(239, 96, 19, 0.2);
border-color: var(--accent);
box-shadow: 0 0 8px rgba(239, 96, 19, 0.3);
}
.hidden-color-input {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
cursor: pointer;
z-index: 1;
}
.card-heading {
padding: 1rem 1.5rem;
font-size: var(--text-s);
font-weight: 600;
border-bottom: 1px solid var(--border);
letter-spacing: 0.3px;
display: flex;
justify-content: space-between;
align-items: center;
}
.card-actions {
display: flex;
gap: 0.5rem;
}
.card-action-btn {
padding: 0.4rem 0.8rem;
background-color: transparent;
color: var(--text-secondary);
border: 1px solid var(--border);
border-radius: 6px;
cursor: pointer;
font-size: var(--text-xs);
transition: var(--transition);
}
.card-action-btn:hover {
color: var(--text-primary);
border-color: var(--accent);
background-color: rgba(239, 96, 19, 0.1);
}
.card-content {
padding: 1.5rem;
}
.control-group {
margin-bottom: 1.5rem;
position: relative;
}
.control-group:last-child {
margin-bottom: 0;
}
.control-label {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.75rem;
}
.label-text {
font-size: var(--text-xs);
font-weight: 500;
letter-spacing: 0.2px;
display: flex;
align-items: center;
gap: 0.5rem;
}
.help-tooltip {
cursor: help;
opacity: 0.7;
transition: var(--transition);
}
.help-tooltip:hover {
opacity: 1;
color: var(--accent);
}
.value-display {
display: flex;
align-items: center;
gap: 0.5rem;
}
.value-text {
font-size: var(--text-xs);
color: var(--text-secondary);
background-color: rgba(50, 50, 50, 0.5);
padding: 2px 8px;
border-radius: 4px;
min-width: 45px;
text-align: center;
}
.reset-btn {
padding: 0.2rem 0.4rem;
background-color: transparent;
color: var(--text-secondary);
border: 1px solid var(--border);
border-radius: 4px;
cursor: pointer;
font-size: 10px;
transition: var(--transition);
}
.reset-btn:hover {
color: var(--danger);
border-color: var(--danger);
background-color: rgba(220, 53, 69, 0.1);
}
input[type="range"] {
-webkit-appearance: none;
width: 100%;
height: 6px;
background: var(--track);
border-radius: 3px;
outline: none;
margin: 0.8rem 0;
position: relative;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 20px;
height: 20px;
background: var(--thumb);
border-radius: 50%;
cursor: pointer;
transition: var(--transition);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
}
input[type="range"]::-webkit-slider-thumb:hover {
transform: scale(1.2);
box-shadow: 0 0 10px rgba(239, 96, 19, 0.5);
}
input[type="text"],
input[type="number"],
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: var(--card-bg);
margin-bottom: 0.75rem;
outline: none;
transition: var(--transition);
}
input[type="text"]:focus,
input[type="number"]:focus,
input[type="url"]:focus,
select:focus {
border-color: var(--accent);
box-shadow: 0 0 0 2px rgba(239, 96, 19, 0.2);
}
.toggle-switch-wrapper {
display: flex;
align-items: center;
margin: 0.5rem 0;
}
.toggle-switch {
position: relative;
display: inline-block;
width: 50px;
height: 24px;
}
.toggle-switch input {
opacity: 0;
width: 0;
height: 0;
}
.toggle-slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: var(--card-bg);
border: 1px solid var(--border);
transition: .4s;
border-radius: 24px;
}
.toggle-slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 4px;
bottom: 3px;
background-color: var(--text-secondary);
transition: .4s;
border-radius: 50%;
}
input:checked + .toggle-slider {
background-color: var(--accent);
border-color: var(--accent);
}
input:checked + .toggle-slider:before {
transform: translateX(26px);
background-color: white;
}
.toggle-label {
margin-left: 10px;
font-size: var(--text-xs);
color: var(--text-secondary);
}
input:checked ~ .toggle-label {
color: var(--accent);
}
.notification {
position: fixed;
bottom: calc(var(--action-bar-height) + 1rem);
left: 50%;
background-color: var(--success);
color: white;
padding: 0.75rem 1rem;
border-radius: var(--input-radius);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
z-index: 1001;
transform: translate(-50%, 200px);
opacity: 0;
transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.4s ease;
font-size: var(--text-xs);
font-weight: 500;
max-width: 320px;
word-wrap: break-word;
line-height: 1.4;
text-align: center;
}
.notification.show {
transform: translate(-50%, 0);
opacity: 1;
}
@media (max-width: 1200px) {
.content {
grid-template-columns: 1fr;
gap: 1.5rem;
}
.preview-section {
position: static;
}
.controls-section {
max-width: 100%;
}
}
@media (max-width: 768px) {
.action-bar {
flex-direction: column;
height: auto;
min-height: var(--action-bar-height);
padding: 0.75rem;
}
.breadcrumb {
order: 1;
width: 100%;
}
.action-buttons {
order: 2;
width: 100%;
justify-content: center;
flex-wrap: wrap;
}
body {
padding-bottom: calc(var(--action-bar-height) + 20px);
}
.notification {
bottom: calc(var(--action-bar-height) + 2rem);
max-width: 280px;
transform: translate(-50%, 250px);
}
.notification.show {
transform: translate(-50%, 0);
opacity: 1;
}
.preview-container {
height: 300px;
}
.data-attribute-display {
font-size: 10px;
padding: 0.4rem 0.6rem;
}
.action-btn {
font-size: 11px;
padding: 0.5rem 0.8rem;
}
.page-title {
font-size: 2rem;
}
}
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
button:focus-visible,
input:focus-visible,
.action-btn:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: var(--background);
}
::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--accent);
}
.loading {
opacity: 0.6;
pointer-events: none;
position: relative;
}
.loading::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 20px;
height: 20px;
margin: -10px 0 0 -10px;
border: 2px solid var(--border);
border-top-color: var(--accent);
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
</style>
</head>
<body>
<div class="action-bar">
<nav class="breadcrumb">
<a href="https://bricksfusion.com" class="breadcrumb-item">Home</a>
<span class="breadcrumb-separator">›</span>
<a href="https://bricksfusion.com/animated-text/" class="breadcrumb-item">Animated text</a>
<span class="breadcrumb-separator">›</span>
<span class="breadcrumb-item active">Video Text Effect</span>
</nav>
<div class="action-buttons">
<div class="data-attribute-display" id="quick-attribute" title="Click to copy data attribute">
data-video-text
</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">Video Text Effect</h1>
<p class="page-subtitle">Interactive video masking 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 text 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</li>
<li>To add the effect to any container: go to <strong>Section → Style → Attributes</strong>, add <code>data-video-text</code> as attribute name (leave value empty)</li>
</ol>
</div>
</div>
</div>
</div>
</div>
<div class="content">
<section class="preview-section">
<div class="preview-container" id="video-text-preview" data-video-text="true">
<div class="preview-content">Interactive Video Text Preview</div>
<div class="preview-controls">
<button class="preview-btn" id="randomize-video-text" title="Randomize (R)">🎲</button>
<div class="background-selector-wrapper">
<button class="preview-btn background-selector-btn" id="background-selector">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polygon points="12,2 2,7 12,12 22,7"/>
<polyline points="2,17 12,22 22,17"/>
<polyline points="2,12 12,17 22,12"/>
</svg>
</button>
<input type="color" id="preview-background-picker" class="hidden-color-input" value="#252525" title="Change Preview Background (B)">
</div>
</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 Source URL</span>
</div>
<input type="url" id="video-src" value="https://videos.pexels.com/video-files/3078336/3078336-uhd_2560_1440_25fps.mp4" placeholder="Enter video URL...">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">Loop Video</span>
</div>
<div class="toggle-switch-wrapper">
<label class="toggle-switch">
<input type="checkbox" id="loop-toggle" checked>
<span class="toggle-slider"></span>
</label>
<span class="toggle-label">On</span>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-heading">
Text Settings
<div class="card-actions">
<button class="card-action-btn" id="reset-text" title="Reset Text Settings">↺</button>
</div>
</div>
<div class="card-content">
<div class="control-group">
<div class="control-label">
<span class="label-text">Text Content</span>
</div>
<input type="text" id="text-content" value="INCREDIBLE" placeholder="Enter text...">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">Font Family</span>
</div>
<select id="font-family">
<option value="Inter">Inter</option>
<option value="Montserrat">Montserrat</option>
<option value="Roboto">Roboto</option>
<option value="Oswald">Oswald</option>
<option value="Playfair Display">Playfair Display</option>
<option value="Poppins">Poppins</option>
<option value="Raleway">Raleway</option>
<option value="Lato">Lato</option>
</select>
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">
Font Size (Desktop)
<span class="help-tooltip" title="Font size for desktop devices">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="font-size-value">100</span>px</span>
<button class="reset-btn" onclick="resetParameter('font-size', 100)">↺</button>
</div>
</div>
<input type="range" id="font-size" min="20" max="300" step="5" value="100">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">
Font Size (Mobile)
<span class="help-tooltip" title="Font size for mobile devices">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="font-size-mobile-value">60</span>px</span>
<button class="reset-btn" onclick="resetParameter('font-size-mobile', 60)">↺</button>
</div>
</div>
<input type="range" id="font-size-mobile" min="10" max="150" step="5" value="60">
</div>
<div class="control-group">
<div class="control-label">
<span class="label-text">Font Weight</span>
</div>
<select id="font-weight">
<option value="normal">Normal</option>
<option value="bold" selected>Bold</option>
<option value="100">100</option>
<option value="200">200</option>
<option value="300">300</option>
<option value="400">400</option>
<option value="500">500</option>
<option value="600">600</option>
<option value="700">700</option>
<option value="800">800</option>
<option value="900">900</option>
</select>
</div>
</div>
</div>
<div class="card">
<div class="card-heading">
Advanced Options
<div class="card-actions">
<button class="card-action-btn" id="reset-advanced" title="Reset Advanced Settings">↺</button>
</div>
</div>
<div class="card-content">
<div class="control-group">
<div class="control-label">
<span class="label-text">
Z-Index
<span class="help-tooltip" title="Layer positioning of the video text effect">ℹ</span>
</span>
<div class="value-display">
<span class="value-text"><span id="z-index-value">1</span></span>
<button class="reset-btn" onclick="resetParameter('z-index', 1)">↺</button>
</div>
</div>
<input type="range" id="z-index" min="-1" max="10" step="1" value="1">
</div>
</div>
</div>
</section>
</div>
</div>
<div class="notification" id="notification"></div>
<script>
document.addEventListener('DOMContentLoaded', function() {
let videoTextConfig = {
videoSrc: 'https://videos.pexels.com/video-files/3078336/3078336-uhd_2560_1440_25fps.mp4',
textContent: 'INCREDIBLE',
fontFamily: 'Inter',
fontSize: 100,
fontSizeMobile: 60,
fontWeight: 'bold',
loop: true,
zIndex: 1
};
const defaultConfig = { ...videoTextConfig };
let activeVideoText = null;
let previewContainer = null;
let videoElement = null;
let updateInProgress = false;
let updateTimeout = null;
function initVideoTextEffect() {
const sections = document.querySelectorAll('[data-video-text]:not([data-video-text-initialized="true"])');
sections.forEach((section) => {
const config = {
videoSrc: section.getAttribute('data-video-src') || videoTextConfig.videoSrc,
textContent: section.getAttribute('data-text-content') || videoTextConfig.textContent,
fontFamily: section.getAttribute('data-font-family') || videoTextConfig.fontFamily,
fontSize: parseInt(section.getAttribute('data-font-size') || videoTextConfig.fontSize, 10),
fontSizeMobile: parseInt(section.getAttribute('data-font-size-mobile') || videoTextConfig.fontSizeMobile, 10),
fontWeight: section.getAttribute('data-font-weight') || videoTextConfig.fontWeight,
loop: section.getAttribute('data-loop') !== 'false',
zIndex: parseInt(section.getAttribute('data-z-index') || videoTextConfig.zIndex, 10)
};
setupVideoTextEffect(section, config);
section.dataset.videoTextInitialized = 'true';
if (section.id === 'video-text-preview') {
activeVideoText = { element: section, config };
videoTextConfig = { ...config };
}
});
}
function setupVideoTextEffect(element, config) {
if (window.getComputedStyle(element).position === 'static') {
element.style.position = 'relative';
}
const maskContainer = document.createElement('div');
maskContainer.className = 'video-text-mask';
maskContainer.style.position = 'absolute';
maskContainer.style.top = '0';
maskContainer.style.left = '0';
maskContainer.style.width = '100%';
maskContainer.style.height = '100%';
maskContainer.style.display = 'flex';
maskContainer.style.alignItems = 'center';
maskContainer.style.justifyContent = 'center';
maskContainer.style.zIndex = config.zIndex;
const video = document.createElement('video');
video.style.width = '100%';
video.style.height = '100%';
video.style.objectFit = 'cover';
video.muted = true;
video.autoplay = true;
video.loop = config.loop;
video.playsInline = true;
video.setAttribute('muted', '');
video.setAttribute('autoplay', '');
if (config.loop) video.setAttribute('loop', '');
video.setAttribute('playsinline', '');
const source = document.createElement('source');
source.src = config.videoSrc;
source.type = 'video/mp4';
video.appendChild(source);
maskContainer.appendChild(video);
element.appendChild(maskContainer);
element._maskContainer = maskContainer;
element._videoElement = video;
element._config = config;
updateTextMask(element);
video.play().catch(error => {
console.warn('Could not autoplay video:', error);
});
if (element.id === 'video-text-preview') {
previewContainer = maskContainer;
videoElement = video;
}
element._updateConfig = function(newConfig) {
Object.assign(this._config, newConfig);
if (this._maskContainer) {
this._maskContainer.style.zIndex = this._config.zIndex;
}
if (this._videoElement && (newConfig.videoSrc || newConfig.loop !== undefined)) {
this._videoElement.loop = this._config.loop;
if (newConfig.videoSrc && newConfig.videoSrc !== this._videoElement.querySelector('source').src) {
this._videoElement.querySelector('source').src = this._config.videoSrc;
this._videoElement.load();
this._videoElement.play().catch(() => {});
}
}
updateTextMask(this);
};
}
function createSVGMask(config) {
const fontSize = window.innerWidth <= 768 ?
`${config.fontSizeMobile}px` :
`${config.fontSize}px`;
const fontFamily = `'${config.fontFamily}', sans-serif`;
return `<svg xmlns='http://www.w3.org/2000/svg' width='100%' height='100%'>
<text
x='50%'
y='50%'
font-size='${fontSize}'
font-weight='${config.fontWeight}'
font-family="${fontFamily}"
text-anchor='middle'
dominant-baseline='middle'
fill='white'
>${config.textContent}</text>
</svg>`;
}
function updateTextMask(element) {
if (!element._maskContainer || !element._config) return;
const svgMask = createSVGMask(element._config);
const dataUrlMask = `url("data:image/svg+xml,${encodeURIComponent(svgMask)}")`;
element._maskContainer.style.maskImage = dataUrlMask;
element._maskContainer.style.maskSize = 'contain';
element._maskContainer.style.maskRepeat = 'no-repeat';
element._maskContainer.style.maskPosition = 'center';
element._maskContainer.style.webkitMaskImage = dataUrlMask;
element._maskContainer.style.webkitMaskSize = 'contain';
element._maskContainer.style.webkitMaskRepeat = 'no-repeat';
element._maskContainer.style.webkitMaskPosition = 'center';
}
function updateVideoTextPreview() {
const preview = document.getElementById('video-text-preview');
if (!preview || !preview._updateConfig) return;
preview._updateConfig(videoTextConfig);
Object.assign(videoTextConfig, preview._config);
}
function generateRandomVideoText() {
const texts = ['AMAZING', 'INCREDIBLE', 'AWESOME', 'STUNNING', 'BRILLIANT', 'DYNAMIC', 'POWERFUL', 'CREATIVE'];
const fonts = ['Inter', 'Montserrat', 'Roboto', 'Oswald', 'Playfair Display', 'Poppins', 'Raleway', 'Lato'];
const weights = ['normal', 'bold', '400', '500', '600', '700', '800', '900'];
videoTextConfig.textContent = texts[Math.floor(Math.random() * texts.length)];
videoTextConfig.fontFamily = fonts[Math.floor(Math.random() * fonts.length)];
videoTextConfig.fontSize = Math.floor(Math.random() * 200) + 50;
videoTextConfig.fontSizeMobile = Math.floor(Math.random() * 100) + 30;
videoTextConfig.fontWeight = weights[Math.floor(Math.random() * weights.length)];
videoTextConfig.zIndex = Math.floor(Math.random() * 5) + 1;
document.getElementById('text-content').value = videoTextConfig.textContent;
document.getElementById('font-family').value = videoTextConfig.fontFamily;
document.getElementById('font-size').value = videoTextConfig.fontSize;
document.getElementById('font-size-mobile').value = videoTextConfig.fontSizeMobile;
document.getElementById('font-weight').value = videoTextConfig.fontWeight;
document.getElementById('z-index').value = videoTextConfig.zIndex;
document.getElementById('font-size-value').textContent = videoTextConfig.fontSize;
document.getElementById('font-size-mobile-value').textContent = videoTextConfig.fontSizeMobile;
document.getElementById('z-index-value').textContent = videoTextConfig.zIndex;
updateVideoTextPreview();
showNotification('Random video text generated!');
}
function showNotification(message, type = 'success') {
const notification = document.getElementById('notification');
notification.textContent = message;
notification.className = `notification ${type}`;
notification.offsetHeight;
notification.style.visibility = 'visible';
notification.classList.add('show');
setTimeout(() => {
notification.classList.remove('show');
setTimeout(() => {
if (!notification.classList.contains('show')) {
notification.style.visibility = 'hidden';
}
}, 400);
}, 3000);
}
function generateUniqueId() {
return Math.random().toString(36).substring(2, 8);
}
function generateFullSectionJSON() {
// Generar IDs únicos para todos los elementos
const sectionId = generateUniqueId();
const containerId = generateUniqueId();
const divId = generateUniqueId();
const textId = 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": "#000000"}},
"_height": "500"
}
},
{
"id": containerId,
"name": "container",
"parent": sectionId,
"children": [divId],
"settings": {
"_alignSelf": "center",
"_justifyContent": "center",
"_alignItems": "center",
"_direction": "row"
}
},
{
"id": divId,
"name": "div",
"parent": containerId,
"children": [textId],
"settings": {
"_display": "flex",
"_alignSelf": "center",
"_justifyContent": "center",
"_alignItems": "center",
"_width": "100%",
"_attributes": [{"id": attributeId, "name": "data-video-text"}],
"_height": "200"
},
"label": "Video Text Div"
},
{
"id": textId,
"name": "text-basic",
"parent": divId,
"children": [],
"settings": {
"text": "Here goes your text ... Select any part of your text to access the formatting toolbar.",
"tag": "p"
}
},
{
"id": codeId,
"name": "code",
"parent": sectionId,
"children": [],
"settings": {
"javascriptCode": jsCode,
"executeCode": true,
"_display": "none"
},
"label": "Video Text JS"
}
],
"source": "bricksCopiedElements",
"sourceUrl": "https://test.bricksfusion.com",
"version": "2.0.1",
"globalClasses": [],
"globalElements": []
};
return JSON.stringify(bricksJSON, null, 2);
}
function generateJavaScriptCode() {
return `(function(window) {
const VideoTextEffect = {
instances: [],
config: {
videoSrc: "${videoTextConfig.videoSrc}",
textContent: "${videoTextConfig.textContent}",
fontFamily: "${videoTextConfig.fontFamily}",
fontSize: ${videoTextConfig.fontSize},
fontSizeMobile: ${videoTextConfig.fontSizeMobile},
fontWeight: "${videoTextConfig.fontWeight}",
loop: ${videoTextConfig.loop},
zIndex: ${videoTextConfig.zIndex}
},
init: function(options = {}) {
const defaultOptions = {
selector: '[data-video-text]',
videoSrcAttr: 'data-video-src',
textContentAttr: 'data-text-content',
fontFamilyAttr: 'data-font-family',
fontSizeAttr: 'data-font-size',
fontSizeMobileAttr: 'data-font-size-mobile',
fontWeightAttr: 'data-font-weight',
loopAttr: 'data-loop',
zIndexAttr: 'data-z-index'
};
const config = { ...defaultOptions, ...options };
const initInstances = () => {
document.querySelectorAll(config.selector).forEach(element => {
if (!element.hasAttribute('data-video-text-initialized')) {
this.createInstance(element, config);
element.setAttribute('data-video-text-initialized', 'true');
}
});
};
initInstances();
setTimeout(initInstances, 100);
window.addEventListener('load', initInstances);
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'childList') {
initInstances();
}
});
});
observer.observe(document.body, { childList: true, subtree: true });
},
createInstance: function(element, config) {
if (window.getComputedStyle(element).position === 'static') {
element.style.position = 'relative';
}
const instanceConfig = {
videoSrc: element.getAttribute(config.videoSrcAttr) || this.config.videoSrc,
textContent: element.getAttribute(config.textContentAttr) || this.config.textContent,
fontFamily: element.getAttribute(config.fontFamilyAttr) || this.config.fontFamily,
fontSize: parseInt(element.getAttribute(config.fontSizeAttr) || this.config.fontSize, 10),
fontSizeMobile: parseInt(element.getAttribute(config.fontSizeMobileAttr) || this.config.fontSizeMobile, 10),
fontWeight: element.getAttribute(config.fontWeightAttr) || this.config.fontWeight,
loop: element.getAttribute(config.loopAttr) !== 'false',
zIndex: parseInt(element.getAttribute(config.zIndexAttr) || this.config.zIndex, 10)
};
const maskContainer = document.createElement('div');
maskContainer.className = 'video-text-mask';
Object.assign(maskContainer.style, {
position: 'absolute',
top: '0',
left: '0',
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
zIndex: instanceConfig.zIndex
});
const video = document.createElement('video');
Object.assign(video.style, {
width: '100%',
height: '100%',
objectFit: 'cover'
});
video.muted = true;
video.autoplay = true;
video.loop = instanceConfig.loop;
video.playsInline = true;
video.setAttribute('muted', '');
video.setAttribute('autoplay', '');
if (instanceConfig.loop) video.setAttribute('loop', '');
video.setAttribute('playsinline', '');
const source = document.createElement('source');
source.src = instanceConfig.videoSrc;
source.type = 'video/mp4';
video.appendChild(source);
maskContainer.appendChild(video);
element.appendChild(maskContainer);
const instance = {
element,
maskContainer,
video,
config: instanceConfig,
updateTextMask: function() {
const fontSize = window.innerWidth <= 768 ?
\`\${this.config.fontSizeMobile}px\` :
\`\${this.config.fontSize}px\`;
const fontFamily = \`'\${this.config.fontFamily}', sans-serif\`;
const svgMask = \`<svg xmlns='http://www.w3.org/2000/svg' width='100%' height='100%'>
<text
x='50%'
y='50%'
font-size='\${fontSize}'
font-weight='\${this.config.fontWeight}'
font-family="\${fontFamily}"
text-anchor='middle'
dominant-baseline='middle'
fill='white'
>\${this.config.textContent}</text>
</svg>\`;
const dataUrlMask = \`url("data:image/svg+xml,\${encodeURIComponent(svgMask)}")\`;
this.maskContainer.style.maskImage = dataUrlMask;
this.maskContainer.style.maskSize = 'contain';
this.maskContainer.style.maskRepeat = 'no-repeat';
this.maskContainer.style.maskPosition = 'center';
this.maskContainer.style.webkitMaskImage = dataUrlMask;
this.maskContainer.style.webkitMaskSize = 'contain';
this.maskContainer.style.webkitMaskRepeat = 'no-repeat';
this.maskContainer.style.webkitMaskPosition = 'center';
},
updateConfig: function(newConfig) {
Object.assign(this.config, newConfig);
if (newConfig.zIndex !== undefined) {
this.maskContainer.style.zIndex = this.config.zIndex;
}
if (newConfig.videoSrc && newConfig.videoSrc !== this.video.querySelector('source').src) {
this.video.querySelector('source').src = this.config.videoSrc;
this.video.load();
this.video.play().catch(() => {});
}
if (newConfig.loop !== undefined) {
this.video.loop = this.config.loop;
if (this.config.loop) {
this.video.setAttribute('loop', '');
} else {
this.video.removeAttribute('loop');
}
}
this.updateTextMask();
}
};
instance.updateTextMask();
video.play().catch(() => {});
let resizeTimeout;
window.addEventListener('resize', () => {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => instance.updateTextMask(), 250);
});
this.instances.push(instance);
},
updateInstance: function(element, newConfig) {
const instance = this.instances.find(inst => inst.element === element);
if (instance) {
instance.updateConfig(newConfig);
}
}
};
window.VideoTextEffect = VideoTextEffect;
VideoTextEffect.init();
})(window);`;
}
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 'font-size':
videoTextConfig.fontSize = defaultValue;
break;
case 'font-size-mobile':
videoTextConfig.fontSizeMobile = defaultValue;
break;
case 'z-index':
videoTextConfig.zIndex = defaultValue;
break;
}
updateVideoTextPreview();
showNotification(`${parameterId.replace(/-/g, ' ')} reset to default`);
}
};
function updateLoopLabel() {
const label = document.querySelector('.toggle-label');
label.textContent = videoTextConfig.loop ? 'On' : 'Off';
}
function initializeUI() {
initVideoTextEffect();
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-text');
});
document.getElementById('download-config').addEventListener('click', () => {
copyJsToClipboard();
});
document.getElementById('copy-full-section').addEventListener('click', () => {
copyFullSectionToClipboard();
});
document.getElementById('randomize-video-text').addEventListener('click', () => {
generateRandomVideoText();
});
const backgroundPicker = document.getElementById('preview-background-picker');
const previewContainer = document.getElementById('video-text-preview');
backgroundPicker.addEventListener('input', (e) => {
const selectedColor = e.target.value;
previewContainer.style.backgroundColor = selectedColor;
showNotification(`Preview background changed to ${selectedColor}`);
});
previewContainer.style.backgroundColor = '#252525';
document.getElementById('reset-video').addEventListener('click', () => {
videoTextConfig.videoSrc = defaultConfig.videoSrc;
videoTextConfig.loop = defaultConfig.loop;
document.getElementById('video-src').value = defaultConfig.videoSrc;
document.getElementById('loop-toggle').checked = defaultConfig.loop;
updateLoopLabel();
updateVideoTextPreview();
showNotification('Video settings reset');
});
document.getElementById('reset-text').addEventListener('click', () => {
videoTextConfig.textContent = defaultConfig.textContent;
videoTextConfig.fontFamily = defaultConfig.fontFamily;
videoTextConfig.fontSize = defaultConfig.fontSize;
videoTextConfig.fontSizeMobile = defaultConfig.fontSizeMobile;
videoTextConfig.fontWeight = defaultConfig.fontWeight;
document.getElementById('text-content').value = defaultConfig.textContent;
document.getElementById('font-family').value = defaultConfig.fontFamily;
document.getElementById('font-size').value = defaultConfig.fontSize;
document.getElementById('font-size-mobile').value = defaultConfig.fontSizeMobile;
document.getElementById('font-weight').value = defaultConfig.fontWeight;
document.getElementById('font-size-value').textContent = defaultConfig.fontSize;
document.getElementById('font-size-mobile-value').textContent = defaultConfig.fontSizeMobile;
updateVideoTextPreview();
showNotification('Text settings reset');
});
document.getElementById('reset-advanced').addEventListener('click', () => {
videoTextConfig.zIndex = defaultConfig.zIndex;
document.getElementById('z-index').value = defaultConfig.zIndex;
document.getElementById('z-index-value').textContent = defaultConfig.zIndex;
updateVideoTextPreview();
showNotification('Advanced settings reset');
});
document.getElementById('video-src').addEventListener('input', function() {
videoTextConfig.videoSrc = this.value;
updateVideoTextPreview();
});
document.getElementById('text-content').addEventListener('input', function() {
videoTextConfig.textContent = this.value;
updateVideoTextPreview();
});
document.getElementById('font-family').addEventListener('change', function() {
videoTextConfig.fontFamily = this.value;
updateVideoTextPreview();
});
document.getElementById('loop-toggle').addEventListener('change', function() {
videoTextConfig.loop = this.checked;
updateLoopLabel();
updateVideoTextPreview();
});
document.getElementById('font-weight').addEventListener('change', function() {
videoTextConfig.fontWeight = this.value;
updateVideoTextPreview();
});
const rangeInputs = document.querySelectorAll('input[type="range"]');
rangeInputs.forEach(input => {
const valueElement = document.getElementById(`${input.id}-value`);
if (valueElement) {
valueElement.textContent = input.value;
}
input.addEventListener('input', () => {
if (valueElement) {
valueElement.textContent = input.value;
}
switch (input.id) {
case 'font-size':
videoTextConfig.fontSize = parseInt(input.value);
break;
case 'font-size-mobile':
videoTextConfig.fontSizeMobile = parseInt(input.value);
break;
case 'z-index':
videoTextConfig.zIndex = parseInt(input.value);
break;
}
updateVideoTextPreview();
});
});
document.addEventListener('keydown', (e) => {
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') {
return;
}
if (e.ctrlKey || e.metaKey) {
switch (e.key.toLowerCase()) {
case 'd':
e.preventDefault();
const downloadBtn = document.getElementById('download-config');
if (downloadBtn && downloadBtn.hasAttribute('data-protection-animation')) {
downloadBtn.click();
} else {
copyJsToClipboard();
}
break;
case 's':
e.preventDefault();
const fullSectionBtn = document.getElementById('copy-full-section');
if (fullSectionBtn && fullSectionBtn.hasAttribute('data-protection-animation')) {
fullSectionBtn.click();
} else {
copyFullSectionToClipboard();
}
break;
}
} else {
switch (e.key.toLowerCase()) {
case 'r':
generateRandomVideoText();
break;
case 'b':
document.getElementById('preview-background-picker').click();
break;
}
}
});
updateLoopLabel();
setTimeout(() => {
showNotification('BricksFusion Video Text Effect Configurator loaded!');
}, 500);
function saveConfiguration() {
try {
localStorage.setItem('bricksfusion-video-text-config', JSON.stringify(videoTextConfig));
} catch (e) {
}
}
function loadConfiguration() {
try {
const saved = localStorage.getItem('bricksfusion-video-text-config');
if (saved) {
const savedConfig = JSON.parse(saved);
Object.assign(videoTextConfig, savedConfig);
document.getElementById('video-src').value = savedConfig.videoSrc;
document.getElementById('text-content').value = savedConfig.textContent;
document.getElementById('font-family').value = savedConfig.fontFamily;
document.getElementById('font-size').value = savedConfig.fontSize;
document.getElementById('font-size-mobile').value = savedConfig.fontSizeMobile;
document.getElementById('font-weight').value = savedConfig.fontWeight;
document.getElementById('loop-toggle').checked = savedConfig.loop;
document.getElementById('z-index').value = savedConfig.zIndex;
document.getElementById('font-size-value').textContent = savedConfig.fontSize;
document.getElementById('font-size-mobile-value').textContent = savedConfig.fontSizeMobile;
document.getElementById('z-index-value').textContent = savedConfig.zIndex;
updateLoopLabel();
updateVideoTextPreview();
}
} catch (e) {
}
}
const originalUpdateVideoTextPreview = updateVideoTextPreview;
updateVideoTextPreview = function() {
originalUpdateVideoTextPreview();
saveConfiguration();
};
loadConfiguration();
}
initializeUI();
});
</script>
</body>
</html>
Video Text
Creates stunning text filled with playing video using CSS masking technique. Video automatically crops to text shape and adapts to font size, weight, and letter spacing changes. Features intelligent canvas-based mask generation that waits for custom fonts to load. Uses MutationObserver to detect text content changes and regenerate masks instantly. Respects all text styling including shadows and transformations. Perfect for hero headlines, creative typography, or eye-catching call-to-actions.
Video Text Effect
Watch as video fills the text below with dynamic motion.
VIDEO TEXT
Video Settings
URL of the video that fills the text. Video automatically loops and plays with object-fit cover for best appearance.
Default: Sample video URL
Repeats video continuously. Recommended to keep enabled for seamless visual effect.
Default: on
Display Settings
Stacking order of the video mask layer. Higher values place it above other elements.
Default: 1
Performance
This element uses HTML5 Canvas to generate text masks and CSS mask-image property to clip video. Features intelligent font loading detection that waits for custom fonts before rendering. MutationObserver monitors text content, styling changes, and size adjustments to regenerate masks automatically. Uses throttled updates with 50ms delay to prevent excessive redraws. Canvas renders at 2x resolution for crisp edges on high-DPI displays. Video is muted and autoplays for optimal user experience. Medium weight - works best with shorter text (1-3 words) and bold fonts for maximum impact. Suitable for hero sections and featured headings.