Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Döner Kebab Simulation</title>
- <style>
- body, html {
- margin: 0;
- padding: 0;
- overflow: hidden;
- background: #000;
- }
- canvas {
- display: block;
- width: 100%;
- height: 100%;
- background: linear-gradient(to bottom, #1a1a1a, #2b2b2b, #1a1a1a);
- }
- </style>
- </head>
- <body>
- <canvas id="canvas"></canvas>
- <script>
- (function(){
- const canvas = document.getElementById('canvas');
- const ctx = canvas.getContext('2d');
- let W, H;
- let centerX, radius, topY, bottomY;
- let alpha = 0; // rotation angle in radians
- // Texture generation
- const texSize = 128;
- const textureCanvas = document.createElement('canvas');
- textureCanvas.width = texSize;
- textureCanvas.height = texSize;
- const texCtx = textureCanvas.getContext('2d');
- const texImage = texCtx.createImageData(texSize, texSize);
- const texData = texImage.data;
- // Generates a seamless, blotchy meat texture
- function generateTexture() {
- const size = texSize;
- // Simple deterministic pseudo-random
- function noise(px, py) {
- const u = px / size;
- const v = py / size;
- // sum of sines for smooth periodic pattern
- let n = 0.5 + 0.5*Math.sin(u * 2 * Math.PI * 2 + v * 2 * Math.PI * 3) * 0.6;
- n += 0.4 * Math.cos(u * 2 * Math.PI * 3 - v * 2 * Math.PI * 5);
- n += 0.3 * Math.sin(u * 2 * Math.PI * 5 + v * 2 * Math.PI * 1);
- n += 0.2 * Math.cos(u * 2 * Math.PI * 1 + v * 2 * Math.PI * 7);
- // Add blotches using a second frequency
- const blotch = 0.5 + 0.5 * Math.sin(u * 2 * Math.PI * 10) * Math.sin(v * 2 * Math.PI * 8);
- n = (n + blotch) / 2;
- return n;
- }
- for (let y = 0; y < size; y++) {
- for (let x = 0; x < size; x++) {
- const n = noise(x, y);
- // Base meat color: brownish
- let r = 120 + 40 * n;
- let g = 55 + 20 * n;
- let b = 20 + 15 * n;
- // Add darker charry spots
- const spot = Math.sin(x * 0.05 + y * 0.07) * 0.5 + noise(x*2, y*2);
- if (spot > 0.65) {
- r *= 0.8; g *= 0.8; b *= 0.8;
- }
- // Add lighter fat streaks
- if (Math.sin(x * 0.1 + y * 0.02) > 0.8) {
- r += 30; g += 20; b += 10;
- }
- const idx = (y * size + x) * 4;
- texData[idx] = Math.min(255, Math.max(0, r));
- texData[idx+1] = Math.min(255, Math.max(0, g));
- texData[idx+2] = Math.min(255, Math.max(0, b));
- texData[idx+3] = 255;
- }
- }
- texCtx.putImageData(texImage, 0, 0);
- }
- generateTexture();
- // Offscreen canvas for kebab
- let kebabCanvas;
- function resize() {
- W = window.innerWidth;
- H = window.innerHeight;
- canvas.width = W;
- canvas.height = H;
- // Adjust positions based on window
- centerX = W * 0.42;
- radius = Math.min(W, H) * 0.16;
- topY = H * 0.12;
- bottomY = H * 0.83;
- // Prepare offscreen canvas for kebab
- const w = Math.ceil(2 * radius) + 2;
- const h = Math.ceil(bottomY - topY) + 2;
- kebabCanvas = document.createElement('canvas');
- kebabCanvas.width = w;
- kebabCanvas.height = h;
- }
- resize();
- window.addEventListener('resize', resize);
- // Utility to wrap a number in [0,1)
- function wrap01(x) {
- return x - Math.floor(x);
- }
- // Draw the kebab using image data mapping
- function drawKebab(time) {
- if (!kebabCanvas) return;
- const w = kebabCanvas.width;
- const h = kebabCanvas.height;
- const offCenterX = Math.ceil(radius);
- const offTop = topY;
- const ctxKeb = kebabCanvas.getContext('2d');
- const imgData = ctxKeb.createImageData(w, h);
- const data = imgData.data;
- const texData = texCtx.getImageData(0,0,texSize,texSize).data;
- const r = radius;
- const height = bottomY - topY;
- // Precompute lighting factor for angle: bright on right, dark on left
- // We'll compute per pixel.
- for (let y = 0; y < h; y++) {
- const v = y / (h - 1); // 0 at top, 1 at bottom
- // v coordinate for texture: we want vertical variation, but maybe not stretch? Use as is.
- const texY = Math.min(texSize-1, Math.floor(v * (texSize-1)));
- const yWorld = topY + y;
- // Skip if outside? We'll compute everything and set alpha 0 if not on cylinder.
- for (let x = 0; x < w; x++) {
- const dx = x - offCenterX; // relative to center
- const absDx = Math.abs(dx);
- // Check if within cylinder radius
- if (absDx <= r) {
- const phi = Math.asin(dx / r); // angle in [-π/2, π/2]
- // Texture u: (phi + alpha) / 2π
- let u = (phi + alpha) / (2 * Math.PI);
- u = wrap01(u);
- // Sample texture
- const texX = Math.min(texSize-1, Math.floor(u * (texSize-1)));
- const idx = (texY * texSize + texX) * 4;
- let R = texData[idx];
- let G = texData[idx+1];
- let B = texData[idx+2];
- // Lighting: right side (phi>0) lit, left dark.
- let light = 0.4 + 0.6 * (phi + Math.PI/2) / Math.PI; // 0 to 1
- // Add a slight vertical variation: hotter near middle
- const verticalLight = 0.85 + 0.15 * Math.sin(v * Math.PI);
- light *= verticalLight;
- // Add flicker from flames? Maybe based on time? We'll keep static.
- R = Math.min(255, Math.max(0, R * light));
- G = Math.min(255, Math.max(0, G * light));
- B = Math.min(255, Math.max(0, B * light));
- // Set pixel
- const dataIdx = (y * w + x) * 4;
- data[dataIdx] = R;
- data[dataIdx+1] = G;
- data[dataIdx+2] = B;
- data[dataIdx+3] = 255;
- } else {
- // Outside cylinder: transparent
- const dataIdx = (y * w + x) * 4;
- data[dataIdx+3] = 0;
- }
- }
- }
- ctxKeb.putImageData(imgData, 0, 0);
- // Draw the kebab canvas onto main canvas
- ctx.drawImage(kebabCanvas, centerX - offCenterX, topY);
- }
- // Draw the skewer stick (vertical metal rod)
- function drawSkewer() {
- ctx.save();
- const stickWidth = Math.max(2, radius * 0.06);
- // Metal gradient
- const gradient = ctx.createLinearGradient(centerX - stickWidth/2, 0, centerX + stickWidth/2, 0);
- gradient.addColorStop(0, '#888');
- gradient.addColorStop(0.5, '#ddd');
- gradient.addColorStop(1, '#555');
- ctx.fillStyle = gradient;
- ctx.fillRect(centerX - stickWidth/2, topY - 20, stickWidth, bottomY - topY + 40);
- // Add top and bottom handles
- ctx.fillStyle = '#999';
- ctx.beginPath();
- ctx.arc(centerX, topY - 20, stickWidth*1.2, 0, Math.PI*2);
- ctx.fill();
- ctx.fillStyle = '#aaa';
- ctx.beginPath();
- ctx.arc(centerX, bottomY + 20, stickWidth*1.2, 0, Math.PI*2);
- ctx.fill();
- ctx.restore();
- }
- // Draw heating element with flickering gas flames
- function drawHeatingElement(time) {
- // Position: to the right of the kebab
- const bx = centerX + radius + 35; // heating element x
- const by = topY - 10;
- const bh = bottomY - topY + 20;
- // Metal backing: dark red glowing grill
- const metalGrad = ctx.createLinearGradient(bx - 10, 0, bx + 10, 0);
- metalGrad.addColorStop(0, '#300');
- metalGrad.addColorStop(0.5, '#500');
- metalGrad.addColorStop(1, '#200');
- ctx.fillStyle = metalGrad;
- ctx.fillRect(bx - 5, by, 15, bh);
- // Gas flames: multiple small burners
- const flameCount = 8;
- const flameSpacing = bh / (flameCount + 1);
- for (let i = 0; i < flameCount; i++) {
- const fy = by + (i+1) * flameSpacing;
- // Flicker using time and index
- const flicker = 0.6 + 0.4 * Math.sin(time * 0.005 + i * 1.7);
- const flameHeight = 30 * flicker + 15;
- // Flame shape: drawn as an ellipse with gradient
- const grad = ctx.createRadialGradient(bx + 7, fy - 5, 0, bx + 7, fy, flameHeight*0.8);
- grad.addColorStop(0, '#ffdd00');
- grad.addColorStop(0.3, '#ff8800');
- grad.addColorStop(0.6, '#ff4400');
- grad.addColorStop(1, 'rgba(50,0,0,0)');
- ctx.fillStyle = grad;
- ctx.beginPath();
- ctx.ellipse(bx + 7, fy - flameHeight/2, flameHeight*0.5, flameHeight*0.9, 0, 0, Math.PI*2);
- ctx.fill();
- // Inner blue flame
- const grad2 = ctx.createRadialGradient(bx + 7, fy, 0, bx + 7, fy, flameHeight*0.4);
- grad2.addColorStop(0, 'rgba(0,200,255,0.8)');
- grad2.addColorStop(0.5, 'rgba(0,100,200,0.4)');
- grad2.addColorStop(1, 'rgba(0,0,100,0)');
- ctx.fillStyle = grad2;
- ctx.beginPath();
- ctx.ellipse(bx + 7, fy - flameHeight*0.3, flameHeight*0.2, flameHeight*0.6, 0, 0, Math.PI*2);
- ctx.fill();
- }
- // Ambient glow on the right side
- const glow = ctx.createRadialGradient(bx + 7, by + bh/2, 0, bx + 7, by + bh/2, radius*3);
- glow.addColorStop(0, 'rgba(255,80,0,0.35)');
- glow.addColorStop(1, 'rgba(255,80,0,0)');
- ctx.fillStyle = glow;
- ctx.fillRect(0,0,W,H);
- }
- // Draw some dripping fat and smoke
- function drawExtras(time) {
- // Fat drips from kebab bottom
- const dripCount = 3;
- for (let i=0; i<dripCount; i++) {
- const x = centerX + radius * 0.5 * Math.sin(time*0.001 + i*2);
- const y = bottomY - 10 + Math.sin(time*0.01 + i) * 5;
- const size = 2 + Math.sin(time*0.02+i)*1;
- ctx.fillStyle = 'rgba(255,220,150,0.6)';
- ctx.beginPath();
- ctx.ellipse(x, y, size, size*1.5, 0, 0, Math.PI*2);
- ctx.fill();
- }
- // Smoke particles
- ctx.fillStyle = 'rgba(128,128,128,0.15)';
- for (let i=0; i<10; i++) {
- const x = centerX + radius * (Math.sin(time*0.0007 + i) * 0.8);
- const y = topY + Math.random() * (bottomY - topY);
- const size = 10 + 5 * Math.sin(time*0.003 + i);
- ctx.beginPath();
- ctx.arc(x, y, size, 0, Math.PI*2);
- ctx.fill();
- }
- }
- // Draw background tile wall
- function drawBackground() {
- // Dark gradient already set in style? We'll override with canvas
- const grad = ctx.createLinearGradient(0,0,0,H);
- grad.addColorStop(0, '#1a1a1a');
- grad.addColorStop(0.5, '#222222');
- grad.addColorStop(1, '#1a1a1a');
- ctx.fillStyle = grad;
- ctx.fillRect(0,0,W,H);
- // Subtle tiles
- ctx.strokeStyle = 'rgba(255,255,255,0.05)';
- ctx.lineWidth = 1;
- const tileSize = 80;
- for (let y=0; y<H; y+=tileSize) {
- for (let x=0; x<W; x+=tileSize) {
- ctx.strokeRect(x, y, tileSize, tileSize);
- }
- }
- // Light from flame on the right wall
- ctx.fillStyle = 'rgba(255,120,0,0.05)';
- ctx.fillRect(0,0,W,H);
- }
- // Main animation loop
- function animate(time) {
- // Update rotation speed
- alpha += 0.018; // radians per frame
- // Clear and draw background
- drawBackground();
- // Draw heating element (behind kebab)
- drawHeatingElement(time);
- // Draw kebab
- drawKebab(time);
- // Draw skewer (should be on top of kebab? Actually skewer goes through, visible where meat ends)
- // We draw after kebab so it's visible
- drawSkewer();
- // Extras like drips and smoke
- drawExtras(time);
- requestAnimationFrame(animate);
- }
- requestAnimationFrame(animate);
- })();
- </script>
- </body>
- </html>
Advertisement
Add Comment
Please, Sign In to add comment