HTML canvas spotlight effect

前端 未结 3 1063
遇见更好的自我
遇见更好的自我 2020-12-03 23:56

Let\'s say I have the following code.

3条回答
  •  轻奢々
    轻奢々 (楼主)
    2020-12-04 00:02

    You can achieve the spotlight effect by positioning a canvas directly over the image. Make the canvas the same size as the image and set the compositing operation to xor so that two black pixels drawn in the same place cancel each other out.

    context.globalCompositeOperation = 'xor';
    

    Now you can paint the canvas black and fill a black circle around the mouse cursor. The result is a hole in the black surface, showing the image underneath.

    // Paint the canvas black.
    context.fillStyle = '#000';
    context.clearRect(0, 0, width, height);
    context.fillRect(0, 0, width, height);
    // Paint a black circle around x, y.
    context.beginPath();
    context.arc(x, y, spotlightRadius, 0, 2 * Math.PI);
    context.fillStyle = '#000';
    context.fill();
    // With xor compositing, the result is a circular hole.
    

    To make a spotlight with blurry edges, define a radial gradient centered on the mouse position and fill a square around it.

    var gradient = context.createRadialGradient(x, y, 0, x, y, spotlightRadius);
    gradient.addColorStop(0, 'rgba(0, 0, 0, 1)');
    gradient.addColorStop(0.9, 'rgba(0, 0, 0, 1)');
    gradient.addColorStop(1, 'rgba(0, 0, 0, 0)');
    context.fillStyle = gradient;
    context.fillRect(x - spotlightRadius, y - spotlightRadius,
                     2 * spotlightRadius, 2 * spotlightRadius);
    

    The following snippet demonstrates both approaches using pure JavaScript. To change from a crisp-edged spotlight to a blurry-edged spotlight, click on the checkbox above the image.

    function getOffset(element, ancestor) {
      var left = 0,
          top = 0;
      while (element != ancestor) {
        left += element.offsetLeft;
        top += element.offsetTop;
        element = element.parentNode;
      }
      return { left: left, top: top };
    }
    
    function getMousePosition(event) {
      event = event || window.event;
      if (event.pageX !== undefined) {
        return { x: event.pageX, y: event.pageY };
      }
      return {
        x: event.clientX + document.body.scrollLeft +
            document.documentElement.scrollLeft,
        y: event.clientY + document.body.scrollTop +
            document.documentElement.scrollTop
      };
    }
    
    window.onload = function () {
      var spotlightRadius = 60,
          container = document.getElementById('container'),
          canvas = document.createElement('canvas'),
          image = container.getElementsByTagName('img')[0],
          width = canvas.width = image.width,
          height = canvas.height = image.height,
          context = canvas.getContext('2d');
      context.globalCompositeOperation = 'xor';
      container.insertBefore(canvas, image.nextSibling);
      container.style.width = width + 'px';
      container.style.height = height + 'px';
      var offset = getOffset(canvas, document.body);
          clear = function () {
            context.fillStyle = '#000';
            context.clearRect(0, 0, width, height);
            context.fillRect(0, 0, width, height);
          };
      clear();
      image.style.visibility = 'visible';
      canvas.onmouseout = clear;
      canvas.onmouseover = canvas.onmousemove = function (event) {
        var mouse = getMousePosition(event),
            x = mouse.x - offset.left,
            y = mouse.y - offset.top;
        clear();
        if (document.getElementById('blurry').checked) {
          var gradient = context.createRadialGradient(x, y, 0, x, y, spotlightRadius);
          gradient.addColorStop(0, 'rgba(0, 0, 0, 1)');
          gradient.addColorStop(0.875, 'rgba(0, 0, 0, 1)');
          gradient.addColorStop(1, 'rgba(0, 0, 0, 0)');
          context.fillStyle = gradient;
          context.fillRect(x - spotlightRadius, y - spotlightRadius,
                           2 * spotlightRadius, 2 * spotlightRadius);
        } else {
          context.beginPath();
          context.arc(x, y, spotlightRadius, 0, 2 * Math.PI);
          context.fillStyle = '#000';
          context.fill();
        }
      };
    };
    * {
      margin: 0;
      padding: 0;
    }
    .control {
      font-family: sans-serif;
      font-size: 15px;
      padding: 10px;
    }
    #container {
      position: relative;
    }
    #container img, #container canvas {
      position: absolute;
      left: 0;
      top: 0;
    }
    #container img {
      visibility: hidden;
    }
    #container canvas {
      cursor: none;
    }

    blurry edges

提交回复
热议问题