forked from numexai/cyberbeats
530 lines
20 KiB
HTML
530 lines
20 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="tr">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<title>TEST93 — Proje Portalı</title>
|
||
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect x='3' y='3' width='26' height='26' rx='7' fill='%23061722' stroke='%2300f0ff' stroke-width='2'/%3E%3Cpath d='M10 20l4-9 4 9M7 17h6M19 17h6' stroke='%23ff4d9e' stroke-width='2.4' fill='none' stroke-linecap='round'/%3E%3Cpolygon points='22,6 22.9,8.4 25.3,8.6 23.5,10.2 24,12.6 22,11.3 20,12.6 20.5,10.2 18.7,8.6 21.1,8.4' fill='%2300f0ff'/%3E%3C/svg%3E" />
|
||
<style>
|
||
:root {
|
||
--bg: #060912;
|
||
--panel: rgba(255,255,255,0.04);
|
||
--border: rgba(255,255,255,0.08);
|
||
--neon1: #00f0ff;
|
||
--neon2: #b14fff;
|
||
--pink: #ff4d9e;
|
||
--green: #39f5a0;
|
||
--gold: #ffd166;
|
||
--txt: #dce9ff;
|
||
--dim: #6a7fa8;
|
||
}
|
||
*, *::before, *::after { margin:0; padding:0; box-sizing:border-box; }
|
||
body {
|
||
font-family: 'Segoe UI', system-ui, -apple-system, Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||
background: var(--bg);
|
||
color: var(--txt);
|
||
min-height: 100vh;
|
||
overflow-x: hidden;
|
||
}
|
||
#bgCanvas {
|
||
position: fixed;
|
||
inset: 0;
|
||
z-index: 0;
|
||
pointer-events: none;
|
||
}
|
||
.wrapper {
|
||
position: relative;
|
||
z-index: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
padding: 60px 24px 80px;
|
||
min-height: 100vh;
|
||
}
|
||
header {
|
||
text-align: center;
|
||
margin-bottom: 72px;
|
||
animation: fadeDown .8s cubic-bezier(.22,1,.36,1) both;
|
||
}
|
||
.eyebrow {
|
||
font-family: 'Cascadia Code', Consolas, 'SF Mono', Menlo, monospace;
|
||
font-size: 11px;
|
||
letter-spacing: 5px;
|
||
text-transform: uppercase;
|
||
color: var(--neon1);
|
||
opacity: .7;
|
||
margin-bottom: 16px;
|
||
}
|
||
header h1 {
|
||
font-size: clamp(2.4rem, 6vw, 4.2rem);
|
||
font-weight: 900;
|
||
line-height: 1.05;
|
||
background: linear-gradient(135deg, var(--neon1) 0%, var(--neon2) 50%, var(--pink) 100%);
|
||
-webkit-background-clip: text;
|
||
-webkit-text-fill-color: transparent;
|
||
background-clip: text;
|
||
margin-bottom: 16px;
|
||
}
|
||
header p {
|
||
font-size: 1rem;
|
||
color: var(--dim);
|
||
max-width: 480px;
|
||
margin: 0 auto;
|
||
line-height: 1.6;
|
||
}
|
||
.badge-row {
|
||
display: flex;
|
||
gap: 10px;
|
||
justify-content: center;
|
||
margin-top: 24px;
|
||
flex-wrap: wrap;
|
||
}
|
||
.badge {
|
||
font-family: 'Cascadia Code', Consolas, 'SF Mono', Menlo, monospace;
|
||
font-size: 10px;
|
||
padding: 4px 12px;
|
||
border-radius: 20px;
|
||
border: 1px solid var(--border);
|
||
background: var(--panel);
|
||
color: var(--dim);
|
||
letter-spacing: 1px;
|
||
}
|
||
.grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
||
gap: 28px;
|
||
width: 100%;
|
||
max-width: 1080px;
|
||
perspective: 1400px; /* kart 03 tilt'inin derinliği */
|
||
}
|
||
.card {
|
||
background: var(--panel);
|
||
border: 1px solid var(--border);
|
||
border-radius: 20px;
|
||
padding: 36px 32px 32px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 20px;
|
||
position: relative;
|
||
overflow: hidden;
|
||
text-decoration: none;
|
||
color: inherit;
|
||
backdrop-filter: blur(16px);
|
||
transition: transform .3s cubic-bezier(.22,1,.36,1), border-color .3s, box-shadow .3s;
|
||
animation: fadeUp .8s cubic-bezier(.22,1,.36,1) both;
|
||
}
|
||
.card:nth-child(1) { animation-delay:.1s; --ac: var(--neon1); }
|
||
.card:nth-child(2) { animation-delay:.22s; --ac: var(--neon2); }
|
||
.card:nth-child(3) { animation-delay:.34s; --ac: var(--pink); }
|
||
.card::before {
|
||
content:'';
|
||
position:absolute; inset:0;
|
||
background: radial-gradient(ellipse at 20% 20%, rgba(255,255,255,0.04) 0%, transparent 70%);
|
||
pointer-events:none;
|
||
}
|
||
.card::after {
|
||
content:'';
|
||
position:absolute; top:0; left:0; right:0; height:2px;
|
||
background: var(--ac);
|
||
opacity:0;
|
||
transition: opacity .3s;
|
||
border-radius:20px 20px 0 0;
|
||
}
|
||
.card:hover { transform:translateY(-8px); border-color: var(--ac); box-shadow: 0 24px 64px -12px rgba(0,0,0,.4); }
|
||
.card:hover::after { opacity:1; }
|
||
.card-header { display:flex; align-items:flex-start; gap:16px; }
|
||
.icon {
|
||
width:52px; height:52px; border-radius:14px;
|
||
background: rgba(255,255,255,0.06);
|
||
border: 1px solid rgba(255,255,255,0.12);
|
||
display:flex; align-items:center; justify-content:center;
|
||
font-size:22px; flex-shrink:0;
|
||
transition: box-shadow .3s;
|
||
}
|
||
.card:hover .icon { box-shadow: 0 0 20px var(--ac); border-color: var(--ac); }
|
||
.card-title-block { flex:1; }
|
||
.card-num {
|
||
font-family:'Cascadia Code',Consolas,'SF Mono',Menlo,monospace;
|
||
font-size:10px; color:var(--ac);
|
||
letter-spacing:3px; text-transform:uppercase;
|
||
margin-bottom:4px; opacity:.7;
|
||
}
|
||
.card-title { font-size:1.5rem; font-weight:700; line-height:1.1; }
|
||
.status-dot {
|
||
width:8px; height:8px; border-radius:50%;
|
||
background:var(--green); box-shadow:0 0 8px var(--green);
|
||
flex-shrink:0; margin-top:6px;
|
||
animation: pulse 2s ease-in-out infinite;
|
||
}
|
||
.card-desc { font-size:.95rem; color:var(--dim); line-height:1.65; flex:1; }
|
||
.tags { display:flex; flex-wrap:wrap; gap:8px; }
|
||
.tag {
|
||
font-family:'Cascadia Code',Consolas,'SF Mono',Menlo,monospace;
|
||
font-size:10px; padding:4px 10px; border-radius:6px;
|
||
background: rgba(255,255,255,0.05);
|
||
border: 1px solid rgba(255,255,255,0.1);
|
||
color: var(--dim); letter-spacing:.5px;
|
||
}
|
||
.card:hover .tag { border-color: var(--ac); color: var(--ac); background: rgba(0,0,0,0.2); }
|
||
.stats { display:flex; gap:20px; padding-top:16px; border-top:1px solid var(--border); }
|
||
.stat { display:flex; flex-direction:column; gap:3px; }
|
||
.stat-val {
|
||
font-family:'Cascadia Code',Consolas,'SF Mono',Menlo,monospace;
|
||
font-size:1.1rem; font-weight:700; color:var(--ac);
|
||
}
|
||
.stat-lbl { font-size:.72rem; color:var(--dim); text-transform:uppercase; letter-spacing:1px; }
|
||
.launch-btn {
|
||
display:flex; align-items:center; justify-content:center; gap:8px;
|
||
padding:14px 24px; border-radius:12px;
|
||
background: rgba(255,255,255,0.05);
|
||
border: 1px solid rgba(255,255,255,0.12);
|
||
color: var(--ac);
|
||
font-family:'Segoe UI',system-ui,-apple-system,sans-serif; font-size:.9rem; font-weight:600;
|
||
transition: background .25s, box-shadow .25s, border-color .25s;
|
||
}
|
||
.card:hover .launch-btn {
|
||
background: rgba(255,255,255,0.1);
|
||
border-color: var(--ac);
|
||
box-shadow: 0 0 24px rgba(0,240,255,0.15);
|
||
}
|
||
.launch-btn svg { width:16px; height:16px; transition:transform .25s; }
|
||
.card:hover .launch-btn svg { transform:translateX(4px); }
|
||
|
||
/* ============ KART BAŞINA ÖZGÜN HOVER EFEKTLERİ ============
|
||
01 CyberBeats -> beat-pulse (ritmik sıçrama + equalizer şeridi)
|
||
02 NeuralFlow -> levitasyon + sinyal taraması (gezgin ışık bandı)
|
||
03 FluidSim -> pointer'a doğru 3D tilt + su dalgası yüzeyi */
|
||
|
||
/* --- 01 CYBERBEATS: beat-pulse ---------------------------------- */
|
||
#card-beats { transform-style:preserve-3d; }
|
||
#card-beats:hover { animation: beatPulse .38s ease-in-out infinite; }
|
||
#card-beats:hover .icon { animation: iconJolt .5s ease-in-out infinite; }
|
||
#card-beats::before { opacity:0; }
|
||
#card-beats:hover::before {
|
||
opacity:1;
|
||
background:
|
||
repeating-linear-gradient(90deg,
|
||
rgba(0,240,255,0) 0 6px,
|
||
rgba(0,240,255,.08) 6px 8px,
|
||
rgba(0,240,255,0) 8px 14px);
|
||
animation: eqSweep .55s linear infinite;
|
||
}
|
||
#card-beats:hover .launch-btn { animation: btnBass .38s ease-in-out infinite; }
|
||
|
||
/* --- 02 NEURALFLOW: levitation + signal travel ------------------ */
|
||
#card-neural:hover { animation: levitate 2.6s ease-in-out infinite; }
|
||
#card-neural:hover .icon { animation: iconSpin 6s linear infinite; }
|
||
#card-neural:hover::after {
|
||
opacity:1;
|
||
background: var(--neon2);
|
||
animation: signalTravel 1.15s linear infinite;
|
||
}
|
||
|
||
/* --- 03 FLUIDSIM: pointer tilt + water lens ---------------------- */
|
||
#card-fluid { transition: transform .18s ease-out, border-color .3s, box-shadow .3s; }
|
||
#card-fluid:hover { border-color: var(--green); box-shadow: 0 30px 80px -18px rgba(57,245,160,.35); }
|
||
#card-fluid:hover .icon { animation: rippleDrop .9s ease-out infinite; }
|
||
/* su dalgası / lens yüzeyi: JS pointer-Tx/Ty değişkenlerini kullanır */
|
||
#card-fluid::after {
|
||
opacity:0; bottom:auto; height:auto;
|
||
background:
|
||
radial-gradient(circle at var(--tx,50%) var(--ty,50%),
|
||
rgba(57,245,160,.18) 0%,
|
||
rgba(57,245,160,0) 46%),
|
||
radial-gradient(circle at var(--tx,50%) var(--ty,50%),
|
||
rgba(0,240,255,.16) 0%, rgba(0,240,255,0) 70%);
|
||
transition:opacity .25s;
|
||
pointer-events:none;
|
||
}
|
||
#card-fluid:hover::after { opacity:1; }
|
||
|
||
/* ------- animasyon keyframe'leri (kartlara özel) ------- */
|
||
@keyframes beatPulse {
|
||
0%,100% { transform:translateY(0) scale(1); }
|
||
25% { transform:translateY(-3px) scale(1.012); }
|
||
75% { transform:translateY(-1px) scale(.994); }
|
||
}
|
||
@keyframes iconJolt { 0%,100%{transform:rotate(0)} 30%{transform:rotate(-7deg)} 60%{transform:rotate(5deg)} }
|
||
@keyframes eqSweep { from{background-position:0 0} to{background-position:28px 0} }
|
||
@keyframes btnBass {
|
||
0%,100% { box-shadow:0 0 0 0 rgba(0,240,255,.35); }
|
||
50% { box-shadow:0 0 22px 2px rgba(0,240,255,.0); }
|
||
}
|
||
@keyframes levitate {
|
||
0%,100% { transform:translateY(0); }
|
||
50% { transform:translateY(-14px); }
|
||
}
|
||
@keyframes iconSpin {
|
||
0% { filter:hue-rotate(0deg) } 100% { filter:hue-rotate(360deg) }
|
||
}
|
||
@keyframes signalTravel {
|
||
0% { left:-80%; }
|
||
100% { left:100%; }
|
||
}
|
||
@keyframes rippleDrop {
|
||
0% { transform:scale(1); box-shadow:0 0 0 0 rgba(57,245,160,.4); }
|
||
100% { transform:scale(1.25); box-shadow:0 0 0 16px rgba(57,245,160,0); }
|
||
}
|
||
|
||
/* kart içindeki ::after için farklı viewport: signalTravel bandı hem
|
||
yatay taşsın hem accent renginde kalsın */
|
||
#card-neural::after {
|
||
bottom:0; top:auto; left:-55%; right:auto;
|
||
width:45%; height:3px;
|
||
box-shadow:0 0 14px var(--neon2);
|
||
}
|
||
|
||
footer {
|
||
margin-top:72px; text-align:center;
|
||
font-family:'Cascadia Code',Consolas,'SF Mono',Menlo,monospace;
|
||
font-size:11px; color:var(--dim); opacity:.45; letter-spacing:2px;
|
||
animation: fadeUp 1s .5s cubic-bezier(.22,1,.36,1) both;
|
||
}
|
||
@keyframes fadeDown {
|
||
from { opacity:0; transform:translateY(-30px); }
|
||
to { opacity:1; transform:translateY(0); }
|
||
}
|
||
@keyframes fadeUp {
|
||
from { opacity:0; transform:translateY(40px); }
|
||
to { opacity:1; transform:translateY(0); }
|
||
}
|
||
@keyframes pulse {
|
||
0%,100% { box-shadow:0 0 8px var(--green); opacity:1; }
|
||
50% { box-shadow:0 0 18px var(--green); opacity:.55; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<canvas id="bgCanvas"></canvas>
|
||
|
||
<div class="wrapper">
|
||
<header>
|
||
<p class="eyebrow">Numex · TEST93 · Proje Portalı</p>
|
||
<h1>Üç Dünya,<br/>Bir Ekran</h1>
|
||
<p>Saf JavaScript ile inşa edilmiş üç farklı gerçek zamanlı simülatör — sıfır kütüphane, sıfır framework.</p>
|
||
<div class="badge-row">
|
||
<span class="badge">ZERO DEPENDENCY</span>
|
||
<span class="badge">WEB AUDIO API</span>
|
||
<span class="badge">CANVAS 2D</span>
|
||
<span class="badge">NAVIER-STOKES</span>
|
||
<span class="badge">BACKPROPAGATION</span>
|
||
</div>
|
||
</header>
|
||
|
||
<div class="grid">
|
||
|
||
<!-- 01 CyberBeats -->
|
||
<a class="card" href="/" target="_blank" id="card-beats">
|
||
<div class="card-header">
|
||
<div class="icon">🥁</div>
|
||
<div class="card-title-block">
|
||
<div class="card-num">01 · Müzik</div>
|
||
<div class="card-title">Cyber Beats</div>
|
||
</div>
|
||
<div class="status-dot" title="Calisiyor"></div>
|
||
</div>
|
||
<p class="card-desc">
|
||
16-adımlı davul makinesi. Kick, snare, hi-hat ve synth —
|
||
tamamen Web Audio API osilatörleri ve gürültü bufferları ile sentezlenir.
|
||
MP3 veya WAV yok, saf matematik sesi.
|
||
</p>
|
||
<div class="tags">
|
||
<span class="tag">OscillatorNode</span>
|
||
<span class="tag">AudioContext</span>
|
||
<span class="tag">16-Step Sequencer</span>
|
||
<span class="tag">BPM Control</span>
|
||
</div>
|
||
<div class="stats">
|
||
<div class="stat"><span class="stat-val">4</span><span class="stat-lbl">Kanal</span></div>
|
||
<div class="stat"><span class="stat-val">16</span><span class="stat-lbl">Adım</span></div>
|
||
<div class="stat"><span class="stat-val">120</span><span class="stat-lbl">BPM</span></div>
|
||
<div class="stat"><span class="stat-val">0</span><span class="stat-lbl">Kütüphane</span></div>
|
||
</div>
|
||
<div class="launch-btn">
|
||
Başlat
|
||
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2">
|
||
<path d="M3 8h10M9 4l4 4-4 4"/>
|
||
</svg>
|
||
</div>
|
||
</a>
|
||
|
||
<!-- 02 NeuralFlow -->
|
||
<a class="card" href="http://localhost:8123/" target="_blank" id="card-neural">
|
||
<div class="card-header">
|
||
<div class="icon">🧠</div>
|
||
<div class="card-title-block">
|
||
<div class="card-num">02 · Yapay Zekâ</div>
|
||
<div class="card-title">NeuralFlow</div>
|
||
</div>
|
||
<div class="status-dot" title="Calisiyor"></div>
|
||
</div>
|
||
<p class="card-desc">
|
||
Gerçek zamanlı yapay sinir ağı simülatörü. Katman sayısı ve nöron sayısı
|
||
slider’larla ayarlanabilir. İleri yayılım ve geri yayılım animasyonlu
|
||
görselleştirilir — elle yazılmış saf matris matematig̀i.
|
||
</p>
|
||
<div class="tags">
|
||
<span class="tag">Sigmoid / ReLU</span>
|
||
<span class="tag">Backprop</span>
|
||
<span class="tag">MSE Loss</span>
|
||
<span class="tag">SGD</span>
|
||
<span class="tag">Heatmap</span>
|
||
</div>
|
||
<div class="stats">
|
||
<div class="stat"><span class="stat-val">5</span><span class="stat-lbl">Katman</span></div>
|
||
<div class="stat"><span class="stat-val">3</span><span class="stat-lbl">Canvas</span></div>
|
||
<div class="stat"><span class="stat-val">948</span><span class="stat-lbl">Satır JS</span></div>
|
||
<div class="stat"><span class="stat-val">0</span><span class="stat-lbl">Kütüphane</span></div>
|
||
</div>
|
||
<div class="launch-btn">
|
||
Başlat
|
||
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2">
|
||
<path d="M3 8h10M9 4l4 4-4 4"/>
|
||
</svg>
|
||
</div>
|
||
</a>
|
||
|
||
<!-- 03 FluidSim -->
|
||
<a class="card" href="/fluidsim/" target="_blank" id="card-fluid">
|
||
<div class="card-header">
|
||
<div class="icon">🌊</div>
|
||
<div class="card-title-block">
|
||
<div class="card-num">03 · Fizik</div>
|
||
<div class="card-title">FluidSim</div>
|
||
</div>
|
||
<div class="status-dot" title="Calisiyor"></div>
|
||
</div>
|
||
<p class="card-desc">
|
||
Jos Stam’ın <em>Stable Fluids</em> algoritmasının saf JavaScript uygulaması.
|
||
128×128 grid üzerinde gerçek zamanlı Navier-Stokes denklemi çözümü —
|
||
diffüzyon, adveksiyon, basınç projeksiyonu.
|
||
</p>
|
||
<div class="tags">
|
||
<span class="tag">Navier-Stokes</span>
|
||
<span class="tag">Gauss-Seidel</span>
|
||
<span class="tag">Semi-Lagrangian</span>
|
||
<span class="tag">putImageData</span>
|
||
<span class="tag">128x128 Grid</span>
|
||
</div>
|
||
<div class="stats">
|
||
<div class="stat"><span class="stat-val">128²</span><span class="stat-lbl">Grid</span></div>
|
||
<div class="stat"><span class="stat-val">20</span><span class="stat-lbl">Poisson</span></div>
|
||
<div class="stat"><span class="stat-val">200</span><span class="stat-lbl">Parçacık</span></div>
|
||
<div class="stat"><span class="stat-val">0</span><span class="stat-lbl">Kütüphane</span></div>
|
||
</div>
|
||
<div class="launch-btn">
|
||
Başlat
|
||
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2">
|
||
<path d="M3 8h10M9 4l4 4-4 4"/>
|
||
</svg>
|
||
</div>
|
||
</a>
|
||
|
||
</div>
|
||
|
||
<footer>NUMEX · ANTIGRAVITY · TEST93 · 2026</footer>
|
||
</div>
|
||
|
||
<script>
|
||
(function(){
|
||
'use strict';
|
||
const cv = document.getElementById('bgCanvas');
|
||
const ctx = cv.getContext('2d');
|
||
let W, H;
|
||
const STARS = [], ORBS = [];
|
||
const COLORS = ['#00f0ff','#b14fff','#ff4d9e','#39f5a0','#ffd166'];
|
||
|
||
function resize(){ W = cv.width = window.innerWidth; H = cv.height = window.innerHeight; }
|
||
|
||
function mkStar(){
|
||
return { x: Math.random()*W, y: Math.random()*H,
|
||
r: Math.random()*1.4+0.2, a: Math.random(),
|
||
sp: Math.random()*0.003+0.001, ph: Math.random()*Math.PI*2 };
|
||
}
|
||
function mkOrb(i){
|
||
return { x: W*(0.15+i*0.35), y: H*(0.2+Math.random()*0.6),
|
||
r: Math.random()*180+120,
|
||
vx:(Math.random()-.5)*0.3, vy:(Math.random()-.5)*0.2,
|
||
color: COLORS[i%COLORS.length] };
|
||
}
|
||
|
||
resize();
|
||
for(var i=0;i<180;i++) STARS.push(mkStar());
|
||
for(var i=0;i<3;i++) ORBS.push(mkOrb(i));
|
||
window.addEventListener('resize', resize);
|
||
|
||
var t=0;
|
||
function draw(){
|
||
t++;
|
||
ctx.clearRect(0,0,W,H);
|
||
|
||
/* neon toplar */
|
||
ORBS.forEach(function(o){
|
||
o.x+=o.vx; o.y+=o.vy;
|
||
if(o.x<-o.r||o.x>W+o.r) o.vx*=-1;
|
||
if(o.y<-o.r||o.y>H+o.r) o.vy*=-1;
|
||
var g=ctx.createRadialGradient(o.x,o.y,0,o.x,o.y,o.r);
|
||
g.addColorStop(0,o.color+'18'); g.addColorStop(1,o.color+'00');
|
||
ctx.fillStyle=g;
|
||
ctx.beginPath(); ctx.arc(o.x,o.y,o.r,0,Math.PI*2); ctx.fill();
|
||
});
|
||
|
||
/* yildizlar */
|
||
STARS.forEach(function(s){
|
||
var a=(Math.sin(t*s.sp+s.ph)*0.5+0.5)*s.a;
|
||
ctx.globalAlpha=a; ctx.fillStyle='#fff';
|
||
ctx.beginPath(); ctx.arc(s.x,s.y,s.r,0,Math.PI*2); ctx.fill();
|
||
});
|
||
ctx.globalAlpha=1;
|
||
|
||
/* grid */
|
||
ctx.strokeStyle='rgba(255,255,255,0.025)'; ctx.lineWidth=1;
|
||
for(var x=0;x<W;x+=60){ ctx.beginPath();ctx.moveTo(x,0);ctx.lineTo(x,H);ctx.stroke(); }
|
||
for(var y=0;y<H;y+=60){ ctx.beginPath();ctx.moveTo(0,y);ctx.lineTo(W,y);ctx.stroke(); }
|
||
|
||
requestAnimationFrame(draw);
|
||
}
|
||
draw();
|
||
})();
|
||
</script>
|
||
|
||
<script>
|
||
(function(){
|
||
'use strict';
|
||
/* FluidSim → pointer takipli 3D tilt + su lensi.
|
||
Saf vanilya JS; hiçbir kütüphane yok. Bu, kart 03'ün kendine özgü
|
||
hover davranışıdır (ikinci kartın 'levitasyon' ve birincinin
|
||
'beat-pulse'undan farklı). */
|
||
const fluid = document.getElementById('card-fluid');
|
||
if (!fluid) return;
|
||
let raf = null;
|
||
|
||
function tilt(e){
|
||
const r = fluid.getBoundingClientRect();
|
||
const px = (e.clientX - r.left) / r.width; // 0..1 x ekseni
|
||
const py = (e.clientY - r.top) / r.height; // 0..1 y ekseni
|
||
const rotY = (px - 0.5) * 12; // ±6°
|
||
const rotX = (0.5 - py) * -10; // ∓5° (kameraya doğru açar)
|
||
const tx = (px * 100).toFixed(1) + '%';
|
||
const ty = (py * 100).toFixed(1) + '%';
|
||
fluid.style.setProperty('--tx', tx);
|
||
fluid.style.setProperty('--ty', ty);
|
||
if (raf) cancelAnimationFrame(raf);
|
||
raf = requestAnimationFrame(function(){
|
||
fluid.style.transform = 'perspective(900px) rotateX(' + rotX.toFixed(2) + 'deg) rotateY(' + rotY.toFixed(2) + 'deg) translateZ(6px)';
|
||
});
|
||
}
|
||
|
||
fluid.addEventListener('mousemove', tilt);
|
||
fluid.addEventListener('mouseleave', function(){
|
||
if (raf) cancelAnimationFrame(raf);
|
||
fluid.style.transform = '';
|
||
fluid.style.removeProperty('--tx');
|
||
fluid.style.removeProperty('--ty');
|
||
});
|
||
fluid.addEventListener('mouseenter', function(){ fluid.style.willChange='transform'; });
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|