【问题标题】:Paper.js Background Rasterization GlitchesPaper.js 背景光栅化故障
【发布时间】:2015-11-12 22:23:15
【问题描述】:

我们正在使用 Paper.js 构建各种图像编辑器。我们在 Paper.js 画布的一侧有一个队列,允许在图像之间切换。每次在图像之间切换时,我们都希望将所有注释(光栅化)展平到刚刚编辑的图像上。

每次我们切换图像时,都会调用此方法,它将当前图像和注释栅格化为数据 URL。 (如果我们重新访问此图像,将显示来自此数据 URL 的栅格。)

var flattenToDataUrl = function() {
  layerAsRaster = paper.project.layers[0].rasterize(); // Layer to Paper.js Raster object
  layerAsRaster.visible = false; // Attempt to set not visible
  var dataString = layerAsRaster.toDataURL();
  return dataString;
};

然后我们最终调用了这个方法,它改变了我们正在编辑的图像:

var setCanvasImage = function(imageObject) {
  if(imageObject != null)
  {
    imageHeight = imageObject.height;
    var imageWidth = imageObject.width;

    // Set up HTMLImage
    var imageElement = new Image(imageObject.width, imageObject.height);
    if(_.has(imageObject, 'imageData')) { // Came as 64 bit data
      imageElement.src = 'data:image/png;base64,' + imageObject.imageData;
    } else if(_.has(imageObject, 'imageUrl')) { // Came as URL
      imageElement.src = imageObject.imageUrl;
    }

    // Add image to Paper.js canvas
    imageElement.onload = function(event) {

      // Initialize Paper.js on the canvas
      paper.setup(canvas);

      raster = new paper.Raster(imageElement, new paper.Point(canvas.width / 2, canvas.height / 2));

      setUpNotes();

      selectedItems = new paper.Group(); // Since Paper.js has been setup we can now initialize this Group
      registerCanvasEvents(); // Panning, zooming, moving selected items, deselecting all selected items

      fitToPage();
    };
  }
};

所以,这会改变图像,但是当我在队列中选择不同的图像后将鼠标移动到画布中时,它会出现我们刚刚在其上的图像(带有注释),直到我执行类似平移,缩放等。然后我会看到我选择并真正使用的图像。

删除flattenToDataUrl() 功能可使队列无缝工作。所以在我看来,那里有些可疑。我们在该方法中生成一个 Paper.js Raster 对象。栅格似乎会自动添加自己。我试图通过调用

来遏制这种情况
layerAsRaster.visible = false;

但无济于事。

是什么导致了这种故障行为,我该如何预防?

更新

为了清晰(希望)和完整性,我决定发布我们与 React 结合使用的整个 PaperFunctions 类,它承载了我们的<canvas> 元素。有很多代码,还有很多清理工作要做,尤其是在registerCanvasEvents() 中。忍受这个学习初学者。而且它有几百行,因此将其粘贴到您喜欢的编辑器中可能会有所帮助。入口点包括setCanvas,它在带有<canvas> 元素的React 类的componentDidMount 中调用,以及从队列中调用的canvasSetImage。我同意 bmacnaughton 的回答,每次加载新图像时调用paper.setup(canvas) 很奇怪。我目前正在研究正确的解决方案,正确的放置位置。 setCanvas 似乎合乎逻辑,但是当我拖动图像以在该设置中移动它时,它会留下一连串图像。无论如何,这里是 PaperFunctions.js:

var JQueryMousewheel = require('jquery-mousewheel')($);

var SimplePanAndZoom = require('./SimplePanAndZoom.js');
var panAndZoom = new SimplePanAndZoom();

var selectedItems;

// We use selection here in two distinct ways.
// An item may be Paper.js selected but not in the selection group.
// This is because we want to show it is selectable.
// A blue bounding box indicates it is selectable.
// A green bounding box indicates it has actually been selected and added to selectedItems.
// Only things in selectedItems are actually operated on.
// So the event handlers in this method basically set up whether or not the item is in selectedItems (and therefore will be operated on for moving, resizing, deleting, etc.).
// That is, the event handlers here are concerned with displaying to the user the status of selection for the item - whether or not it will be operated on when events actually happen on the selectedItems Group.
var registerItemEvents = function(item) {
  // Boolean flag for mouseup to know if was drag or click
  var dragged;

  // For tracking if dragging or clicking is happening
  item.on('mousedown', function(e) {
    dragged = false;
  });

  // On click toggle selection
  item.on('mouseup', function(event) {
    event.stopPropagation(); // Only for item applied to
    event.preventDefault();

    if(!dragged) {
      var justAdded = addIfNotInSelectedItems(item);
      if(!justAdded) { // Item was in selection group, remove it
        item.remove();
        paper.project.activeLayer.addChild(item);

        this.selectedColor = paper.project.activeLayer.selectedColor;
        //item.selected = false;
      }
    }
  });

  // Show as selectable even after has been deselected
  item.on('mousemove', function(event) {
    this.selected = true;
  })

  // If not selected, on mouse enter show that it is selectable
  item.on('mouseenter', function(event) {
    if(!this.selected) {
      this.selected = true;
    }
  });

  // If not selected, on mouse leave remove indicator that is selectable
  item.on('mouseleave', function(event) {
    var isInSelectedItems = selectedItems.getItem(item);
    if(this.selected && isInSelectedItems == null) {
      this.selected = false;
    }
  });

  // On drag, move item
  item.on('mousedrag', function(event) {
    dragged = true;

    // If user starts dragging automatically select the item
    addIfNotInSelectedItems(item);
  });
}

var addIfNotInSelectedItems = function(item) {
  var isInSelectedItems = selectedItems.getItem(item);
  if(isInSelectedItems == null) { // Item not currently in selection group, add it
    selectedItems.addChild(item);
    item.selectedColor = 'green';
    item.selected = true;
    return true; // Was added, return true
  } else {
    return false; // Already in, return false
  }
}

var registerCanvasEvents = function() {
  if(paper.view != null && canvas != null) {
    // Zoom on mousewheel
    $(canvas).mousewheel(function(event) {
      event.preventDefault();
      var mousePosition = new paper.Point(event.offsetX, event.offsetY);
      var viewPosition = paper.view.viewToProject(mousePosition);
      var returnedValues = panAndZoom.changeZoom(paper.view.zoom, (event.deltaY * -1), paper.view.center, viewPosition, 1.1);
      var newZoom = returnedValues[0];
      var offset = returnedValues[1];
      paper.view.zoom = newZoom;
      paper.view.center = paper.view.center.add(offset);
    });

    // For tracking if dragging or clicking is happening
    var dragged;
    paper.project.layers[0].on('mousedown', function(e) { // TODO should be layer 0 in long run?
      dragged = false;
    });


    // Pan on mouse drag
    /*paper.project.layers[0].on('mousedrag', function(event) { // TODO should be layer 0 in long run?
      if(!event.event.ctrlKey && !event.event.altKey && !event.event.shiftKey) { // No keys (that we use) can be pushed
        dragged = true; // We're panning, we don't wish to deselect all items as we would do with a click
        paper.view.center = panAndZoom.changeCenter(paper.view.center, event.delta.x, event.delta.y, 0.7);
        //event.preventDefault();
      }
    });*/

    // Move selected items on mouse drag
    selectedItems.on('mousedrag', function(event) {
      event.stopPropagation(); // Don't propogate up or it will register as a pan event
      event.preventDefault();

      dragged = true; // We're panning, we don't wish to deselect all items as we would do with a click

      this.translate(new paper.Point(event.delta.x, event.delta.y));
    });

    // If was a click and not a drag, deselect selected items
    paper.project.layers[0].on('mouseup', function(event) {
      if(!dragged) {
        var removedItems = selectedItems.removeChildren(); // Remove from selection group, which also removes from display
        paper.project.activeLayer.addChildren(removedItems); // Return to display

        // Reset selection colors for showing selectable
        for(var i =0; i < removedItems.length; i++) {
          removedItems[i].selectedColor = paper.project.activeLayer.selectedColor;
          removedItems[i].selected = false;
        }
      }
    });

    // Initial path object, will be reset for new paths after Alt is released
    var path = newPath();
    var paths = [];
    paths.push(path);

    // On mousedown add point to start from
    paper.project.layers[0].on('mousedown', function(event) {
      if(event.event.altKey && !event.event.ctrlKey) { // Alt key to add a path, but disallow attempting to add text at the same time
        if(paths[paths.length-1].lastSegment == null) {
          //path.add(event.point, event.point);
          paths[paths.length-1].add(event.point, event.point);
        } else {
          //path.add(path.lastSegment.point, path.lastSegment.point);
          paths[paths.length-1].add(paths[paths.length-1].lastSegment.point, paths[paths.length-1].lastSegment.point);
        }
      }
    });

    // On mousedrag add points to path
    paper.project.layers[0].on('mousedrag', function(event) {
      if(event.event.altKey && !event.event.ctrlKey) { // Alt key to add a path, but disallow attempting to add text at the same time
        if(event.event.shiftKey) { // Use shift key for freeform
          //path.add(event.point);
          paths[paths.length-1].add(event.point);
        } else { // Default of straight line added to path
          //path.lastSegment.point = event.point;
          paths[paths.length-1].lastSegment.point = event.point;
        }
      }
    }.bind(this));

    var tool = new paper.Tool();

    var startDragPoint;

    // Capture start of drag selection
    paper.tool.onMouseDown = function(event) {
      if((event.event.ctrlKey && event.event.shiftKey) || (event.event.ctrlKey && event.event.altKey)) {
        startDragPoint = new paper.Point(event.point);
      }
    };

    paper.tool.onMouseDrag = function(event) {
      // Panning
      if(!event.event.ctrlKey && !event.event.altKey && !event.event.shiftKey) { // No keys (that we use) can be pushed
        dragged = true; // We're panning, we don't wish to deselect all items as we would do with a click
        paper.view.center = panAndZoom.changeCenter(paper.view.center, event.delta.x, event.delta.y, 0.7);
        //event.preventDefault();
      }

      // Show box indicating the area that has been selected
      // For moving area and whiting out area
      if((event.event.ctrlKey && event.event.shiftKey) || (event.event.ctrlKey && event.event.altKey)) {
        dragged = true;
        var showSelection = new paper.Path.Rectangle({
            from: startDragPoint,
            to: event.point,
            strokeColor: 'red',
            strokeWidth: 1
        });

        // Stop showing the selected area on drag (new one is created) and up because we're done
        showSelection.removeOn({
            drag: true,
            up: true
        });
      }
    };

    // Capture start of drag selection
    paper.tool.onMouseUp = function(event) {
      if((event.event.ctrlKey && event.event.shiftKey) || (event.event.ctrlKey && event.event.altKey)) {
        var endDragPoint = new paper.Point(event.point);
        if(event.event.ctrlKey && event.event.shiftKey) { // Whiteout area
          whiteoutArea(startDragPoint, endDragPoint);
        } else if(event.event.ctrlKey && event.event.altKey) { // Move selected area
          selectArea(startDragPoint, endDragPoint);
        }
      }
    };

    // Key events
    paper.tool.onKeyUp = function(event) {
      // Delete selected items on delete key
      if(event.key == 'delete') {
        selectedItems.removeChildren();
      } else if (event.key == 'option') {
        registerItemEvents(paths[paths.length-1]);

        // Start a new path
        paths.push(newPath());
      }
    }
  }
}

// These variables are scoped so that all methods in PaperFunctions can access them
var canvas; // Set by setCanvas
var imageHeight; // Set by setCanvasImage

var raster;

var toolsSetup = false;

var setCanvas = function(canvasElement) {
  canvas = canvasElement;
  paper.setup(canvas);
};

var setCanvasImage = function(imageObject) {
  if(imageObject != null)
  {
    imageHeight = imageObject.height;
    var imageWidth = imageObject.width;

    // Set up HTMLImage
    var imageElement = new Image(imageObject.width, imageObject.height);
    if(_.has(imageObject, 'imageData')) { // Came as 64 bit data
      imageElement.src = 'data:image/png;base64,' + imageObject.imageData;
    } else if(_.has(imageObject, 'imageUrl')) { // Came as URL
      imageElement.src = imageObject.imageUrl;
    }

    // Add image to Paper.js canvas
    imageElement.onload = function(event) {
      //canvas.height = $(document).height()-3; // Set canvas height. Why do this here and not in the React component? Because we set the width here too, so we're keeping those together. Perhaps in the future this will be changed when we are responsive to window resizing.
      //scalingFactor = canvas.height / imageObject.height; // Determine the ratio
      //canvas.width = imageElement.width * scalingFactor; // Scale width based on height; canvas height has been set to the height of the document

      // Initialize Paper.js on the canvas
      paper.setup(canvas);

      raster = new paper.Raster(imageElement, new paper.Point(canvas.width / 2, canvas.height / 2));

      //setUpLineAndFreeFormDrawing(); // TODO once we cycle through images will we need to reset this for each new image or can we do this just once?

      setUpNotes(); // TODO once we cycle through images will we need to reset this for each new image or can we do this just once?

      selectedItems = new paper.Group(); // Since Paper.js has been setup we can now initialize this Group
      registerCanvasEvents(); // Panning, zooming, moving selected items, deselecting all selected items

      fitToPage();
    };
  }
};

var fitToPage = function() {
  if(paper.view != null && canvas != null) {
    // Fit image to page so whole thing is displayed
    var scalingFactor = canvas.height / imageHeight; // Constant representation of the ratio of the canvas size to the image size
    var zoomFactor = scalingFactor / paper.view.zoom; // Dynamic representation of the zoom needed to return to viewing the whole image in the canvas

    // Reset the center point to the center of the canvas
    var canvasCenter = new paper.Point(canvas.width/2, canvas.height/2);
    paper.view.center = canvasCenter;

    // Zoom to fit the whole image in the canvas
    var returnedValues = panAndZoom.changeZoom(paper.view.zoom, -1, canvasCenter, canvasCenter, zoomFactor); // Always pass -1 as the delta, not entirely sure why
    var newZoom = returnedValues[0];
    var offset = returnedValues[1];
    paper.view.zoom = newZoom;
    paper.view.center = paper.view.center.add(offset);
  }
};

var addImage = function(imageDataUrl) {
  if(paper.view != null) {
    var img = document.createElement("img");
    img.src = imageDataUrl;
    var presentMomentForId = new Date().getTime() + "-image"; // For purposes of having unique IDs
    img.id = presentMomentForId;
    img.hidden = true;
    document.body.appendChild(img);

    var raster = new paper.Raster(presentMomentForId);

    registerItemEvents(raster);
  }
};

var setUpLineAndFreeFormDrawing = function() {
  if(paper.project != null) {
    // Initial path object, will be reset for new paths after Alt is released
    var path = newPath();
    var paths = [];
    paths.push(path);

    // On mousedown add point to start from
    paper.project.layers[0].on('mousedown', function(event) {
      if(event.event.altKey && !event.event.ctrlKey) { // Alt key to add a path, but disallow attempting to add text at the same time
        if(paths[paths.length-1].lastSegment == null) {
          //path.add(event.point, event.point);
          paths[paths.length-1].add(event.point, event.point);
        } else {
          //path.add(path.lastSegment.point, path.lastSegment.point);
          paths[paths.length-1].add(paths[paths.length-1].lastSegment.point, paths[paths.length-1].lastSegment.point);
        }
      }
    });

    // On mousedrag add points to path
    paper.project.layers[0].on('mousedrag', function(event) {
      if(event.event.altKey && !event.event.ctrlKey) { // Alt key to add a path, but disallow attempting to add text at the same time
        if(event.event.shiftKey) { // Use shift key for freeform
          //path.add(event.point);
          paths[paths.length-1].add(event.point);
        } else { // Default of straight line added to path
          //path.lastSegment.point = event.point;
          paths[paths.length-1].lastSegment.point = event.point;
        }
      }
    }.bind(this));

    // Each time Alt comes up, start a new path
    paper.tool.onKeyUp = function(event) {
      if(event.key == "option") {
        registerItemEvents(paths[paths.length-1]);

        // Start a new path
        paths.push(newPath());
      }
    };
  }
};

// Establishes default line style
var newPath = function() {
  var path = new paper.Path();
  path.strokeColor = 'black';
  path.strokeWidth = 10;
  return path;
};

var note = "";
var setNote = function(newNote) {
  note = newNote;
};

var setUpNotes = function() {
  if(paper.project != null) {
    paper.project.layers[0].on('mousedown', function(event) { // TODO should be layer 0 in long run?
      if(event.event.ctrlKey && !event.event.altKey && !event.event.shiftKey) { // Only Ctrl key to add text

        // Add text box
        var textBox = new paper.PointText(event.point);
        textBox.justification = 'left';
        textBox.fillColor = 'black';
        textBox.fontSize = 60;
        textBox.content = note;

        registerItemEvents(textBox);
      }
    });
  }
};

var selectArea = function(startDragPoint, endDragPoint) {
  var rasterTopLeftCorner = new paper.Point(raster.bounds.topLeft);
  var adjustedStartDragPoint = new paper.Point(startDragPoint.x - rasterTopLeftCorner.x, startDragPoint.y - rasterTopLeftCorner.y);
  var adjustedEndDragPoint = new paper.Point(endDragPoint.x - rasterTopLeftCorner.x, endDragPoint.y - rasterTopLeftCorner.y);
  var boundingRectangleRasterCoordinates = new paper.Rectangle(adjustedStartDragPoint, adjustedEndDragPoint);
  var boundingRectangleCanvasCoordinates = new paper.Rectangle(startDragPoint, endDragPoint);

  var selectedArea = raster.getSubRaster(boundingRectangleRasterCoordinates);

  var whitedOutSelection = new paper.Shape.Rectangle(boundingRectangleCanvasCoordinates);
  whitedOutSelection.fillColor = 'white';
  whitedOutSelection.insertAbove(raster); // Whiteout just above the image we're working with

  registerItemEvents(selectedArea);
}

var whiteoutArea = function(startDragPoint, endDragPoint) {
  var whitedOutSelection = new paper.Shape.Rectangle(startDragPoint, endDragPoint);
  whitedOutSelection.fillColor = 'white';
  whitedOutSelection.insertAbove(raster); // Whiteout just above the image we're working with
}

var flattenToDataUrl = function() {
  layerAsRaster = paper.project.layers[0].rasterize(); // TODO should be layer 0 in long run? // Layer to Paper.js Raster object
  layerAsRaster.visible = false;
  var dataString = layerAsRaster.toDataURL();
  return dataString;
};

module.exports = {
  setCanvas: setCanvas,
  setCanvasImage: setCanvasImage,
  fitToPage: fitToPage,
  addImage: addImage,
  setNote: setNote,
  flattenToDataUrl: flattenToDataUrl
};

此外,为了清楚起见,这里是 SimplePanAndZoom.js 文件。它使用最少的 Paper 函数,主要只是做计算:

// Based on http://matthiasberth.com/articles/stable-zoom-and-pan-in-paperjs/

var SimplePanAndZoom = (function() {
  function SimplePanAndZoom() { }

  SimplePanAndZoom.prototype.changeZoom = function(oldZoom, delta, centerPoint, offsetPoint, zoomFactor) {
    var newZoom = oldZoom;
    if (delta < 0) {
      newZoom = oldZoom * zoomFactor;
    }
    if (delta > 0) {
      newZoom = oldZoom / zoomFactor;
    }

    // Zoom towards offsetPoint, not centerPoint (unless they're the same)
    var a = null;
    if(!centerPoint.equals(offsetPoint)) {
      var scalingFactor = oldZoom / newZoom;
      var difference = offsetPoint.subtract(centerPoint);
      a = offsetPoint.subtract(difference.multiply(scalingFactor)).subtract(centerPoint);
    }

    return [newZoom, a];
  };

  SimplePanAndZoom.prototype.changeCenter = function(oldCenter, deltaX, deltaY, factor) {
    var offset;
    offset = new paper.Point(-deltaX, -deltaY);
    offset = offset.multiply(factor);
    return oldCenter.add(offset);
  };

  return SimplePanAndZoom;

})();

module.exports = SimplePanAndZoom;

谢谢。

【问题讨论】:

  • 我认为如果您在选择另一个基本图像时使用图层并切换可见性和活动性会容易得多。这样您就不必在栅格之间切换时(卸载)加载注释。您是否有理由改为使用不同的项目?
  • @AlexBlackwood 这似乎是合理的。直到有人指出我不知道我正在使用单独的项目。
  • 这绝对是我会采取的方法。正如@bmacnaughton 提到的,最好将 paper.project.activeLayer 用于当前层。您可以使用 paper.project.layers[index].activate() 设置队列中哪个是活动的。
  • @AlexBlackwood - 我同意多层似乎是正确的方法。奇怪的是,当创建第二个项目时,该文件清除了第一个项目。即使使用相同的画布,我也没想到会出现这种行为。

标签: javascript image canvas raster paperjs


【解决方案1】:

我在这里进行了一些猜测,但我将解决代码中的一些问题,希望能解决您所看到的行为。

首先,我假设paper.project.layers[0]paper.project.activeLayer。栅格化后 1) 栅格被添加到图层并设置 visible = false 确实会导致它在更新完成时消失。

其次,当您在imageElement.onload 中调用paper.setup(canvas) 时,您将创建一个新的论文项目。该项目作为活动项目开始,并使之前的项目“消失”。因此,当您使用 raster = new paper.Raster(...) 创建栅格时,它会进入新项目,而不是旧项目。

所以现在在旧项目(我们称之为 project1)中有一个隐藏的 (.visible = false) 栅格,在 project2 中有一个新版本。

我不确定这是否是预期的行为,但是当您似乎第二次调用 paper.setup(canvas) 时,纸张似乎注意到它们都引用同一个画布并保留 project1 和 project2同步中。所以创建第二个项目会清除第一个项目的 children 数组。添加new paper.Raster(...) 最终会将栅格添加到project1 和project2。

现在我不知道下一个难题是什么。您需要添加一些信息,例如 1) 鼠标事件处理程序的设置位置和附加对象,2) setUpNotes() 的作用,3) registerCanvasEvents() 的作用,以及 4) fitToPage 的作用。

创建了一些全局变量,imageHeightraster,这可能不是故意的。而且还不清楚为什么您需要使用 new Image() - paper.Raster() 接受 URL,包括数据 URL。

我很惊讶论文通过了第一个项目。很好奇。

版本 2:

让我尝试使用层来构建它。我建议您摆脱多个项目,因为将鼠标事件处理程序附加到共享同一画布的多个项目会增加太多复杂性。

所以,在您的代码初始化中:paper.setup(canvas)。只做一次。

在最初由纸张创建的单层中设置初始图像。

// this will be inserted into the current layer, project.activeLayer
var raster = new paper.Raster(imageURL, paper.view.bounds.center);

当队列中的图像发生变化时,请执行以下操作:

// make the existing image/layer invisible
paper.project.activeLayer.visible = false;

// add a new layer which is inserted in the project and activated
var layer = new paper.Layer();

// the new layer is activated, create a raster for the image
var raster = new paper.Raster(imageURL, paper.view.bounds.center);

// now do your normal logic for editing, zooming, etc.

实际上比这要复杂一些,因为您有一个图像队列,并且您只想在第一次访问图像时创建一个图层。您可以在一开始就初始化所有栅格,例如:

var imageURLs = ["url to image1", "url to image2", "etc"];
imageURLs.forEach(function(url) {
    new paper.Layer();
    paper.project.activeLayer.visible = false;
    new paper.Raster(url, paper.view.bounds.center);
});
// make the first layer visible and activate it
paper.project.layers[0].visible = true;
paper.project.layers[0].activate();

前面的代码为队列中的图像提供了一个并行数组,因此切换图像很简单 - 无需检查该图像是否已创建:

function setImage(index) {
    paper.project.activeLayer.visible = false;
    paper.project.layers[index].activate();
    paper.project.layers[index].visible = true;
}

最后,我会确保我的鼠标操作不会给我带来问题。从您发布的新代码看来,每个项目都有一个处理“mousedown”、“mousedrag”和“mouseup”事件的全局工具,还有一组用于activeLayer的处理程序,用于“mousedown”、“mousedrag”和“ mouseup' 事件,selectedItems 也有一个处理 'mousedrag' 的处理程序。我无法跟踪项目中所有不同的处理程序应该做什么。我猜这些是你看到的闪烁的根本问题。

我很可能只使用paper.view.on 来处理“mousedown”、“mousedrag”和“mouseup”事件。当我收到一个事件时,我会使用以下命令检查图层上是否有任何内容:

paper.project.activeLayer.hitTest(event.point);

能够在视图上设置事件对于纸来说是新事物,但非常有用。处理突出显示未选择的项目可能需要进行一些其他调整。一种相对简单的处理方法是拥有一组已选项目和一组未选项目:

unSelectedGroup.on('mouseenter', function() {
    unSelectedGroup.selected = true;
});

unSelectedGroup.on('mouseleave', function() {
    unSelectedGroup.selected = false;
});

当一次只有一层可见时,这些应该是跨层安全的。我会在设置图像时设置这些组处理程序,无论是预先设置还是按需设置。或者,您也可以添加paper.view.on('mousemove', ...) 并使用hitTest 自己处理“mouseenter”和“mouseleave”事件,如上所示,但任何一种方法都应该有效。

我认为对图像使用基于层的方法可以保持同步。基于项目的方法和许多不同的鼠标事件处理程序存在足够多的问题,无论如何您都将处于更稳定的基础上。

【讨论】:

  • 我用我的整个 PaperFunctions 课程更新了我的问题,这样你就可以在上下文中看到事情了。它需要进行大量清理,但希望它有意义。我希望它会有所帮助。
  • 感谢您提供的所有信息。 我可能只将 paper.view.on 用于“mousedown”、“mousedrag”和“mouseup”事件。 View 不支持这些:paperjs.org/reference/view/#on-type-function 但 paper.tool 似乎很合适全局等效项。
  • paper 的文档没有显示它们,但在 0.9.25 中受支持 - 请参阅此 sketch.paperjs.org/#S/…。它是最近添加的。
  • 如果您认为这解决了您的问题,我将不胜感激您接受答案,而不仅仅是支持它。如果没有,请告诉我缺少什么。
  • 这是一个关于加载图像和活动层的后续问题:stackoverflow.com/questions/33785370/…
猜你喜欢
  • 2014-09-01
  • 1970-01-01
  • 2016-10-05
  • 1970-01-01
  • 2021-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多