Files

394 lines
16 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Static TV Cat Component</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
user-select: none;
}
.crt-wrapper {
background: #080808;
padding: 20px 24px 24px 24px;
border-radius: 56px 56px 64px 64px;
box-shadow: 0 25px 45px rgba(0,0,0,0.8), inset 0 1px 0 rgba(255,255,255,0.05);
border: 1px solid #2a2520;
position: relative;
display: inline-block;
}
.crt-wrapper::before {
content: "";
position: absolute;
top: 12px;
left: 20%;
width: 60%;
height: 8px;
background: radial-gradient(ellipse, rgba(70,200,140,0.3), transparent);
filter: blur(6px);
border-radius: 50%;
pointer-events: none;
}
.cat-artifact {
background: black;
border-radius: 28px;
padding: 16px;
box-shadow: inset 0 0 30px rgba(0,0,0,0.7), 0 12px 24px rgba(0,0,0,0.5);
position: relative;
}
.pixel-canvas-wrapper {
position: relative;
display: inline-block;
background: #000;
border-radius: 20px;
overflow: hidden;
box-shadow: 0 0 0 3px #3a3530, 0 0 0 7px #0e0e0e;
}
canvas {
display: block;
margin: 0 auto;
image-rendering: crisp-edges;
image-rendering: pixelated;
filter: url(#heavyStaticFilter) blur(0.6px) contrast(1.3) brightness(1.05);
}
.scanlines-heavy {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
background: repeating-linear-gradient(0deg,
rgba(0,0,0,0.35) 0px,
rgba(0,0,0,0.35) 2px,
transparent 2px,
transparent 5px);
z-index: 5;
border-radius: 20px;
animation: scanlineShift 0.2s linear infinite;
}
@keyframes scanlineShift {
0% { background-position: 0 0; }
100% { background-position: 0 4px; }
}
.static-overlay-heavy {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 300"><filter id="tvNoiseHeavy"><feTurbulence type="fractalNoise" baseFrequency="1.2" numOctaves="5" stitchTiles="stitch"><animate attributeName="baseFrequency" values="1.2;1.8;1.2" dur="0.12s" repeatCount="indefinite" /></filter><rect width="100%" height="100%" filter="url(%23tvNoiseHeavy)" opacity="0.7"/></svg>');
background-repeat: repeat;
background-size: 120px 90px;
mix-blend-mode: screen;
opacity: 0.65;
border-radius: 20px;
z-index: 3;
animation: staticIntense 0.08s infinite;
}
@keyframes staticIntense {
0% { background-position: 0% 0%; opacity: 0.6; mix-blend-mode: screen; }
25% { background-position: 3% 2%; opacity: 0.75; mix-blend-mode: hard-light; }
50% { background-position: -2% 1%; opacity: 0.65; mix-blend-mode: screen; }
75% { background-position: 4% 3%; opacity: 0.8; }
100% { background-position: 0% 0%; opacity: 0.6; }
}
.sparkle-static {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
background-image: radial-gradient(circle at 30% 45%, rgba(255,255,220,0.12) 1px, transparent 1px);
background-size: 12px 12px;
mix-blend-mode: overlay;
animation: sparkleFlicker 0.1s infinite;
z-index: 4;
border-radius: 20px;
}
@keyframes sparkleFlicker {
0% { opacity: 0.4; background-size: 10px 10px; }
50% { opacity: 0.7; background-size: 14px 14px; }
100% { opacity: 0.4; background-size: 10px 10px; }
}
/* VHS glitch */
.glitch-flash {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(255,255,240,0.03);
pointer-events: none;
animation: vhsFlicker 0.05s infinite;
z-index: 2;
border-radius: 20px;
}
@keyframes vhsFlicker {
0% { opacity: 0.04; background: rgba(255,240,200,0.02); }
33% { opacity: 0.12; background: rgba(0,0,0,0.08); }
66% { opacity: 0.06; background: rgba(180,210,255,0.03); }
100% { opacity: 0.04; background: rgba(0,0,0,0.02); }
}
/* rolling noise bar */
.rolling-bar {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 6px;
background: rgba(255,255,240,0.2);
filter: blur(3px);
animation: rollDown 1.8s linear infinite;
pointer-events: none;
z-index: 6;
border-radius: 20px;
}
@keyframes rollDown {
0% { top: -10px; opacity: 0; }
10% { opacity: 0.5; }
90% { opacity: 0.3; }
100% { top: 100%; opacity: 0; }
}
.crt-glow {
position: absolute;
bottom: -6px;
left: 15%;
width: 70%;
height: 16px;
background: radial-gradient(ellipse, rgba(90,220,150,0.25), transparent);
filter: blur(10px);
pointer-events: none;
}
</style>
</head>
<body style="margin:0; padding:0; background:transparent; display:flex; justify-content:center; align-items:center; min-height:100vh;">
<div class="crt-wrapper">
<div class="cat-artifact">
<div class="pixel-canvas-wrapper">
<canvas id="pixelCatCanvas" width="416" height="352" style="width:100%; height:auto; max-width:416px; border-radius: 16px;"></canvas>
<div class="scanlines-heavy"></div>
<div class="static-overlay-heavy"></div>
<div class="sparkle-static"></div>
<div class="glitch-flash"></div>
<div class="rolling-bar"></div>
</div>
<div class="crt-glow"></div>
</div>
</div>
<!-- SVG STATIC FILTER -->
<svg style="position: absolute; width: 0; height: 0; overflow: visible;" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="heavyStaticFilter" x="-15%" y="-15%" width="130%" height="130%">
<feGaussianBlur in="SourceGraphic" stdDeviation="0.55" result="bloom" />
<feComponentTransfer in="bloom" result="boostContrast">
<feFuncR type="linear" slope="1.45" intercept="-0.2"/>
<feFuncG type="linear" slope="1.45" intercept="-0.2"/>
<feFuncB type="linear" slope="1.45" intercept="-0.2"/>
<feFuncA type="linear" slope="1" intercept="0"/>
</feComponentTransfer>
<feTurbulence type="fractalNoise" baseFrequency="2.2" numOctaves="4" result="intenseNoise">
<animate attributeName="baseFrequency" values="2.1;2.5;2.1" dur="0.08s" repeatCount="indefinite" />
</feTurbulence>
<feDisplacementMap in="boostContrast" in2="intenseNoise" scale="1.8" xChannelSelector="R" yChannelSelector="G" result="displacedStatic"/>
<feColorMatrix type="matrix" values="1.1 0 0 0 0.05 0 1.05 0 0 0.02 0 0 1.1 0 0.03 0 0 0 1 0" in="displacedStatic" result="colorNoise"/>
<feGaussianBlur in="colorNoise" stdDeviation="0.3" result="finalStatic"/>
</filter>
</defs>
</svg>
<script>
(function() {
// CAT PIXEL DATA
const originalCatRows = [
[0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0],
[0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0],
[0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0],
[0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0],
[0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0],
[0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0],
[0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0],
[0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0],
[0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0],
[0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0],
[0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,0,0],
[0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0],
[0,0,1,1,1,1,1,1,0,1,0,1,1,1,0,1,1,1,1,1,1,1,1,0,0,0],
[0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0],
[0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0],
[0,0,0,0,0,0,1,1,1,1,0,0,1,1,0,1,1,1,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
];
let currentCatRows = JSON.parse(JSON.stringify(originalCatRows));
const rows = 22, cols = 26;
const cellSize = 16;
const canvas = document.getElementById('pixelCatCanvas');
const ctx = canvas.getContext('2d');
const leftEyeCols = [6, 7, 8, 9];
const rightEyeCols = [15, 16, 17, 18];
const eyeRow = 12;
let leftPupilCol = 7;
let rightPupilCol = 16;
let isBlinking = false;
function updatePupilInMatrix() {
for (let col of leftEyeCols) {
if (currentCatRows[eyeRow][col] === 1) currentCatRows[eyeRow][col] = 0;
}
for (let col of rightEyeCols) {
if (currentCatRows[eyeRow][col] === 1) currentCatRows[eyeRow][col] = 0;
}
if (leftEyeCols.includes(leftPupilCol)) currentCatRows[eyeRow][leftPupilCol] = 1;
else currentCatRows[eyeRow][7] = 1;
if (rightEyeCols.includes(rightPupilCol)) currentCatRows[eyeRow][rightPupilCol] = 1;
else currentCatRows[eyeRow][16] = 1;
}
function drawCatFromMatrix() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
const val = currentCatRows[row][col];
const x = col * cellSize;
const y = row * cellSize;
if (val === 1) {
ctx.fillStyle = "#12100c";
ctx.fillRect(x, y, cellSize, cellSize);
for (let i = 0; i < 12; i++) {
const dx = x + 2 + Math.random() * (cellSize - 4);
const dy = y + 2 + Math.random() * (cellSize - 4);
ctx.beginPath();
ctx.arc(dx, dy, 0.7 + Math.random() * 1.7, 0, Math.PI*2);
ctx.fillStyle = `rgba(35,30,24,${0.5 + Math.random() * 0.5})`;
ctx.fill();
}
if (Math.random() > 0.78) {
ctx.beginPath();
ctx.arc(x + Math.random() * cellSize, y + Math.random() * cellSize, 1.1, 0, Math.PI*2);
ctx.fillStyle = `rgba(210,200,180,0.6)`;
ctx.fill();
}
} else {
ctx.fillStyle = "#f5f2ea";
ctx.fillRect(x, y, cellSize, cellSize);
for (let d = 0; d < 4; d++) {
ctx.fillStyle = `rgba(0,0,0,0.07)`;
ctx.beginPath();
ctx.arc(x + Math.random() * cellSize, y + Math.random() * cellSize, 0.9, 0, Math.PI*2);
ctx.fill();
}
if (Math.random() > 0.92) {
ctx.fillStyle = `rgba(30,25,20,0.4)`;
ctx.beginPath();
ctx.arc(x + Math.random() * cellSize, y + Math.random() * cellSize, 1.3, 0, Math.PI*2);
ctx.fill();
}
}
}
}
for (let i = 0; i < 1200; i++) {
const randX = Math.random() * canvas.width;
const randY = Math.random() * canvas.height;
ctx.beginPath();
ctx.arc(randX, randY, 0.6 + Math.random() * 1.8, 0, Math.PI * 2);
ctx.fillStyle = `rgba(0,0,0,${0.1 + Math.random() * 0.35})`;
ctx.fill();
}
for (let s = 0; s < 400; s++) {
const sx = Math.random() * canvas.width;
const sy = Math.random() * canvas.height;
ctx.fillStyle = `rgba(230,220,190,${0.2 + Math.random() * 0.5})`;
ctx.fillRect(sx, sy, 1.5 + Math.random() * 3, 1);
}
for (let b = 0; b < 45; b++) {
const bandY = Math.random() * canvas.height;
ctx.fillStyle = `rgba(0,0,0,0.12)`;
ctx.fillRect(0, bandY, canvas.width, 1.5);
}
}
function performBlink() {
if (isBlinking) return;
isBlinking = true;
const savedLeft = leftPupilCol;
const savedRight = rightPupilCol;
for (let col of leftEyeCols) currentCatRows[eyeRow][col] = 1;
for (let col of rightEyeCols) currentCatRows[eyeRow][col] = 1;
drawCatFromMatrix();
setTimeout(() => {
for (let col of leftEyeCols) currentCatRows[eyeRow][col] = 0;
for (let col of rightEyeCols) currentCatRows[eyeRow][col] = 0;
leftPupilCol = savedLeft;
rightPupilCol = savedRight;
updatePupilInMatrix();
drawCatFromMatrix();
isBlinking = false;
}, 130);
}
setInterval(() => {
if (isBlinking) return;
leftPupilCol = (leftPupilCol === 7) ? 8 : 7;
rightPupilCol = (rightPupilCol === 16) ? 17 : 16;
updatePupilInMatrix();
drawCatFromMatrix();
}, 480);
setInterval(() => {
if (!isBlinking) performBlink();
}, 2600 + Math.random() * 2400);
const wrapper = document.querySelector('.pixel-canvas-wrapper');
wrapper.addEventListener('mouseenter', () => { if (!isBlinking) performBlink(); });
wrapper.addEventListener('click', () => {
if (!isBlinking) performBlink();
setTimeout(() => { if (!isBlinking) performBlink(); }, 180);
});
for (let col of leftEyeCols) currentCatRows[eyeRow][col] = 0;
for (let col of rightEyeCols) currentCatRows[eyeRow][col] = 0;
leftPupilCol = 7;
rightPupilCol = 16;
updatePupilInMatrix();
drawCatFromMatrix();
setInterval(() => {
if (!isBlinking) drawCatFromMatrix();
}, 180);
})();
</script>
</body>
</html>