Guest User

Untitled

a guest
Aug 1st, 2026
50
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
HTML 15.64 KB | Software | 0 0
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6.     <title>3D Rubik's Cube Animation</title>
  7.     <style>
  8.         body {
  9.             margin: 0;
  10.             display: flex;
  11.             justify-content: center;
  12.             align-items: center;
  13.             height: 100vh;
  14.             background: #1a1a2e;
  15.             overflow: hidden;
  16.         }
  17.         canvas {
  18.             background: #1a1a2e;
  19.             display: block;
  20.         }
  21.     </style>
  22. </head>
  23. <body>
  24.     <canvas id="c" width="850" height="850"></canvas>
  25.     <script>
  26.         (function() {
  27.             const canvas = document.getElementById('c');
  28.             const ctx = canvas.getContext('2d');
  29.             const W = canvas.width;
  30.             const H = canvas.height;
  31.  
  32.             // ====== COLORS ======
  33.             const C = {
  34.                 red: '#E41B17',
  35.                 green: '#00B550',
  36.                 blue: '#1E5AFF',
  37.                 orange: '#FF6B00',
  38.                 white: '#F0F0F0',
  39.                 yellow: '#FFD500',
  40.                 black: '#111111'
  41.             };
  42.  
  43.             // ====== MATRIX HELPERS ======
  44.             function rotX(a) { let c = Math.cos(a), s = Math.sin(a); return [[1,0,0],[0,c,-s],[0,s,c]]; }
  45.             function rotY(a) { let c = Math.cos(a), s = Math.sin(a); return [[c,0,s],[0,1,0],[-s,0,c]]; }
  46.             function rotZ(a) { let c = Math.cos(a), s = Math.sin(a); return [[c,-s,0],[s,c,0],[0,0,1]]; }
  47.             function matMul(A, B) {
  48.                 let R = [[0,0,0],[0,0,0],[0,0,0]];
  49.                 for (let i = 0; i < 3; i++)
  50.                    for (let j = 0; j < 3; j++)
  51.                        R[i][j] = A[i][0]*B[0][j] + A[i][1]*B[1][j] + A[i][2]*B[2][j];
  52.                return R;
  53.            }
  54.            function matApply(M, v) {
  55.                return [
  56.                    M[0][0]*v[0] + M[0][1]*v[1] + M[0][2]*v[2],
  57.                    M[1][0]*v[0] + M[1][1]*v[1] + M[1][2]*v[2],
  58.                    M[2][0]*v[0] + M[2][1]*v[1] + M[2][2]*v[2]
  59.                ];
  60.            }
  61.            function getRotMat(axis, angle) {
  62.                if (axis === 'x') return rotX(angle);
  63.                if (axis === 'y') return rotY(angle);
  64.                return rotZ(angle);
  65.            }
  66.  
  67.            // ====== CUBIES ======
  68.            let cubies = [];
  69.            for (let x = -1; x <= 1; x++)
  70.                for (let y = -1; y <= 1; y++)
  71.                    for (let z = -1; z <= 1; z++)
  72.                        cubies.push({
  73.                            x: x, y: y, z: z,
  74.                            ox: x, oy: y, oz: z,
  75.                            rot: [[1,0,0],[0,1,0],[0,0,1]]
  76.                        });
  77.  
  78.            // ====== FACE COLORS ======
  79.            function getFaceColors(cubie) {
  80.                let cols = [null, null, null, null, null, null];
  81.                if (cubie.ox === 1) cols[0] = C.red;
  82.                if (cubie.ox === -1) cols[1] = C.orange;
  83.                if (cubie.oy === 1) cols[2] = C.white;
  84.                if (cubie.oy === -1) cols[3] = C.yellow;
  85.                if (cubie.oz === 1) cols[4] = C.green;
  86.                if (cubie.oz === -1) cols[5] = C.blue;
  87.                return cols;
  88.            }
  89.  
  90.            const FACE_NORMALS = [[1,0,0],[-1,0,0],[0,1,0],[0,-1,0],[0,0,1],[0,0,-1]];
  91.  
  92.            // ====== FACE VERTICES ======
  93.            function getFaceVerts(face, inset) {
  94.                let h = inset;
  95.                switch (face) {
  96.                    case 0: return [[0.5,h,h],[0.5,h,-h],[0.5,-h,-h],[0.5,-h,h]];
  97.                    case 1: return [[-0.5,h,-h],[-0.5,h,h],[-0.5,-h,h],[-0.5,-h,-h]];
  98.                    case 2: return [[h,0.5,h],[-h,0.5,h],[-h,0.5,-h],[h,0.5,-h]];
  99.                    case 3: return [[h,-0.5,-h],[-h,-0.5,-h],[-h,-0.5,h],[h,-0.5,h]];
  100.                    case 4: return [[h,h,0.5],[-h,h,0.5],[-h,-h,0.5],[h,-h,0.5]];
  101.                    case 5: return [[h,h,-0.5],[h,-h,-0.5],[-h,-h,-0.5],[-h,h,-0.5]];
  102.                }
  103.            }
  104.  
  105.            // ====== MOVE DEFINITIONS ======
  106.            function getMove(face) {
  107.                switch (face) {
  108.                    case 'R':  return { axis: 'x', layer: 1, angle: -Math.PI/2 };
  109.                    case 'R\'': return { axis: 'x', layer: 1, angle: Math.PI/2 };
  110.                    case 'L':  return { axis: 'x', layer: -1, angle: Math.PI/2 };
  111.                    case 'L\'': return { axis: 'x', layer: -1, angle: -Math.PI/2 };
  112.                    case 'U':  return { axis: 'y', layer: 1, angle: Math.PI/2 };
  113.                    case 'U\'': return { axis: 'y', layer: 1, angle: -Math.PI/2 };
  114.                    case 'D':  return { axis: 'y', layer: -1, angle: -Math.PI/2 };
  115.                    case 'D\'': return { axis: 'y', layer: -1, angle: Math.PI/2 };
  116.                    case 'F':  return { axis: 'z', layer: 1, angle: -Math.PI/2 };
  117.                    case 'F\'': return { axis: 'z', layer: 1, angle: Math.PI/2 };
  118.                    case 'B':  return { axis: 'z', layer: -1, angle: Math.PI/2 };
  119.                    case 'B\'': return { axis: 'z', layer: -1, angle: -Math.PI/2 };
  120.                }
  121.            }
  122.  
  123.            // ====== ANIMATION STATE ======
  124.            let anim = null;
  125.            let phase = 0;
  126.            let moveIdx = 0;
  127.            let phaseStart = 0;
  128.            let startTime = null;
  129.  
  130.            const MOVE_DURATION = 0.5;
  131.            const INITIAL_DURATION = 2;
  132.            const PAUSE_DURATION = 2;
  133.  
  134.            const scrambleMoves = ['R','U','F\'','D','L\'','B','R\'','U\'','F','D\''];
  135.            const solveMoves = ['D','F\'','U','R','B\'','L','D\'','F','U\'','R\''];
  136.  
  137.            function startMove(moveDef) {
  138.                let affected = cubies.filter(c => {
  139.                     if (moveDef.axis === 'x') return c.x === moveDef.layer;
  140.                     if (moveDef.axis === 'y') return c.y === moveDef.layer;
  141.                     return c.z === moveDef.layer;
  142.                 });
  143.                 let originals = affected.map(c => ({
  144.                     x: c.x, y: c.y, z: c.z,
  145.                     rot: c.rot.map(row => [...row])
  146.                 }));
  147.                 anim = {
  148.                     moveDef: moveDef,
  149.                     affected: affected,
  150.                     originals: originals,
  151.                     startTime: performance.now() / 1000,
  152.                     duration: MOVE_DURATION
  153.                 };
  154.             }
  155.  
  156.             function completeMove() {
  157.                 let moveDef = anim.moveDef;
  158.                 let rotMat = getRotMat(moveDef.axis, moveDef.angle);
  159.                 for (let i = 0; i < anim.affected.length; i++) {
  160.                    let c = anim.affected[i];
  161.                    let orig = anim.originals[i];
  162.                    let pos = matApply(rotMat, [orig.x, orig.y, orig.z]);
  163.                    c.x = Math.round(pos[0]);
  164.                    c.y = Math.round(pos[1]);
  165.                    c.z = Math.round(pos[2]);
  166.                    c.rot = matMul(rotMat, orig.rot);
  167.                }
  168.                anim = null;
  169.            }
  170.  
  171.            function updateState(time) {
  172.                if (startTime === null) {
  173.                    startTime = time;
  174.                    phaseStart = time;
  175.                }
  176.  
  177.                // Handle animation completion
  178.                if (anim) {
  179.                    if (time - anim.startTime >= anim.duration) {
  180.                         completeMove();
  181.                         moveIdx++;
  182.                         if (phase === 1) {
  183.                             if (moveIdx >= scrambleMoves.length) {
  184.                                 phase = 2;
  185.                                 phaseStart = time;
  186.                             } else {
  187.                                 startMove(getMove(scrambleMoves[moveIdx]));
  188.                             }
  189.                         } else if (phase === 3) {
  190.                             if (moveIdx >= solveMoves.length) {
  191.                                 phase = 4;
  192.                                 phaseStart = time;
  193.                             } else {
  194.                                 startMove(getMove(solveMoves[moveIdx]));
  195.                             }
  196.                         }
  197.                     }
  198.                 }
  199.  
  200.                 // Phase transitions
  201.                 if (!anim) {
  202.                     if (phase === 0 && time - phaseStart >= INITIAL_DURATION) {
  203.                        phase = 1;
  204.                         phaseStart = time;
  205.                         moveIdx = 0;
  206.                         startMove(getMove(scrambleMoves[0]));
  207.                     } else if (phase === 2 && time - phaseStart >= PAUSE_DURATION) {
  208.                        phase = 3;
  209.                         phaseStart = time;
  210.                         moveIdx = 0;
  211.                         startMove(getMove(solveMoves[0]));
  212.                     }
  213.                 }
  214.             }
  215.  
  216.             // ====== GET CUBIE STATE (with animation) ======
  217.             function getCubieState(cubie) {
  218.                 if (anim && anim.affected.includes(cubie)) {
  219.                    let idx = anim.affected.indexOf(cubie);
  220.                     let orig = anim.originals[idx];
  221.                     let t = Math.min((performance.now() / 1000 - anim.startTime) / anim.duration, 1);
  222.                     let angle = anim.moveDef.angle * t;
  223.                     let rotMat = getRotMat(anim.moveDef.axis, angle);
  224.                     let pos = matApply(rotMat, [orig.x, orig.y, orig.z]);
  225.                     let rot = matMul(rotMat, orig.rot);
  226.                     return { x: pos[0], y: pos[1], z: pos[2], rot: rot };
  227.                 }
  228.                 return { x: cubie.x, y: cubie.y, z: cubie.z, rot: cubie.rot };
  229.             }
  230.  
  231.             // ====== SHADING ======
  232.             function computeShade(normal) {
  233.                 let len = Math.sqrt(normal[0] ** 2 + normal[1] ** 2 + normal[2] ** 2);
  234.                 if (len === 0) return 1;
  235.                 let n = [normal[0] / len, normal[1] / len, normal[2] / len];
  236.                 let light = [0.3, 0.7, 0.5];
  237.                 let llen = Math.sqrt(light[0] ** 2 + light[1] ** 2 + light[2] ** 2);
  238.                 light = [light[0] / llen, light[1] / llen, light[2] / llen];
  239.                 let dot = n[0] * light[0] + n[1] * light[1] + n[2] * light[2];
  240.                 return 0.35 + 0.65 * Math.max(0, dot);
  241.             }
  242.  
  243.             // ====== PROJECTION ======
  244.             function projectVertex(v, camDist, fov) {
  245.                 let depth = camDist - v[2];
  246.                 return {
  247.                     x: W / 2 + v[0] * fov / depth,
  248.                     y: H / 2 - v[1] * fov / depth,
  249.                     depth: depth
  250.                 };
  251.             }
  252.  
  253.             // ====== DRAW FACE ======
  254.             function drawFace(face) {
  255.                 let r1 = parseInt(face.color.slice(1, 3), 16);
  256.                 let g1 = parseInt(face.color.slice(3, 5), 16);
  257.                 let b1 = parseInt(face.color.slice(5, 7), 16);
  258.                 let shade = face.shade;
  259.                 let r = Math.min(255, Math.round(r1 * shade));
  260.                 let g = Math.min(255, Math.round(g1 * shade));
  261.                 let b = Math.min(255, Math.round(b1 * shade));
  262.                 ctx.fillStyle = `rgb(${r},${g},${b})`;
  263.                 ctx.beginPath();
  264.                 ctx.moveTo(face.verts[0].x, face.verts[0].y);
  265.                 for (let i = 1; i < 4; i++) {
  266.                    ctx.lineTo(face.verts[i].x, face.verts[i].y);
  267.                }
  268.                ctx.closePath();
  269.                ctx.fill();
  270.                ctx.strokeStyle = 'rgba(0,0,0,0.35)';
  271.                ctx.lineWidth = 1;
  272.                ctx.stroke();
  273.            }
  274.  
  275.            // ====== RENDER ======
  276.            function render(time) {
  277.                ctx.clearRect(0, 0, W, H);
  278.  
  279.                // Global slow rotation
  280.                let gY = rotY(time * 0.45);
  281.                let gX = rotX(Math.sin(time * 0.28) * 0.25);
  282.                let globalRot = matMul(gY, gX);
  283.  
  284.                let camDist = 6.5;
  285.                let fov = 520;
  286.  
  287.                let faces = [];
  288.  
  289.                for (let cubie of cubies) {
  290.                    let state = getCubieState(cubie);
  291.                    let pos = [state.x, state.y, state.z];
  292.                    let rot = state.rot;
  293.                    let colors = getFaceColors(cubie);
  294.  
  295.                    for (let f = 0; f < 6; f++) {
  296.                        // Body face
  297.                        let bodyVerts = getFaceVerts(f, 0.5);
  298.                        let bodyWorldVerts = [];
  299.                        let bodyNormal = matApply(rot, FACE_NORMALS[f]);
  300.                        bodyNormal = matApply(globalRot, bodyNormal);
  301.                        let bodyAvgZ = 0;
  302.  
  303.                        for (let v of bodyVerts) {
  304.                            let p = matApply(rot, v);
  305.                            p = [p[0] + pos[0], p[1] + pos[1], p[2] + pos[2]];
  306.                            p = matApply(globalRot, p);
  307.                            bodyWorldVerts.push(p);
  308.                            bodyAvgZ += p[2];
  309.                        }
  310.                        bodyAvgZ /= 4;
  311.                        let bodyDepth = camDist - bodyAvgZ;
  312.  
  313.                        let bodyScreenVerts = bodyWorldVerts.map(v => projectVertex(v, camDist, fov));
  314.  
  315.                         faces.push({
  316.                             verts: bodyScreenVerts,
  317.                             color: C.black,
  318.                             depth: bodyDepth,
  319.                             shade: 1,
  320.                             isSticker: false
  321.                         });
  322.  
  323.                         // Sticker
  324.                         if (colors[f]) {
  325.                             let stickerVerts = getFaceVerts(f, 0.38);
  326.                             let stickerWorldVerts = [];
  327.                             let stickerAvgZ = 0;
  328.                             for (let v of stickerVerts) {
  329.                                 let p = matApply(rot, v);
  330.                                 p = [p[0] + pos[0], p[1] + pos[1], p[2] + pos[2]];
  331.                                 p = matApply(globalRot, p);
  332.                                 stickerWorldVerts.push(p);
  333.                                 stickerAvgZ += p[2];
  334.                             }
  335.                             stickerAvgZ /= 4;
  336.                             let stickerDepth = camDist - stickerAvgZ - 0.02; // slightly closer
  337.  
  338.                             let stickerScreenVerts = stickerWorldVerts.map(v => projectVertex(v, camDist, fov));
  339.  
  340.                             let shade = computeShade(bodyNormal);
  341.  
  342.                             faces.push({
  343.                                 verts: stickerScreenVerts,
  344.                                 color: colors[f],
  345.                                 depth: stickerDepth,
  346.                                 shade: shade,
  347.                                 isSticker: true
  348.                             });
  349.                         }
  350.                     }
  351.                 }
  352.  
  353.                 // Sort: far to near
  354.                 faces.sort((a, b) => {
  355.                     let diff = b.depth - a.depth;
  356.                     if (Math.abs(diff) < 0.005) {
  357.                        return a.isSticker ? 1 : -1;
  358.                    }
  359.                    return diff;
  360.                });
  361.  
  362.                for (let face of faces) {
  363.                    drawFace(face);
  364.                }
  365.            }
  366.  
  367.            // ====== ANIMATION LOOP ======
  368.            function animate(msTime) {
  369.                let t = msTime / 1000;
  370.                updateState(t);
  371.                render(t);
  372.                requestAnimationFrame(animate);
  373.            }
  374.  
  375.            requestAnimationFrame(animate);
  376.        })();
  377.    </script>
  378. </body>
  379. </html>
  380.  
Advertisement
Add Comment
Please, Sign In to add comment