- 74 brands: airbnb, stripe, apple, figma, linear, vercel, etc. - 147 files: DESIGN.md + README.md per brand (~2.1 MB) - Each DESIGN.md contains structured brand analysis (colors, typography, components) - Updated 00-overview.md with new design-systems directory
317 lines
7.0 KiB
Markdown
317 lines
7.0 KiB
Markdown
---
|
|
name: media/video-mask
|
|
title: Video Background with Mask & Clip
|
|
description: Video displayed inside shapes, text, or masks. Creates striking visual impact.
|
|
tags: [media, video, mask, clip, effect, wow]
|
|
source: amppz.com, brad.co, godly.website, linear.app
|
|
---
|
|
|
|
# Video Background with Mask & Clip
|
|
|
|
## Что это
|
|
Видео, отображаемое не как прямоугольник, а внутри формы: маски, обрезка по тексту, SVG-маски. Создаёт премиальный wow-эффект.
|
|
|
|
## Типы масок для видео
|
|
|
|
### 1. Text Mask (видео внутри текста)
|
|
```html
|
|
<section class="hero-video-text">
|
|
<h1 class="text-mask-video">
|
|
PREVIEW
|
|
</h1>
|
|
</section>
|
|
```
|
|
|
|
```css
|
|
.text-mask-video {
|
|
font-size: clamp(4rem, 12vw, 10rem);
|
|
font-weight: 900;
|
|
line-height: 1;
|
|
background: url('/video-preview.mp4') center/cover;
|
|
-webkit-mask-image: text;
|
|
mask-image: text;
|
|
-webkit-mask-repeat: no-repeat;
|
|
mask-repeat: no-repeat;
|
|
-webkit-mask-position: center;
|
|
mask-position: center;
|
|
-webkit-mask-size: contain;
|
|
mask-size: contain;
|
|
color: #0a0a0a; /* fallback color */
|
|
margin: 0 auto;
|
|
}
|
|
```
|
|
|
|
### 2. Circle Mask (видео в круге)
|
|
```html
|
|
<div class="video-circle">
|
|
<video autoplay muted loop playsinline>
|
|
<source src="/hero-video.webm" type="video/webm">
|
|
</video>
|
|
</div>
|
|
```
|
|
|
|
```css
|
|
.video-circle {
|
|
width: 400px;
|
|
height: 400px;
|
|
border-radius: 50%;
|
|
overflow: hidden;
|
|
border: 2px solid rgba(255, 255, 255, 0.1);
|
|
}
|
|
|
|
.video-circle video {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
/* Увеличиваем чтобы заполнить круг */
|
|
transform: scale(1.2);
|
|
}
|
|
```
|
|
|
|
### 3. SVG Mask (произвольная форма)
|
|
```html
|
|
<svg style="position: absolute; width: 0; height: 0;">
|
|
<defs>
|
|
<mask id="star-mask">
|
|
<rect width="100%" height="100%" fill="white"/>
|
|
<path d="M150 0 L200 100 L300 100 L220 160 L250 260 L150 210 L50 260 L80 160 L0 100 L100 100 Z"
|
|
fill="black"/>
|
|
</mask>
|
|
</defs>
|
|
</svg>
|
|
|
|
<div class="video-masked" style="mask: url(#star-mask); -webkit-mask: url(#star-mask);">
|
|
<video autoplay muted loop playsinline>
|
|
<source src="/hero-video.webm" type="video/webm">
|
|
</video>
|
|
</div>
|
|
```
|
|
|
|
```css
|
|
.video-masked {
|
|
width: 300px;
|
|
height: 300px;
|
|
}
|
|
|
|
.video-masked video {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
```
|
|
|
|
### 4. Clip-path Polygon
|
|
```css
|
|
.video-polygon {
|
|
clip-path: polygon(20% 0%, 80% 0%, 100% 50%, 80% 100%, 20% 100%, 0% 50%);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.video-polygon video {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
```
|
|
|
|
### 5. Video as Hero Background
|
|
```html
|
|
<section class="hero-video-bg">
|
|
<div class="hero-overlay"></div>
|
|
<div class="hero-content">
|
|
<h1>Заголовок поверх видео</h1>
|
|
</div>
|
|
</section>
|
|
```
|
|
|
|
```css
|
|
.hero-video-bg {
|
|
position: relative;
|
|
min-height: 100vh;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.hero-video-bg video {
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
min-width: 100%;
|
|
min-height: 100%;
|
|
width: auto;
|
|
height: auto;
|
|
transform: translate(-50%, -50%);
|
|
object-fit: cover;
|
|
z-index: 0;
|
|
}
|
|
|
|
.hero-overlay {
|
|
position: absolute;
|
|
inset: 0;
|
|
background: rgba(0, 0, 0, 0.5);
|
|
z-index: 1;
|
|
}
|
|
|
|
.hero-content {
|
|
position: relative;
|
|
z-index: 2;
|
|
max-width: 800px;
|
|
margin: 0 auto;
|
|
padding: 120px 24px;
|
|
text-align: center;
|
|
}
|
|
```
|
|
|
|
## Performance-Optimized Video Setup
|
|
|
|
### WebM + MP4 Fallback
|
|
```html
|
|
<video autoplay muted loop playsinline poster="/video-poster.jpg" preload="metadata">
|
|
<source src="/video.webm" type="video/webm">
|
|
<source src="/video.mp4" type="video/mp4">
|
|
<!-- Fallback image -->
|
|
<img src="/video-poster.jpg" alt="" class="video-fallback">
|
|
</video>
|
|
```
|
|
|
|
```css
|
|
.video-fallback {
|
|
display: none;
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
|
|
/* Показываем fallback если видео не загрузилось */
|
|
video:not(:success) + .video-fallback {
|
|
display: block;
|
|
}
|
|
```
|
|
|
|
### Responsive Video Sizing
|
|
```css
|
|
.video-responsive {
|
|
width: 100%;
|
|
aspect-ratio: 16 / 9;
|
|
object-fit: cover;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.video-responsive {
|
|
aspect-ratio: 9 / 16;
|
|
}
|
|
}
|
|
```
|
|
|
|
## Lazy Loading Video
|
|
|
|
```html
|
|
<!-- Видео загружается только при появлении в viewport -->
|
|
<video class="video-lazy"
|
|
data-src="/video.webm"
|
|
data-src-mp4="/video.mp4"
|
|
autoplay muted loop playsinline>
|
|
</video>
|
|
```
|
|
|
|
```javascript
|
|
// IntersectionObserver для ленивой загрузки
|
|
const videoObserver = new IntersectionObserver((entries) => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
const video = entry.target;
|
|
video.src = video.dataset.src;
|
|
video.load();
|
|
videoObserver.unobserve(video);
|
|
}
|
|
});
|
|
}, {
|
|
rootMargin: '200px'
|
|
});
|
|
|
|
document.querySelectorAll('.video-lazy').forEach(video => {
|
|
videoObserver.observe(video);
|
|
});
|
|
```
|
|
|
|
## Video with Noise Overlay (premium feel)
|
|
```html
|
|
<div class="video-container">
|
|
<video autoplay muted loop playsinline>
|
|
<source src="/hero-video.webm" type="video/webm">
|
|
</video>
|
|
<div class="noise-overlay"></div>
|
|
<div class="gradient-overlay"></div>
|
|
</div>
|
|
```
|
|
|
|
```css
|
|
.noise-overlay {
|
|
position: absolute;
|
|
inset: 0;
|
|
background-image: url('/noise.png');
|
|
background-size: 256px 256px;
|
|
opacity: 0.03;
|
|
mix-blend-mode: overlay;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.gradient-overlay {
|
|
position: absolute;
|
|
inset: 0;
|
|
background: linear-gradient(
|
|
180deg,
|
|
transparent 0%,
|
|
rgba(10, 10, 10, 0.3) 50%,
|
|
rgba(10, 10, 10, 0.9) 100%
|
|
);
|
|
pointer-events: none;
|
|
}
|
|
```
|
|
|
|
## Anti-patterns
|
|
|
|
| ❌ Плохо | ✅ Хорошо |
|
|
|----------|-----------|
|
|
| `.mp4` без `.webm` fallback | WebM + MP4 + poster fallback |
|
|
| Без `preload="metadata"` | `preload="metadata"` + lazy load |
|
|
| Видео > 10MB | Сжать до < 5MB (HandBrake) |
|
|
| Без `playsinline` на мобильных | Всегда `playsinline` для iOS |
|
|
| Без poster | Всегда `poster` как fallback |
|
|
| Звук | Никогда не включать звук на автоплеер |
|
|
|
|
## Compression Pipeline
|
|
|
|
```bash
|
|
# HandBrake CLI — сжатие видео для веба
|
|
HandBrakeCLI -i input.mp4 -o output.webm \
|
|
--format webm \
|
|
-e libvpx-vp9 \
|
|
-q 25 \
|
|
--width 1920 \
|
|
--max-height 1080 \
|
|
--two-pass \
|
|
--turbo 2
|
|
|
|
# FFmpeg — альтернатива
|
|
ffmpeg -i input.mp4 -c:v libvpx-vp9 -b:v 1500k \
|
|
-c:a libopus -b:a 128k -f webm output.webm
|
|
```
|
|
|
|
## CV Service Validation
|
|
|
|
```bash
|
|
# Проверить performance (video loading impact)
|
|
curl -s -X POST http://localhost:8001/design/analyze-dom \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"html": "...", "full_analysis": false}' | python3 -c "
|
|
import sys, json
|
|
d = json.load(sys.stdin)
|
|
perf = d['analyzers'].get('performance', {})
|
|
print(f\"Performance: {perf.get('score', 'N/A')}/10\")
|
|
print(f\" LCP: {perf.get('lcp_impact', 'N/A')}\")
|
|
print(f\" Largest Element: {perf.get('largest_element', 'N/A')}\")
|
|
"
|
|
```
|
|
|
|
Ожидается: `performance` > 7 (video должен быть оптимизирован).
|