You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Canvas鼠标交互遮罩问题:修复拖动时轨迹残留,仅显示鼠标当前位置圆形可见区域

Fix: Remove Mouse Trail Residue from Canvas Mask

Let's tackle that mouse trail issue you're facing. The core problem with your current code is that you're modifying the existing canvas content instead of redrawing the entire mask from scratch every time the mouse moves. Here's how to fix it:

Key Issue Analysis

Your original redraw function uses destination-in and tries to draw two arcs with evenodd filling, but this builds on top of previous canvas state. Instead, we need to:

  1. Reset the canvas each time we redraw the mask
  2. Redraw the full opaque mask covering the background
  3. Cut out only the current mouse position circle to reveal the background beneath

Modified Solution Code

let canvas = document.getElementById('canvas');
const CANVAS_SIZE = 200;
const MASK_RADIUS = CANVAS_SIZE / 2;
const REVEAL_RADIUS = CANVAS_SIZE / 4;

// Initialize the full mask on load
function initMask() {
  canvas.width = CANVAS_SIZE;
  canvas.height = CANVAS_SIZE;
  const ctx = canvas.getContext('2d');
  ctx.fillStyle = '#000'; // Opaque black mask
  ctx.beginPath();
  ctx.arc(CANVAS_SIZE / 2, CANVAS_SIZE / 2, MASK_RADIUS, 0, 2 * Math.PI);
  ctx.fill();
}

window.onload = initMask;

function getMouse(e, canvas) {
  const rect = canvas.getBoundingClientRect();
  return {
    x: (e.clientX - rect.left) / (rect.right - rect.left) * canvas.width,
    y: (e.clientY - rect.top) / (rect.bottom - rect.top) * canvas.height
  };
}

function redraw(canvas, mouse) {
  const ctx = canvas.getContext('2d');
  // Step 1: Clear and redraw the full mask
  initMask();
  
  // Step 2: Cut out the current mouse circle to reveal background
  ctx.globalCompositeOperation = 'destination-out';
  ctx.beginPath();
  ctx.arc(mouse.x, mouse.y, REVEAL_RADIUS, 0, 2 * Math.PI);
  ctx.fill();
  
  // Reset composite operation to default for next draws
  ctx.globalCompositeOperation = 'source-over';
}

function moveOnMouseDown(canvas, moveFunction) {
  const endMove = function () {
    // Restore full mask on mouse up
    initMask();
    window.removeEventListener('mousemove', moveFunction);
    window.removeEventListener('mouseup', endMove);
  };

  canvas.addEventListener('mousedown', function (event) {
    event.stopPropagation();
    const mouse = getMouse(event, canvas);
    redraw(canvas, mouse);
    window.addEventListener('mousemove', moveFunction);
    window.addEventListener('mouseup', endMove);
  });
}

moveOnMouseDown(canvas, function (e) {
  const mouse = getMouse(e, canvas);
  redraw(canvas, mouse);
});
#TML_div {
  position: absolute;
  overflow: hidden;
  width: 200px;
  height: 200px;
  border-radius: 50%;
  background: url('your-background-image.jpg') center/cover; /* Add your background here */
}

#canvas {
  width: 100%;
  height: 100%;
}
<div id='TML_div'>
  <canvas id='canvas'></canvas>
</div>

What Changed

  • initMask function: Extracts the mask initialization into a reusable function to avoid code duplication and ensure we start with a fresh mask every time.
  • redraw function: Now calls initMask to reset the canvas, then uses destination-out to cut only the current mouse position circle from the mask. This ensures no previous trails are left behind.
  • Simplified endMove: Just calls initMask to restore the full mask instead of repeating the drawing code.

This approach guarantees that only the current mouse position's circle is revealed, with no leftover trails from previous mouse movements.

内容的提问来源于stack exchange,提问作者shaedopotato

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 12:27:38