简体   繁体   中英

How to create fading cursor trails on an image canvas?

I've created a cursor trail over an image canvas where the image canvas is at the background and the cursor trail is on a second canvas on top of the image canvas. The issue I'm currently facing is that I am unable to create a trail that fades away gradually.

I've seen tips on how to make it fade with a plain background using fillStyle but I don't know how to make a fading cursor trail work with an image as the background.

<!DOCTYPE html>
<html>
  <head>
    <style>
      .stack {
        position: relative;
      }

      .stack canvas {
        position: absolute;
        left: 0;
        top: 0;
      }

      .stack,
      #main_canvas {
        background-size: contain;
        width: 100%;
        margin: auto;
      }
    </style>
  </head>

  <body>
    <div class="stack">
      <canvas id="main_canvas"> main canvas</canvas>
    </div>

    <script>
      var SCREEN_WIDTH = window.innerWidth;
      var SCREEN_HEIGHT = window.innerHeight;

      var RADIUS = 70;

      var RADIUS_SCALE = 1;
      var RADIUS_SCALE_MIN = 1;
      var RADIUS_SCALE_MAX = 1.5;

      var QUANTITY = 25;

      var canvas;
      var canvas_bg;
      var context;
      var context_bg;
      var particles;
      var slider_image;

      var mouseX = SCREEN_WIDTH * 0.5;
      var mouseY = SCREEN_HEIGHT * 0.5;
      var mouseIsDown = false;

      var ind = 0;

      function init() {
        canvas = document.getElementById("main_canvas");
        canvas_bg = document.createElement("canvas"); //<canvas> predefined
        canvas.setAttribute("alt", "countless stars");

        if (canvas && canvas.getContext) {
          windowResizeHandler();

          //background canvas
          create_sliders();

          //main canvas for creating  mouse trails
          context = canvas.getContext("2d");

          // Register event listeners
          window.addEventListener("mousemove", documentMouseMoveHandler, false);
          window.addEventListener("mousedown", documentMouseDownHandler, false);
          window.addEventListener("mouseup", documentMouseUpHandler, false);
          document.addEventListener(
            "touchstart",
            documentTouchStartHandler,
            false
          );
          document.addEventListener(
            "touchmove",
            documentTouchMoveHandler,
            false
          );
          window.addEventListener("resize", windowResizeHandler, false);

          createParticles();
          setInterval(loop, 1000 / 60);
        }
      }

      function create_sliders() {
        slider_image = new Image();
        slider_image.src =
          "https://images.pexels.com/photos/956999/milky-way-starry-sky-night-sky-star-956999.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940";

        canvas_bg.width = canvas.width;
        canvas_bg.height = canvas.height;
        // insert into DOM on top:
        canvas.parentNode.insertBefore(canvas_bg, canvas);

        context_bg = canvas_bg.getContext("2d");
        context_bg.drawImage(slider_image, 0, 0, canvas.width, canvas.height);
      }

      function createParticles() {
        particles = [];

        for (var i = 0; i < QUANTITY; i++) {
          var particle = {
            size: 1,
            position: { x: mouseX, y: mouseY },
            offset: { x: 0, y: 0 },
            shift: { x: mouseX, y: mouseY },
            speed: 0.01 + Math.random() * 0.04,
            targetSize: 1,
            fillColor:
              "#" + ((Math.random() * 0x404040 + 0xaaaaaa) | 0).toString(16),
            orbit: RADIUS * 0.5 + RADIUS * 0.5 * Math.random()
          };
          particles.push(particle);
        }
      }

      function documentMouseMoveHandler(event) {
        mouseX = event.clientX - (window.innerWidth - SCREEN_WIDTH) * 0.5;
        mouseY = event.clientY - (window.innerHeight - SCREEN_HEIGHT) * 0.5;
      }

      function documentMouseDownHandler(event) {
        mouseIsDown = true;
      }

      function documentMouseUpHandler(event) {
        mouseIsDown = false;
      }

      function documentTouchStartHandler(event) {
        if (event.touches.length == 1) {
          event.preventDefault();

          mouseX =
            event.touches[0].pageX - (window.innerWidth - SCREEN_WIDTH) * 0.5;
          mouseY =
            event.touches[0].pageY - (window.innerHeight - SCREEN_HEIGHT) * 0.5;
        }
      }

      function documentTouchMoveHandler(event) {
        if (event.touches.length == 1) {
          event.preventDefault();

          mouseX =
            event.touches[0].pageX - (window.innerWidth - SCREEN_WIDTH) * 0.5;
          mouseY =
            event.touches[0].pageY - (window.innerHeight - SCREEN_HEIGHT) * 0.5;
        }
      }

      function windowResizeHandler() {
        SCREEN_WIDTH = window.innerWidth;
        SCREEN_HEIGHT = window.innerHeight;

        canvas.width = SCREEN_WIDTH;
        canvas.height = SCREEN_HEIGHT;
      }

      function loop() {
        if (mouseIsDown) {
          RADIUS_SCALE += (RADIUS_SCALE_MAX - RADIUS_SCALE) * 0.02;
        } else {
          RADIUS_SCALE -= (RADIUS_SCALE - RADIUS_SCALE_MIN) * 0.02;
        }

        RADIUS_SCALE = Math.min(RADIUS_SCALE, RADIUS_SCALE_MAX);

        // context.fillStyle = 'rgba(0,0,0,0.05)';
        context.fillStyle = "rgba(0, 0, 0, 0)";
        context.fillRect(0, 0, context.canvas.width, context.canvas.height);

        for (i = 0, len = particles.length; i < len; i++) {
          var particle = particles[i];

          var lp = { x: particle.position.x, y: particle.position.y };

          // Rotation
          particle.offset.x += particle.speed;
          particle.offset.y += particle.speed;

          // Follow mouse with some lag
          particle.shift.x += (mouseX - particle.shift.x) * particle.speed;
          particle.shift.y += (mouseY - particle.shift.y) * particle.speed;

          // Apply position
          particle.position.x =
            particle.shift.x +
            Math.cos(i + particle.offset.x) * (particle.orbit * RADIUS_SCALE);
          particle.position.y =
            particle.shift.y +
            Math.sin(i + particle.offset.y) * (particle.orbit * RADIUS_SCALE);

          // Limit to screen bounds
          particle.position.x = Math.max(
            Math.min(particle.position.x, SCREEN_WIDTH),
            0
          );
          particle.position.y = Math.max(
            Math.min(particle.position.y, SCREEN_HEIGHT),
            0
          );

          particle.size += (particle.targetSize - particle.size) * 0.05;

          if (Math.round(particle.size) == Math.round(particle.targetSize)) {
            particle.targetSize = 1 + Math.random() * 7;
          }

          if (particle.position) context.beginPath();
          context.fillStyle = particle.fillColor;
          context.strokeStyle = particle.fillColor;
          context.lineWidth = particle.size;
          context.moveTo(lp.x, lp.y);
          context.lineTo(particle.position.x, particle.position.y);
          context.stroke();
          context.arc(
            particle.position.x,
            particle.position.y,
            particle.size / 2,
            0,
            Math.PI * 2,
            true
          );
          context.fill();
        }
      }
      window.onload = init;
    </script>
  </body>
</html>

Currently, the older trails will not fade away and it is creating something more of like a paint effect which I don't want.

One way to fade out your particles is to replace your (currently transparent) drawRect call with a drawImage that draws semi-transparent copy of your background image over each frame before adding particles to the current frame:

  In your loop() function:

  // Instead of this:
  // context.fillStyle = "rgba(0, 0, 0, 0)";
  // context.fillRect(0, 0, context.canvas.width, context.canvas.height);

  // Do this:
  context.save();
  context.globalAlpha = 0.1;
  context.drawImage(slider_image, 0, 0, canvas.width, canvas.height);
  context.restore();

  for (i = 0, len = particles.length; i < len; i++) {
    ...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM