394 lines
16 KiB
HTML
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> |