Ошибки фоновой растеризации Paper.js

Мы создаем своего рода редактор изображений, используя 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;

но безрезультатно.

Что вызывает такое глючное поведение и как его предотвратить?

Обновить

Для ясности (надеюсь) и полноты я решил опубликовать весь класс PaperFunctions, который мы используем вместе с React, в котором размещен наш элемент <canvas>. Там много кода и много чего надо почистить, особенно в registerCanvasEvents(). Потерпите этого новичка. Кроме того, это несколько сотен строк, поэтому может быть полезно вставить их в ваш любимый редактор. Точки входа включают setCanvas, который вызывается в componentDidMount класса React с элементом <canvas>, и 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 для ясности. Он использует минимум бумажных функций, в основном просто выполняет вычисления:

// 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;

Спасибо.


person Scotty H    schedule 12.11.2015    source источник
comment
Я думаю, было бы намного проще, если бы вы использовали слои и переключали видимость и активность при выборе другого базового изображения. Таким образом, вам не нужно (раз) загружать свои аннотации при переключении между растрами. Есть ли причина, по которой вы используете отдельные проекты?   -  person Alex Blackwood    schedule 13.11.2015
comment
@AlexBlackwood Это кажется разумным. Пока не было указано, я не знал, что использую отдельные проекты.   -  person Scotty H    schedule 13.11.2015
comment
Это определенно подход, который я бы выбрал. Как упоминал @bmacnaughton, для текущего слоя лучше использовать paper.project.activeLayer. Вы можете установить активный из вашей очереди с помощью paper.project.layers[index].activate().   -  person Alex Blackwood    schedule 13.11.2015
comment
@AlexBlackwood - я согласен, что несколько слоев кажутся правильным способом сделать это. Странно, однако, что бумага очищала первый проект, когда был создан второй проект. Даже используя тот же холст, я не ожидал такого поведения.   -  person bmacnaughton    schedule 13.11.2015


Ответы (1)


Здесь я делаю некоторые предположения, но я решу некоторые проблемы в коде, который, надеюсь, устранит поведение, которое вы видите.

Во-первых, я предполагаю, что paper.project.layers[0] это paper.project.activeLayer. После растеризации 1) растр добавляется к слою, и установка visible = false приводит к его исчезновению при выполнении обновления.

Во-вторых, когда вы вызываете paper.setup(canvas) в imageElement.onload, вы создаете новый бумажный проект. Этот проект начинается как активный проект и делает предыдущий проект «исчезающим». Поэтому, когда вы создаете растр с помощью raster = new paper.Raster(...), он попадает в новый проект, а не в старый.

Итак, теперь есть скрытый (.visible = false) растр в старом проекте (назовем его проект1) и его новая версия в проекте2.

Я не уверен, является ли это предполагаемым поведением или нет, но когда вы вызываете paper.setup(canvas) во второй раз, бумага, кажется, замечает, что они оба относятся к одному и тому же холсту, и синхронизирует проекты1 и проект2. Таким образом, создание второго проекта очищает дочерний массив первого проекта. И добавление new paper.Raster(...) приводит к добавлению растра в проект1 и проект2.

Теперь я не могу сказать, какая следующая часть головоломки. Вам нужно будет добавить некоторую информацию, например 1) где установлены обработчики событий мыши и к чему они подключены, 2) что делает setUpNotes(), 3) что делает registerCanvasEvents() и 4) что делает fitToPage.

Создано несколько глобальных переменных, imageHeight и raster, которые, вероятно, созданы непреднамеренно. И непонятно, зачем вообще нужно использовать 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 имеет обработчик для 'мышиного перетаскивания'. Я не могу отслеживать, что должны делать все разные обработчики в разных проектах. Я предполагаю, что это основная проблема с мерцанием, которое вы видели.

Я бы, скорее всего, просто использовал 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', ...) и самостоятельно обрабатывать события mouseenter и mouseleave, используя hitTest, как показано выше, но любой подход должен работать.

Я думаю, что использование подхода на основе слоев к вашим изображениям будет поддерживать синхронизацию. Существует достаточно проблем с подходом, основанным на проектах, и множеством различных обработчиков событий мыши, поэтому вы все равно окажетесь на более стабильной основе.

person bmacnaughton    schedule 13.11.2015
comment
Я обновил свой вопрос со всем классом PaperFunctions, чтобы вы могли видеть вещи в контексте. Требуется много очистки, но, надеюсь, это имеет смысл. Я надеюсь, что это полезно. - person Scotty H; 13.11.2015
comment
Спасибо всем за информацию. Скорее всего, я бы использовал paper.view.on для событий mousedown, mousedrag и mouseup. View не поддерживает следующие действия: paperjs.org/reference/view/#on-type-function Но paper.tool кажется подходящим глобальным эквивалентом. - person Scotty H; 13.11.2015
comment
paper's documentation doesn't show them but it is supported in 0.9.25 - see this sketch.paperjs.org/#S/. Он был добавлен недавно. - person bmacnaughton; 13.11.2015
comment
Если вы считаете, что это относится к вашему вопросу, я был бы признателен, если бы вы приняли ответ, а не просто проголосовали за него. Если нет, дайте мне знать, чего не хватает. - person bmacnaughton; 13.11.2015
comment
Вот дополнительный вопрос о загрузке изображений и активного слоя: stackoverflow.com/questions/33785370/ - person Scotty H; 18.11.2015