>_htmlhub
library/interactive/click-response

Click Response

Live previewPerformance varies by device
click-response.html

No account needed to copy or download. Check dependencies and thelicense status before publishing.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>Click Response</title>
  <!-- Anime.js library for animations -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/1.0.0/anime.min.js"></script>
  <style>
    canvas {
      display: block;
      width: 100vw;
      height: 100vh;
    }
  </style>
</head>
<body>
  <canvas id="c"></canvas>
  <script>
    // Recreate the click response animation from CodePen
    var c = document.getElementById("c");
    var ctx = c.getContext("2d");
    var cH;
    var cW;
    var bgColor = "#FF6138";
    var animations = [];
    // Color cycling helper
    var colorPicker = (function() {
      var colors = ["#FF6138", "#FFBE53", "#2980B9", "#282741"];
      var index = 0;
      function next() {
        index = index++ < colors.length - 1 ? index : 0;
        return colors[index];
      }
      function current() {
        return colors[index];
      }
      return { next: next, current: current };
    })();
    function removeAnimation(animation) {
      var index = animations.indexOf(animation);
      if (index > -1) animations.splice(index, 1);
    }
    function calcPageFillRadius(x, y) {
      var l = Math.max(x - 0, cW - x);
      var h = Math.max(y - 0, cH - y);
      return Math.sqrt(Math.pow(l, 2) + Math.pow(h, 2));
    }
    function addClickListeners() {
      document.addEventListener("touchstart", handleEvent);
      document.addEventListener("mousedown", handleEvent);
    }
    function handleEvent(e) {
      if (e.touches) {
        e.preventDefault();
        e = e.touches[0];
      }
      var currentColor = colorPicker.current();
      var nextColor = colorPicker.next();
      var targetR = calcPageFillRadius(e.pageX, e.pageY);
      var rippleSize = Math.min(200, cW * 0.4);
      var minCoverDuration = 750;
      var pageFill = new Circle({ x: e.pageX, y: e.pageY, r: 0, fill: nextColor });
      var fillAnimation = anime({
        targets: pageFill,
        r: targetR,
        duration: Math.max(targetR / 2, minCoverDuration),
        easing: "easeOutQuart",
        complete: function() {
          bgColor = pageFill.fill;
          removeAnimation(fillAnimation);
        },
      });
      var ripple = new Circle({
        x: e.pageX,
        y: e.pageY,
        r: 0,
        fill: currentColor,
        stroke: { width: 3, color: currentColor },
        opacity: 1,
      });
      var rippleAnimation = anime({
        targets: ripple,
        r: rippleSize,
        opacity: 0,
        easing: "easeOutExpo",
        duration: 900,
        complete: removeAnimation,
      });
      var particles = [];
      for (var i = 0; i < 32; i++) {
        var particle = new Circle({
          x: e.pageX,
          y: e.pageY,
          fill: currentColor,
          r: anime.random(24, 48),
        });
        particles.push(particle);
      }
      var particlesAnimation = anime({
        targets: particles,
        x: function(particle) {
          return particle.x + anime.random(rippleSize, -rippleSize);
        },
        y: function(particle) {
          return particle.y + anime.random(rippleSize * 1.15, -rippleSize * 1.15);
        },
        r: 0,
        easing: "easeOutExpo",
        duration: anime.random(1000, 1300),
        complete: removeAnimation,
      });
      animations.push(fillAnimation, rippleAnimation, particlesAnimation);
    }
    function extend(a, b) {
      for (var key in b) {
        if (b.hasOwnProperty(key)) {
          a[key] = b[key];
        }
      }
      return a;
    }
    var Circle = function(opts) {
      extend(this, opts);
    };
    Circle.prototype.draw = function() {
      ctx.globalAlpha = this.opacity || 1;
      ctx.beginPath();
      ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI, false);
      if (this.stroke) {
        ctx.strokeStyle = this.stroke.color;
        ctx.lineWidth = this.stroke.width;
        ctx.stroke();
      }
      if (this.fill) {
        ctx.fillStyle = this.fill;
        ctx.fill();
      }
      ctx.closePath();
      ctx.globalAlpha = 1;
    };
    var animationLoop = anime({
      duration: Infinity,
      update: function() {
        ctx.fillStyle = bgColor;
        ctx.fillRect(0, 0, cW, cH);
        animations.forEach(function(anim) {
          anim.animatables.forEach(function(animatable) {
            animatable.target.draw();
          });
        });
      },
    });
    var resizeCanvas = function() {
      cW = window.innerWidth;
      cH = window.innerHeight;
      var pixelRatio = window.devicePixelRatio || 1;
      c.width = cW * pixelRatio;
      c.height = cH * pixelRatio;
      ctx.scale(pixelRatio, pixelRatio);
    };
    (function init() {
      resizeCanvas();
      window.addEventListener("resize", resizeCanvas);
      addClickListeners();
      handleInactiveUser();
    })();
    function handleInactiveUser() {
      var inactive = setTimeout(function() {
        fauxClick(cW / 2, cH / 2);
      }, 2000);
      function clearInactiveTimeout() {
        clearTimeout(inactive);
        document.removeEventListener("mousedown", clearInactiveTimeout);
        document.removeEventListener("touchstart", clearInactiveTimeout);
      }
      document.addEventListener("mousedown", clearInactiveTimeout);
      document.addEventListener("touchstart", clearInactiveTimeout);
    }
    function fauxClick(x, y) {
      var faux = new Event("mousedown");
      faux.pageX = x;
      faux.pageY = y;
      document.dispatchEvent(faux);
    }
  </script>
</body>
</html>

About this animation

Ripple effect on mouse click.

Source Code
Copied to clipboard