Deno Logo (animated)

Mr. Denosaur with animated raindrops on the backdrop.

by Nicholas Berlette

HTML

<canvas class="raindrops" fill="black" width="100%" height="100%" style="z-index:500;"></canvas>
<svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 512 512" width="100%" height="100%" color="#000e">
  <title>Deno Land</title>
  <defs>
    <filter id="light1" x="0" y="0" width="100%" height="100%">
      <feDiffuseLighting in="SourceGraphic" result="light" lighting-color="dodgerblue">
        <feSpotLight x="0" y="0" z="14" limitingConeAngle="25" pointsAtX="300" pointsAtY="420" pointsAtZ="0"/>
      </feDiffuseLighting>
      <feComposite in="SourceGraphic" in2="light" operator="arithmetic" k1="1" k2="0" k3="1" k4="0"/>
    </filter>
    <mask id="deno-logo-mask">
      <circle fill="white" cx="256" cy="256" r="230" />
    </mask>
  </defs>
  <circle fill="currentColor" cx="256" cy="256" r="256"  style="z-index:-100" />
  <circle fill="currentColor" cx="256" cy="256" r="256"  style="z-index:-100" filter="url(#light1)" mask="url(#deno-logo-mask)" />
  <g style="z-index:100">
    <path mask="url(#deno-logo-mask)" stroke="none" stroke-width="20" stroke-linecap="round" class="_raindrops" d="M71 319l17-63M107.964 161.095l17-63M36.93 221l17-63M125.964 385l17-63M160.372 486.829l17-63M230 456.329l17-63M206.257 92.587l17-63M326.395 173.004l17-63M452.182 304.693l17-63M409.124 221l17-63M299.027 54.558l17-63M400.624 86.058l17-63" />
    <path mask="url(#deno-logo-mask)" fill="white" stroke="black" stroke-width="12" d="M252.225 344.418c-86.65 2.61-144.576-34.5-144.576-94.363 0-61.494 60.33-111.145 138.351-111.145 37.683 0 69.532 10.65 94.392 30.092 21.882 17.113 37.521 40.526 45.519 66.312 2.574 8.301 22.863 83.767 61.112 227.295l1.295 4.86-159.793 74.443-1.101-8.063c-8.85-64.778-16.546-113.338-23.076-145.634-3.237-16.004-6.178-27.96-8.79-35.794-1.227-3.682-2.355-6.361-3.303-7.952a12.56 12.56 0 00-.03-.05z" />
    <circle mask="url(#deno-logo-mask)" cx="262" cy="203" r="16" style="z-index:1000;" transform-origin="50% 40%" transform="scale(1, 1);">
      <animateTransform...

CSS

html,
body {
  overflow: hidden;
}

 svg, canvas {
  min-width: 256px;
  min-height: 256px;
  max-width:512px;
  max-height:512px;
}

.raindrops {
  margin-top: 0rem;
  position: absolute;
  top: 0;
  left: 0;
  width: calc(100% - 32px);
  height: auto;
  background-color: transparent;
  clip-path: ellipse(40% 40% at 50% 50%);
  z-index: 1;
  transform-origin: 50% 50%;
  transform: rotate(15deg);
  border: 16px solid black;
}

.eye {
  fill: #000;
  -webkit-transform-origin: 50% 40%;
  -webkit-animation: blink 4s 1s infinite;
  animation: blink 4s 1s infinite z-index: 200;
}

@-webkit-keyframes blink {

  0%,
  6%,
  100% {
    transform: scaleY(.2)
  }

  3%,
  12%,
  98% {
    transform: scale(1)
  }
}

@keyframes blink {

  0%,
  6%,
  100% {
    transform: scaleY(.2)
  }

  3%,
  12%,
  98% {
    transform: scale(1)
  }
}

JavaScript

(() => {
    // deno:file:///home/runner/work/dotcom/dotcom/landingpage/main.js
    var observer = new IntersectionObserver((entries) => {
      for (const entry of entries) {
        if (entry.isIntersecting) {
          entry.target.className += " inview";
        }
      }
    });
    var observed = document.getElementsByClassName("observed");
    for (const target of observed) {
      observer.observe(target);
    }

    var canvas = document.querySelector("canvas");
    var ctx = canvas.getContext("2d");

    function resize(canvas2) {
      const displayWidth = canvas2.clientWidth;
      const displayHeight = canvas2.clientHeight;
      if (canvas2.width != displayWidth) {
        canvas2.width = displayWidth;
      }
      if (canvas2.height != displayHeight) {
        canvas2.height = displayHeight;
      }
    }
    var drops = [];
    for (let i = 0; i < 100; i++) {
      drops.push({
        t: Math.random(),
        deg: Math.random() * Math.PI
      });
    }
    var particles = [];
    var CY = -300;
    var RAIN_LENGTH = 0.05;
    var circleR = 300;
    var t = 0;
    var forward = true;

    function redraw(_timestamp, diff) {
      resize(canvas);
      circleR = canvas.height * 4;
      CY = -circleR * 3 / 4;
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      ctx.strokeStyle = "white";
      ctx.lineWidth = 1.25;
      ctx.save();
      ctx.translate(canvas.width / 2, CY);
      if (forward) {
        t += diff / 2e3;
      } else {
        t -= diff / 2e3;
      }
      t = 1;
      ctx.beginPath();
      for (const drop of drops) {
        const x1 = Math.cos(drop.deg) * drop.t * circleR;
        const x2 = Math.cos(drop.deg) * (drop.t + RAIN_LENGTH) * circleR;
        const y1 = Math.sin(drop.deg) * drop.t * circleR;
        const y2 = Math.sin(drop.deg) * (drop.t + RAIN_LENGTH) * circleR;
        ctx.moveTo(x1, y1);
        ctx.lineTo(x2, y2);
        drop.t += diff / 800;
        if (drop.t > 1) {
          if (Math.random() <...