用代码画蒙娜丽莎

Published on
/
/趣玩前端
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>代码生成蒙娜丽莎</title>
    <style>
      html,
      body {
        margin: 0;
        padding: 0;
        overflow: hidden;
      }
    </style>
  </head>
  <body>
    <canvas id="c" />
    <script>
      const c = document.querySelector('#c');
      const ctx = c.getContext('2d');

      const ratio = window.innerHeight / window.innerWidth;
      const width = window.innerWidth;
      const height = window.innerHeight;

      c.height = Math.max(900, window.innerHeight);
      c.width = c.height / ratio;

      c.style.width = '100vw';
      c.style.height = '100vh';

      function pseudoRandom(seed) {
        return Math.sin(seed) * 10000 - Math.floor(Math.sin(seed) * 10000);
      }

      function noise(x, y) {
        const seed = x * 137 + y * 149;
        return pseudoRandom(seed) % 1;
      }

      const stepAngle = (Math.PI * 2) / 3;

      const getThread = (i) => {
        return {
          lastPoint: {
            x: c.width / 2 + Math.cos((Math.PI / 10) * i) * i * 3,
            y: c.height / 2 + Math.sin((Math.PI / 10) * i) * i * 3,
          },
          angle: Math.PI / 3 + stepAngle * i,
        };
      };

      const threads = [];

      for (let i = 0; i < 256; i++) {
        threads.push(getThread(i));
      }

      const palette = [
        // '#001219',
        '#80FFDB',
        '#72EFDD',
        '#64DFDF',
        '#56CFE1',
        '#48BFE3',
        '#4EA8DE',
        '#5390D9',
        '#5E60CE',
        '#6930C3',
        '#7400B8',
      ];

      // https://www.asciiart.eu/art-and-design/mona-lisa
      const monaLisa = `
                                  _______
                           _,,ad8888888888bba,_
                        ,ad88888I888888888888888ba,
                      ,88888888I88888888888888888888a,
                    ,d888888888I8888888888888888888888b,
                   d88888PP"""" ""YY88888888888888888888b,
                 ,d88"'__,,--------,,,,.;ZZZY8888888888888,
                ,8IIl'"                ;;l"ZZZIII8888888888,
               ,I88l;'                  ;lZZZZZ888III8888888,
             ,II88Zl;.                  ;llZZZZZ888888I888888,
            ,II888Zl;.                .;;;;;lllZZZ888888I8888b
           ,II8888Z;;                 ';;;;;''llZZ8888888I8888,
           II88888Z;'                        .;lZZZ8888888I888b
           II88888Z; _,aaa,      .,aaaaa,__.l;llZZZ88888888I888
           II88888IZZZZZZZZZ,  .ZZZZZZZZZZZZZZ;llZZ88888888I888,
           II88888IZZ<'(@@>Z|  |ZZZ<'(@@>ZZZZ;;llZZ888888888I88I
          ,II88888;   '""" ;|  |ZZ; '"""     ;;llZ8888888888I888
          II888888l            ';;          .;llZZ8888888888I888,
         ,II888888Z;           ;;;        .;;llZZZ8888888888I888I
         III888888Zl;    ..,   ';;       ,;;lllZZZ88888888888I888
         II88888888Z;;...;(_    _)      ,;;;llZZZZ88888888888I888,
         II88888888Zl;;;;;' '--'Z;.   .,;;;;llZZZZ88888888888I888b
         ]I888888888Z;;;;'   ";llllll;..;;;lllZZZZ88888888888I8888,
         II888888888Zl.;;"Y88bd888P";;,..;lllZZZZZ88888888888I8888I
         II8888888888Zl;.; '"PPP";;;,..;lllZZZZZZZ88888888888I88888
         II888888888888Zl;;. ';;;l;;;;lllZZZZZZZZW88888888888I88888
         'II8888888888888Zl;.    ,;;lllZZZZZZZZWMZ88888888888I88888
          II8888888888888888ZbaalllZZZZZZZZZWWMZZZ8888888888I888888,
          'II88888888888888888b"WWZZZZZWWWMMZZZZZZI888888888I888888b
           'II88888888888888888;ZZMMMMMMZZZZZZZZllI888888888I8888888
            'II8888888888888888 ';lZZZZZZZZZZZlllll888888888I8888888,
             II8888888888888888, ';lllZZZZllllll;;.Y88888888I8888888b,
            ,II8888888888888888b   .;;lllllll;;;.;..88888888I88888888b,
            II888888888888888PZI;.  .';;;.;;;..; ...88888888I8888888888,
            II888888888888PZ;;';;.   ;. .;.  .;. .. Y8888888I88888888888b,
           ,II888888888PZ;;'                        '8888888I8888888888888b,
           II888888888'                              888888I8888888888888888b
          ,II888888888                              ,888888I88888888888888888
         ,d88888888888                              d888888I8888888888ZZZZZZZ
      ,ad888888888888I                              8888888I8888ZZZZZZZZZZZZZ
    ,d888888888888888'                              888888IZZZZZZZZZZZZZZZZZZ
  ,d888888888888P'8P'                               Y888ZZZZZZZZZZZZZZZZZZZZZ
 ,8888888888888,  "                                 ,ZZZZZZZZZZZZZZZZZZZZZZZZ
d888888888888888,                                ,ZZZZZZZZZZZZZZZZZZZZZZZZZZZ
888888888888888888a,      _                    ,ZZZZZZZZZZZZZZZZZZZZ888888888
888888888888888888888ba,_d'                  ,ZZZZZZZZZZZZZZZZZ88888888888888
8888888888888888888888888888bbbaaa,,,______,ZZZZZZZZZZZZZZZ888888888888888888
88888888888888888888888888888888888888888ZZZZZZZZZZZZZZZ888888888888888888888
8888888888888888888888888888888888888888ZZZZZZZZZZZZZZ88888888888888888888888
888888888888888888888888888888888888888ZZZZZZZZZZZZZZ888888888888888888888888
8888888888888888888888888888888888888ZZZZZZZZZZZZZZ88888888888888888888888888
88888888888888888888888888888888888ZZZZZZZZZZZZZZ8888888888888888888888888888
8888888888888888888888888888888888ZZZZZZZZZZZZZZ88888888888888888888888888888
88888888888888888888888888888888ZZZZZZZZZZZZZZ8888888888888888888888888888888
8888888888888888888888888888888ZZZZZZZZZZZZZZ88888888888888888888888888888888
`
        .split('\n')
        .slice(1);

      const symbols = ` .-'":_,^=;><+!rc*/z?sLTv)J7(|Fi{C}fI31tlu[neoZ5Yxjya]2ESwqkP6h9d4VpPOGbUAKXHm8RD#$Bg0MNWQ%&@`;

      ctx.fillStyle = 'rgba(0, 0, 0, 1)';
      ctx.fillRect(0, 0, c.width, c.height);

      const animate = (time) => {
        requestAnimationFrame(animate);

        ctx.fillStyle = 'black';
        ctx.fillRect(0, 0, c.width, c.height);

        ctx.lineWidth = 1;

        // exit
        let prevColor = '#000000';
        for (let y = 1; y < monaLisa.length; y++) {
          for (let x = 0; x < 76; x++) {
            const padding = (c.width - 760) / 2;
            const letter = monaLisa[y][x] ?? ' ';
            const level = 1.256 - symbols.indexOf(letter) / symbols.length;
            const size = 20;

            ctx.beginPath();

            const colors = [65, 85, 105, 125, 145, 165];

            const e =
              Math.cos((x + time / 10000) * 10) +
              Math.sin((y + time / 10000) * 10);
            const deg = 220 + colors[Math.floor(e * colors.length)];

            ctx.arc(padding + x * 10, y * 20, level * 5, 0, Math.PI * 2);

            ctx.fillStyle = `hsl(${deg}deg, 60%, ${20 + level * 40}%)`;
            ctx.fill();

            prevColor = ctx.fillStyle;
          }
        }
      };

      animate(0);
    </script>
  </body>
</html>