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:
- Reset the canvas each time we redraw the mask
- Redraw the full opaque mask covering the background
- 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
initMaskfunction: Extracts the mask initialization into a reusable function to avoid code duplication and ensure we start with a fresh mask every time.redrawfunction: Now callsinitMaskto reset the canvas, then usesdestination-outto cut only the current mouse position circle from the mask. This ensures no previous trails are left behind.- Simplified
endMove: Just callsinitMaskto 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
相关产品推荐
相关产品推荐

