| Server IP : 127.0.0.1 / Your IP : 216.73.216.109 Web Server : Apache/2.4.54 (Win64) OpenSSL/1.1.1q PHP/8.1.10 System : Windows NT DESKTOP-E5T4RUN 10.0 build 19045 (Windows 10) AMD64 User : SERVERWEB ( 0) PHP Version : 8.1.10 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : OFF | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : C:/Users/SERVERWEB/AppData/Local/Microsoft/OneDrive/26.074.0420.0001/WebAssets/ |
Upload File : |
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Photo Cascade</title><script src="/static/platform/ecs.js"></script><script src="/static/view-lib/sql.js"></script><script src="/static/platform/vibe-telemetry.js"></script><script src="/static/platform/od4-fetch.js"></script><script src="/static/view-lib/theme-sync.js"></script><script src="/static/view-lib/user-activity.js"></script><link rel="stylesheet" href="/static/tokens/scrollbar.css"><style>*{margin:0;padding:0;box-sizing:border-box}body{background:#000;overflow:hidden;height:100vh}canvas{display:block}canvas.cascade-mode{filter:sepia(1) hue-rotate(90deg) saturate(3) brightness(.8)}#loading{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;background:#000;color:#888;font-size:14px;z-index:20;transition:opacity .6s}#loading.hidden{opacity:0;pointer-events:none}.controls{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);display:flex;align-items:center;gap:14px;z-index:10;background:rgba(0,0,0,.75);border:1px solid rgba(255,255,255,.1);border-radius:10px;padding:10px 20px}.ctrl-btn{background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.12);color:rgba(255,255,255,.5);border-radius:6px;cursor:pointer;transition:background .15s,color .15s;padding:6px 14px;font-size:12px;font-family:inherit}.ctrl-btn:hover{background:rgba(255,255,255,.14);color:rgba(255,255,255,.85)}.ctrl-btn.active{background:rgba(0,255,80,.2);border-color:#0f6;color:#0f6}.ctrl-sep{width:1px;height:24px;background:rgba(255,255,255,.08)}.ctrl-hint{font-size:10px;color:#444}</style></head><body><div id="loading">Loading photos...</div><canvas id="c"></canvas><div class="controls"><button class="ctrl-btn" id="cascadeBtn">Cascade Mode</button><div class="ctrl-sep"></div><span class="ctrl-hint">Click a column to freeze it</span></div><script>const canvas=document.getElementById("c"),ctx=canvas.getContext("2d"),PHOTO_SIZE=48,GAP=3,CELL=51;let columns=[],cascadeMode=!1;function resize(){canvas.width=window.innerWidth,canvas.height=window.innerHeight,ctx.fillStyle="#000",ctx.fillRect(0,0,canvas.width,canvas.height)}async function init(){const t=canvas.width,e=canvas.height,a=Math.ceil(t/51)+1,n=Math.ceil(e/51)+4,o=Math.min(a*n,500),s=await fetch("/api/thumbnails?offset=0&limit="+o),c=(await s.json()).items.filter(t=>t.thumbnailId);for(let t=c.length-1;t>0;t--){const e=Math.floor(Math.random()*(t+1));[c[t],c[e]]=[c[e],c[t]]}const i=[];for(let t=0;t<o;t++){const e=c[t%c.length];i.push(new Promise(t=>{const a=new Image;a.onload=()=>t(a),a.onerror=()=>t(null),a.src="/api/thumbnail/"+e.thumbnailId}))}const l=(await Promise.all(i)).filter(Boolean);if(l.length){for(let t=0;t<a;t++){const e=Math.random(),a=[];for(let e=0;e<n;e++)a.push(l[(t*n+e)%l.length]);columns.push({x:51*t,speed:.3+2.5*e,offset:Math.random()*n*51,photos:a,totalHeight:51*n,frozen:!1,brightness:.15+.65*e,delay:2*Math.random()})}document.getElementById("loading").classList.add("hidden"),startTime=performance.now(),requestAnimationFrame(animate)}}resize();let startTime=0;function animate(t){const e=(t-startTime)/1e3,a=canvas.width,n=canvas.height;ctx.fillStyle=cascadeMode?"rgba(0,0,0,0.04)":"rgba(0,0,0,0.1)",ctx.fillRect(0,0,a,n);for(const t of columns){if(e<t.delay)continue;t.frozen||(t.offset=(t.offset+t.speed)%t.totalHeight);let a=-1/0,o=-1;for(let e=0;e<t.photos.length;e++){let s=51*e-t.offset;for(;s<-51;)s+=t.totalHeight;s>=0&&s<n&&s>a&&(a=s,o=e)}for(let e=0;e<t.photos.length;e++){let a=51*e-t.offset;for(;a<-51;)a+=t.totalHeight;a>n+51||(ctx.globalAlpha=e===o?Math.min(1,t.brightness+.4):t.brightness,ctx.drawImage(t.photos[e],t.x,a,48,48))}t.frozen&&(ctx.globalAlpha=.4,ctx.strokeStyle="#7b9dff",ctx.lineWidth=1,ctx.strokeRect(t.x-1,0,50,n))}ctx.globalAlpha=1,requestAnimationFrame(animate)}document.getElementById("cascadeBtn").onclick=()=>{cascadeMode=!cascadeMode,canvas.classList.toggle("cascade-mode",cascadeMode),document.getElementById("cascadeBtn").classList.toggle("active",cascadeMode)},canvas.addEventListener("click",t=>{const e=Math.floor(t.clientX/51);e>=0&&e<columns.length&&(columns[e].frozen=!columns[e].frozen)}),window.addEventListener("resize",resize),init()</script></body></html>