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

Windows版Edge中Canvas元素无法删除的问题求助

问题:Windows版Edge中刮刮乐Canvas无法在刮开40%后移除

我用Canvas实现了交互式刮刮乐功能,设定刮开像素占比达到40%时移除Canvas元素。该功能在Chrome、Firefox、Safari及Mac版Edge中均正常运行,但Windows版Edge中Canvas无法被删除。因无Windows设备及Browserstack订阅,无法直接测试。预期效果为刮开至少40%后自动移除Canvas。

可能的问题原因

  • Windows Edge对getImageData的像素值处理存在差异:原代码判断像素红色通道值为0,但Edge中被刮除区域的像素值可能不是严格的0,而是接近0的数值
  • parentNode.removeChild的DOM操作在Edge中存在延迟或上下文冲突
  • 鼠标移动时频繁触发移除逻辑,导致Edge中DOM操作异常

解决方案

1. 优化像素判断逻辑

改用Alpha通道判断透明度(而非红色通道),设置阈值替代严格等于0的判断,适配Edge的渲染特性:

function getFilledInPixels(stride) {
  if (!stride || stride < 1) { stride = 1; }

  var pixels = ctx.getImageData(0, 0, canvasWidth, canvasHeight),
    pdata = pixels.data,
    l = pdata.length,
    total = (l / stride),
    count = 0;

  // 检测Alpha通道,判断像素是否接近完全透明
  for (var i = count = 0; i < l; i += stride) {
    if (pdata[i + 3] < 10) {
      count++;
    }
  }

  return Math.round((count / total) * 100);
}

2. 改用更可靠的DOM移除方法

替换parentNode.removeChild为现代浏览器支持的remove()方法,并添加标记防止重复触发移除操作:

function handlePercentage(filledInPixels) {
  filledInPixels = filledInPixels || 0;
  console.log(filledInPixels + '%');
  // 添加标记避免重复执行移除逻辑
  if (filledInPixels > 40 && !canvas.dataset.removed) {
    canvas.dataset.removed = 'true';
    canvas.remove();
    // 移除事件监听,避免内存泄漏
    canvas.removeEventListener('mousedown', handleMouseDown);
    canvas.removeEventListener('touchstart', handleMouseDown);
    canvas.removeEventListener('mousemove', handleMouseMove);
    canvas.removeEventListener('touchmove', handleMouseMove);
    canvas.removeEventListener('mouseup', handleMouseUp);
    canvas.removeEventListener('touchend', handleMouseUp);
  }
}

3. 调整Canvas绘制上下文设置

将globalCompositeOperation设置移到循环外,减少重复操作,避免Edge渲染异常:

function handleMouseMove(e) {
  if (!isDrawing) { return; }

  e.preventDefault();

  var currentPoint = getMouse(e, canvas),
    dist = distanceBetween(lastPoint, currentPoint),
    angle = angleBetween(lastPoint, currentPoint),
    x, y;

  // 提前设置混合模式,避免循环内重复执行
  ctx.globalCompositeOperation = 'destination-out';
  for (var i = 0; i < dist; i++) {
    x = lastPoint.x + (Math.sin(angle) * i) - 25;
    y = lastPoint.y + (Math.cos(angle) * i) - 25;
    ctx.drawImage(brush, x, y);
  }

  lastPoint = currentPoint;
  handlePercentage(getFilledInPixels(32));
}

4. 确保画笔图片加载完成再绑定事件

原代码未等待画笔图片加载完成就绑定事件,可能导致Edge中绘制异常:

brush.src = 'data:image/png;base64,[Encoded Image Here]';
brush.onload = function() {
  // 图片加载完成后再绑定交互事件
  canvas.addEventListener('mousedown', handleMouseDown, false);
  canvas.addEventListener('touchstart', handleMouseDown, false);
  canvas.addEventListener('mousemove', handleMouseMove, false);
  canvas.addEventListener('touchmove', handleMouseMove, false);
  canvas.addEventListener('mouseup', handleMouseUp, false);
  canvas.addEventListener('touchend', handleMouseUp, false);
};

修改后的完整JavaScript代码

(function () {

  'use strict';

  var isDrawing, lastPoint;
  var container = document.getElementById('js-container'),
    canvas = document.getElementById('js-canvas'),
    canvasWidth = canvas.width,
    canvasHeight = canvas.height,
    ctx = canvas.getContext('2d'),
    image = new Image(),
    brush = new Image();


  image.src = 'CanvasImage.png';
  image.onload = function () {
    ctx.drawImage(image, 0, 0);
  };
  brush.src = 'data:image/png;base64,[Encoded Image Here]';
  brush.onload = function() {
    canvas.addEventListener('mousedown', handleMouseDown, false);
    canvas.addEventListener('touchstart', handleMouseDown, false);
    canvas.addEventListener('mousemove', handleMouseMove, false);
    canvas.addEventListener('touchmove', handleMouseMove, false);
    canvas.addEventListener('mouseup', handleMouseUp, false);
    canvas.addEventListener('touchend', handleMouseUp, false);
  };

  function distanceBetween(point1, point2) {
    return Math.sqrt(Math.pow(point2.x - point1.x, 2) + Math.pow(point2.y - point1.y, 2));
  }

  function angleBetween(point1, point2) {
    return Math.atan2(point2.x - point1.x, point2.y - point1.y);
  }

  function getFilledInPixels(stride) {
    if (!stride || stride < 1) { stride = 1; }

    var pixels = ctx.getImageData(0, 0, canvasWidth, canvasHeight),
      pdata = pixels.data,
      l = pdata.length,
      total = (l / stride),
      count = 0;

    for (var i = count = 0; i < l; i += stride) {
      if (pdata[i + 3] < 10) {
        count++;
      }
    }

    return Math.round((count / total) * 100);
  }

  function getMouse(e, canvas) {
    var offsetX = 0, offsetY = 0, mx, my;

    if (canvas.offsetParent !== undefined) {
      do {
        offsetX += canvas.offsetLeft;
        offsetY += canvas.offsetTop;
      } while ((canvas = canvas.offsetParent));
    }

    mx = (e.pageX || e.touches[0].clientX) - offsetX;
    my = (e.pageY || e.touches[0].clientY) - offsetY;

    return { x: mx, y: my };
  }

  function handlePercentage(filledInPixels) {
    filledInPixels = filledInPixels || 0;
    console.log(filledInPixels + '%');
    if (filledInPixels > 40 && !canvas.dataset.removed) {
      canvas.dataset.removed = 'true';
      canvas.remove();
      canvas.removeEventListener('mousedown', handleMouseDown);
      canvas.removeEventListener('touchstart', handleMouseDown);
      canvas.removeEventListener('mousemove', handleMouseMove);
      canvas.removeEventListener('touchmove', handleMouseMove);
      canvas.removeEventListener('mouseup', handleMouseUp);
      canvas.removeEventListener('touchend', handleMouseUp);
    }
  }

  function handleMouseDown(e) {
    isDrawing = true;
    lastPoint = getMouse(e, canvas);
  }

  function handleMouseMove(e) {
    if (!isDrawing) { return; }

    e.preventDefault();

    var currentPoint = getMouse(e, canvas),
      dist = distanceBetween(lastPoint, currentPoint),
      angle = angleBetween(lastPoint, currentPoint),
      x, y;

    ctx.globalCompositeOperation = 'destination-out';
    for (var i = 0; i < dist; i++) {
      x = lastPoint.x + (Math.sin(angle) * i) - 25;
      y = lastPoint.y + (Math.cos(angle) * i) - 25;
      ctx.drawImage(brush, x, y);
    }

    lastPoint = currentPoint;
    handlePercentage(getFilledInPixels(32));
  }

  function handleMouseUp(e) {
    isDrawing = false;
  }

})();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 08:04:57