mirror of
https://gitlab.com/vyla-entertainment/home-frontend.git
synced 2026-11-09 20:47:19 -05:00
updated site and images
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 3.3 MiB After Width: | Height: | Size: 1.6 MiB |
BIN
assets/images/site_preview.png
Normal file
BIN
assets/images/site_preview.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
13
index.html
13
index.html
@@ -265,8 +265,8 @@
|
||||
<section class="hero" aria-labelledby="hero-title">
|
||||
<div class="hero-atmosphere" aria-hidden="true">
|
||||
<video class="hero-atmosphere-video" autoplay loop muted playsinline webkit-playsinline preload="auto"
|
||||
aria-hidden="true">
|
||||
<source src="https://videos.pexels.com/video-files/35997189/15263283_3840_2160_25fps.mp4"
|
||||
crossorigin="anonymous" aria-hidden="true">
|
||||
<source src="https://videos.pexels.com/video-files/26754965/12000494_3840_2160_30fps.mp4"
|
||||
type="video/mp4">
|
||||
</video>
|
||||
<div class="hero-atmosphere-sheen"></div>
|
||||
@@ -370,10 +370,6 @@
|
||||
</svg>
|
||||
<span>Available for Windows 10 / 11 (64-bit)</span>
|
||||
</div>
|
||||
<div class="hero-screen-mockup">
|
||||
<img src="assets/images/desktop-preview.png" alt="Vyla Home Desktop Media Player" width="1020"
|
||||
height="574" fetchpriority="high" decoding="async">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="partners-inline reveal" id="partners-grid-wrap">
|
||||
@@ -552,8 +548,9 @@
|
||||
<section class="cta-banner" aria-labelledby="cta-heading">
|
||||
<div class="cta-banner-media" aria-hidden="true">
|
||||
<video class="cta-banner-video hero-atmosphere-video"
|
||||
src="https://videos.pexels.com/video-files/35997189/15263283_3840_2160_25fps.mp4" autoplay loop muted
|
||||
playsinline webkit-playsinline preload="auto" aria-hidden="true"></video>
|
||||
src="https://videos.pexels.com/video-files/26754965/12000494_3840_2160_30fps.mp4" autoplay loop muted
|
||||
playsinline webkit-playsinline preload="auto" crossorigin="anonymous" aria-hidden="true">
|
||||
</video>
|
||||
<canvas id="cta-byte-overlay" aria-hidden="true"></canvas>
|
||||
<div class="cta-banner-overlay"></div>
|
||||
</div>
|
||||
|
||||
@@ -13,14 +13,12 @@ const DIFFUSION_ITERATIONS = 2;
|
||||
const PROJECT_ITERATIONS = 3;
|
||||
const VELOCITY_DAMP = 0.1;
|
||||
const DYE_DAMP = 0.1;
|
||||
const TRAIL_LIFETIME_MS = 184;
|
||||
const TRAIL_LIFETIME_MS = 2000;
|
||||
const TRAIL_RADIUS_PX = 100;
|
||||
const TRAIL_EASE = 2.5;
|
||||
const TRAIL_STRENGTH = 3;
|
||||
const TRAIL_FORCE_DECAY = 1.2;
|
||||
const TRAIL_WOBBLE = 0.14;
|
||||
const DRAG_THRESHOLD_PX = 12;
|
||||
const MAX_TRAIL_POINTS = 24;
|
||||
|
||||
export function initFluidByteField(canvasId) {
|
||||
const canvas = document.getElementById(canvasId);
|
||||
@@ -57,11 +55,6 @@ export function initFluidByteField(canvasId) {
|
||||
let accumulator = 0;
|
||||
let running = true;
|
||||
|
||||
const trails = [];
|
||||
let dragStartX = 0;
|
||||
let dragStartY = 0;
|
||||
let dragging = false;
|
||||
|
||||
function resize() {
|
||||
const rect = container.getBoundingClientRect();
|
||||
const dpr = Math.min(window.devicePixelRatio || 1, 2);
|
||||
@@ -111,8 +104,16 @@ export function initFluidByteField(canvasId) {
|
||||
|
||||
function onPointerMove(e) {
|
||||
const rect = container.getBoundingClientRect();
|
||||
const px = e.clientX - rect.left;
|
||||
const py = e.clientY - rect.top;
|
||||
let px, py;
|
||||
|
||||
if (e.touches && e.touches.length > 0) {
|
||||
px = e.touches[0].clientX - rect.left;
|
||||
py = e.touches[0].clientY - rect.top;
|
||||
} else {
|
||||
px = e.clientX - rect.left;
|
||||
py = e.clientY - rect.top;
|
||||
}
|
||||
|
||||
if (cols > 0 && lastPointerX > -9000 && px >= 0 && py >= 0 && px <= rect.width && py <= rect.height) {
|
||||
const elapsed = Math.min(0.05, Math.max(0.001, (e.timeStamp - lastPointerTime) / 1000));
|
||||
const fx = (px - lastPointerX) / CELL_SIZE / elapsed * ADVECT_SCALE;
|
||||
@@ -129,78 +130,7 @@ export function initFluidByteField(canvasId) {
|
||||
lastPointerY = -9999;
|
||||
}
|
||||
|
||||
function onPointerDown(e) {
|
||||
const rect = container.getBoundingClientRect();
|
||||
const px = e.clientX - rect.left;
|
||||
const py = e.clientY - rect.top;
|
||||
dragStartX = px;
|
||||
dragStartY = py;
|
||||
dragging = px >= 0 && py >= 0 && px <= rect.width && py <= rect.height;
|
||||
}
|
||||
|
||||
function onPointerUp(e) {
|
||||
if (!dragging) return;
|
||||
dragging = false;
|
||||
const rect = container.getBoundingClientRect();
|
||||
const px = e.clientX - rect.left;
|
||||
const py = e.clientY - rect.top;
|
||||
if (Math.hypot(px - dragStartX, py - dragStartY) > DRAG_THRESHOLD_PX) return;
|
||||
trails.push({ x: px, y: py, age: 0, seed: Math.random() * Math.PI * 2 });
|
||||
if (trails.length > MAX_TRAIL_POINTS) trails.shift();
|
||||
}
|
||||
|
||||
function applyTrails() {
|
||||
if (!trails.length) return;
|
||||
const lifetimeSec = TRAIL_LIFETIME_MS / 1000;
|
||||
const cellsPerRadius = TRAIL_RADIUS_PX / CELL_SIZE;
|
||||
for (let i = trails.length - 1; i >= 0; i--) {
|
||||
const trail = trails[i];
|
||||
trail.age += DT;
|
||||
const progress = trail.age / lifetimeSec;
|
||||
if (progress >= 1) {
|
||||
trails.splice(i, 1);
|
||||
continue;
|
||||
}
|
||||
const eased = 1 - Math.pow(1 - progress, TRAIL_EASE);
|
||||
const ringRadius = eased * TRAIL_RADIUS_PX / CELL_SIZE;
|
||||
const remaining = 1 - progress;
|
||||
const intensity = 0.35 + 0.65 * eased;
|
||||
const force = TRAIL_STRENGTH * Math.pow(remaining, TRAIL_FORCE_DECAY);
|
||||
const dyeAmount = 0.2 * Math.pow(remaining, 2);
|
||||
const cx = trail.x / CELL_SIZE;
|
||||
const cy = trail.y / CELL_SIZE;
|
||||
const seed = trail.seed;
|
||||
const band = ringRadius * 1.3 + cellsPerRadius;
|
||||
const xMin = Math.max(1, Math.floor(cx - band));
|
||||
const xMax = Math.min(cols - 2, Math.ceil(cx + band));
|
||||
const yMin = Math.max(1, Math.floor(cy - band));
|
||||
const yMax = Math.min(rows - 2, Math.ceil(cy + band));
|
||||
for (let y = yMin; y <= yMax; y++) {
|
||||
for (let x = xMin; x <= xMax; x++) {
|
||||
const dx = x - cx;
|
||||
const dy = y - cy;
|
||||
const dist = Math.sqrt(dx * dx + dy * dy);
|
||||
const angle = Math.atan2(dy, dx);
|
||||
const wobble = 0.5 * Math.sin(6 * angle + seed) + 0.25 * Math.sin(3 * angle + 0.05 * dist + 0.3 * seed) + 0.5;
|
||||
const wobbleAmount = TRAIL_WOBBLE * (2 * wobble - 1) * intensity;
|
||||
const targetRadius = ringRadius * Math.min(1.6, Math.max(0.6, 1 + wobbleAmount));
|
||||
const ringDist = Math.abs(dist - targetRadius) / cellsPerRadius;
|
||||
if (ringDist >= 1) continue;
|
||||
const falloff = smoothstep(1 - ringDist);
|
||||
const forceMul = Math.min(2.2, Math.max(0.4, 1 + wobbleAmount));
|
||||
const invDist = dist > 0.001 ? 1 / dist : 0;
|
||||
const idx = x + y * cols;
|
||||
velX[idx] += dx * invDist * force * forceMul * falloff;
|
||||
velY[idx] += dy * invDist * force * forceMul * falloff;
|
||||
dye[idx] = Math.min(1.5, dye[idx] + dyeAmount * forceMul * falloff);
|
||||
if (Math.random() < 0.03 * falloff) glyphs[idx] = Math.floor(Math.random() * 256);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function step() {
|
||||
applyTrails();
|
||||
const visc = 1.5 * DT;
|
||||
velX0.set(velX);
|
||||
diffuse(velX, velX0, visc, cols, rows, DT, DIFFUSION_ITERATIONS);
|
||||
@@ -255,18 +185,18 @@ export function initFluidByteField(canvasId) {
|
||||
videoLuminance = (r * 0.299 + g * 0.587 + b * 0.114) / 255;
|
||||
}
|
||||
|
||||
const baseAlpha = 0.04 + videoLuminance * 0.22;
|
||||
const baseAlpha = 0;
|
||||
if (d <= 0.02) {
|
||||
ctx.shadowBlur = 0;
|
||||
ctx.globalAlpha = baseAlpha;
|
||||
ctx.fillStyle = 'rgba(0, 0, 0, 0.45)';
|
||||
ctx.fillStyle = 'rgba(255, 255, 255)';
|
||||
ctx.fillText(hex, x * CELL_SIZE + half, y * CELL_SIZE + half);
|
||||
} else {
|
||||
const alpha = d * FADE_RATE;
|
||||
const activeAlpha = Math.min(alpha + baseAlpha, MAX_ALPHA);
|
||||
const alpha = d * 1.5;
|
||||
const activeAlpha = Math.min(alpha + baseAlpha, 1.0);
|
||||
ctx.globalAlpha = activeAlpha;
|
||||
ctx.fillStyle = '#ffffff';
|
||||
ctx.shadowColor = '#ffffff';
|
||||
ctx.shadowBlur = 0;
|
||||
ctx.fillText(hex, x * CELL_SIZE + half, y * CELL_SIZE + half);
|
||||
}
|
||||
}
|
||||
@@ -304,8 +234,8 @@ export function initFluidByteField(canvasId) {
|
||||
new ResizeObserver(resize).observe(container);
|
||||
window.addEventListener('pointermove', onPointerMove, { passive: true });
|
||||
window.addEventListener('pointerleave', onPointerLeave);
|
||||
window.addEventListener('pointerdown', onPointerDown, { passive: true });
|
||||
window.addEventListener('pointerup', onPointerUp, { passive: true });
|
||||
window.addEventListener('touchmove', onPointerMove, { passive: true });
|
||||
window.addEventListener('touchend', onPointerLeave);
|
||||
document.addEventListener('visibilitychange', onVisibilityChange);
|
||||
|
||||
resize();
|
||||
|
||||
@@ -6,6 +6,101 @@ import { DISCORD_LOADER_SCRIPT, COPY_FEEDBACK_DURATION_MS } from '../config/cons
|
||||
|
||||
const HERO_GREETING = 'Vyla Home';
|
||||
|
||||
function initVideoColorExtraction() {
|
||||
const videos = document.querySelectorAll('.hero-atmosphere-video');
|
||||
if (videos.length === 0) return;
|
||||
|
||||
console.log('Video color extraction initialized, found', videos.length, 'videos');
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d', { willReadFrequently: true });
|
||||
let animationFrameId;
|
||||
let frameCount = 0;
|
||||
let currentVideoColor = 'rgb(0, 0, 0)';
|
||||
|
||||
function extractColors() {
|
||||
const video = videos[0];
|
||||
if (video.readyState < 2) {
|
||||
console.log('Video not ready, readyState:', video.readyState);
|
||||
animationFrameId = requestAnimationFrame(extractColors);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
canvas.width = 64;
|
||||
canvas.height = 64;
|
||||
ctx.drawImage(video, 0, 0, 64, 64);
|
||||
const imageData = ctx.getImageData(0, 0, 64, 64);
|
||||
const data = imageData.data;
|
||||
|
||||
let r = 0, g = 0, b = 0;
|
||||
let count = 0;
|
||||
|
||||
for (let i = 0; i < data.length; i += 4) {
|
||||
r += data[i];
|
||||
g += data[i + 1];
|
||||
b += data[i + 2];
|
||||
count++;
|
||||
}
|
||||
|
||||
r = Math.round(r / count);
|
||||
g = Math.round(g / count);
|
||||
b = Math.round(b / count);
|
||||
|
||||
currentVideoColor = `rgb(${r}, ${g}, ${b})`;
|
||||
const dominantColorTransparent = `rgba(${r}, ${g}, ${b}, 0.5)`;
|
||||
|
||||
document.documentElement.style.setProperty('--video-dominant-color', currentVideoColor);
|
||||
document.documentElement.style.setProperty('--video-dominant-color-transparent', dominantColorTransparent);
|
||||
|
||||
if (window.scrollY === 0) {
|
||||
document.documentElement.style.setProperty('--color-bg', currentVideoColor);
|
||||
}
|
||||
|
||||
frameCount++;
|
||||
if (frameCount % 30 === 0) {
|
||||
console.log('Frame', frameCount, '- Extracted color:', currentVideoColor);
|
||||
console.log('CSS variable set:', getComputedStyle(document.documentElement).getPropertyValue('--video-dominant-color'));
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error extracting video colors:', e);
|
||||
}
|
||||
|
||||
animationFrameId = requestAnimationFrame(extractColors);
|
||||
}
|
||||
|
||||
function handleScroll() {
|
||||
const scrollY = window.scrollY;
|
||||
const heroHeight = window.innerHeight;
|
||||
const scrollProgress = Math.min(scrollY / (heroHeight * 0.5), 1);
|
||||
|
||||
const videoColorRGB = currentVideoColor.match(/\d+/g);
|
||||
if (videoColorRGB) {
|
||||
const r = Math.round(parseInt(videoColorRGB[0]) * (1 - scrollProgress));
|
||||
const g = Math.round(parseInt(videoColorRGB[1]) * (1 - scrollProgress));
|
||||
const b = Math.round(parseInt(videoColorRGB[2]) * (1 - scrollProgress));
|
||||
const fadedColor = `rgb(${r}, ${g}, ${b})`;
|
||||
document.documentElement.style.setProperty('--color-bg', fadedColor);
|
||||
}
|
||||
}
|
||||
|
||||
animationFrameId = requestAnimationFrame(extractColors);
|
||||
window.addEventListener('scroll', handleScroll, { passive: true });
|
||||
handleScroll();
|
||||
|
||||
videos.forEach(video => {
|
||||
video.addEventListener('pause', () => {
|
||||
console.log('Video paused, stopping color extraction');
|
||||
cancelAnimationFrame(animationFrameId);
|
||||
});
|
||||
|
||||
video.addEventListener('play', () => {
|
||||
console.log('Video playing, starting color extraction');
|
||||
animationFrameId = requestAnimationFrame(extractColors);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function initHeroTitle() {
|
||||
const heroTitle = qs('.hero-title');
|
||||
if (heroTitle) typewriterReveal(heroTitle, HERO_GREETING);
|
||||
@@ -48,8 +143,7 @@ export function initHeroSection() {
|
||||
initDiscordCopyBar();
|
||||
initHeroCopyButton();
|
||||
initCtaCopyButton();
|
||||
if (!isMobileViewport()) {
|
||||
initFluidByteField('hero-byte-overlay');
|
||||
initFluidByteField('cta-byte-overlay');
|
||||
}
|
||||
initVideoColorExtraction();
|
||||
initFluidByteField('hero-byte-overlay');
|
||||
initFluidByteField('cta-byte-overlay');
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
z-index: -1;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
background: var(--video-dominant-color, black);
|
||||
-webkit-mask-image:
|
||||
linear-gradient(to bottom, transparent, black 50%),
|
||||
linear-gradient(to top, transparent, black 70%);
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
-webkit-mask-image: linear-gradient(to bottom,
|
||||
black 0%,
|
||||
black 48%,
|
||||
rgba(0, 0, 0, 0.5) 80%,
|
||||
var(--video-dominant-color, black) 0%,
|
||||
var(--video-dominant-color, black) 48%,
|
||||
var(--video-dominant-color-transparent, rgba(0, 0, 0, 0.5)) 80%,
|
||||
transparent 96%);
|
||||
mask-image: linear-gradient(to bottom,
|
||||
black 0%,
|
||||
black 48%,
|
||||
rgba(0, 0, 0, 0.5) 80%,
|
||||
var(--video-dominant-color, black) 0%,
|
||||
var(--video-dominant-color, black) 48%,
|
||||
var(--video-dominant-color-transparent, rgba(0, 0, 0, 0.5)) 80%,
|
||||
transparent 96%);
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
-webkit-mask-size: 100% 100%;
|
||||
@@ -50,13 +50,6 @@
|
||||
animation: hero-sheen 16s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
.hero-atmosphere-vignette {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(to bottom, transparent 15%, rgba(0, 0, 0, 0.15) 55%, rgba(0, 0, 0, 0.95) 100%),
|
||||
radial-gradient(ellipse at center, transparent 40%, rgba(0, 0, 0, 0.2) 100%);
|
||||
}
|
||||
|
||||
#hero-byte-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
@@ -103,7 +96,6 @@
|
||||
@media (orientation: landscape) and (max-height: 600px) {
|
||||
.hero {
|
||||
height: auto;
|
||||
min-height: 100svh;
|
||||
padding-top: 5rem;
|
||||
height: 100vh;
|
||||
}
|
||||
}
|
||||
@@ -95,8 +95,8 @@
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(8px) saturate(140%);
|
||||
-webkit-backdrop-filter: blur(8px) saturate(140%);
|
||||
backdrop-filter: blur(15px) saturate(140%);
|
||||
-webkit-backdrop-filter: blur(15px) saturate(140%);
|
||||
border: none;
|
||||
padding: 0.35rem 0.85rem;
|
||||
border-radius: var(--radius-pill);
|
||||
@@ -104,6 +104,8 @@
|
||||
font-weight: 500;
|
||||
color: var(--color-fg-dim);
|
||||
margin-bottom: 1.25rem;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.platform-pill svg {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
@media (orientation: landscape) and (min-width: 600px) and (max-height: 500px) {
|
||||
.container {
|
||||
width: 95%;
|
||||
width: 70%;
|
||||
padding-inline: 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user