HTML Canvas - фоновый ластик, работающий не за один клик - PullRequest
0 голосов
/ 07 октября 2019

Я работаю над приложением для удаления фона, используя html5 canvas и angularJS. Я укладываю два холста и удаляю пиксели из верхнего с помощью пользовательского курсора, который соответствует размеру ластика. У меня действительно странная ошибка, которая позволяет мне удалять передний план только в том случае, если я быстро перемещаю мышь, одиночные щелчки и медленное движение мыши не влияют на холст. Я подозреваю, что есть проблема с угловыми даже прослушивателями, так как та же версия кода, которую я запускал, используя liveServer в vscode и стандартных слушателях событий JS canvas.addEventListener('mousemove', erase);, работала нормально. Буду признателен за любую помощь в этом.

Я работаю над кодом, чтобы лучше проиллюстрировать проблему.

Разметка:

<div class="cursor" ng-mousemove="erase($event)"
     ng-mousedown="startPosition($event)"
     ng-mouseup="finishedPosition($event)"><!-- -->
    <!--ng-click="cursorMove($event)"-->
    <span class="innerCursor"></span>
</div>

<div class="canvas-container" ng-init="init();">
    <canvas id="canvas" style="z-index: 2;" ng-mousemove="erase($event);"
            ng-mousedown="startPosition($event)"
            ng-mouseup="finishedPosition($event)"></canvas>
    <canvas id="bgCanvas" style="z-index: 1;"></canvas>
</div>

JS:

//Gets real mouse position in canvas
function getMousePos(canvas, e) {
    var rect = canvas.getBoundingClientRect(), // abs. size of element
        scaleX = canvas.width / rect.width,    // relationship bitmap vs. element for X
        scaleY = canvas.height / rect.height;  // relationship bitmap vs. element for Y

    return {
        x: (e.clientX - rect.left) * scaleX,   // scale mouse coordinates after they have
        y: (e.clientY - rect.top) * scaleY     // been adjusted to be relative to element
    }
}

//Controls the movement of the custom cursor 
$scope.cursorMove = function (x, y) {
    var cursor = document.querySelector('.cursor');
    cursor.setAttribute('style', 'width: ' + $scope.options.brushSize + 'px; height: ' + $scope.options.brushSize + 'px; top: ' + y + 'px; left: ' + x + 'px; transform : translate(-' + 50 + '%, -' + 50 + '%)' + ';');
}

$scope.init = function () {
    //Foreground
    $scope.canvas = document.querySelector('#canvas');
    $scope.ctx = canvas.getContext('2d');
    //Background
    $scope.bgCanvas = document.querySelector('#bgCanvas');
    $scope.ctxBg = bgCanvas.getContext('2d');

    $scope.ctx.canvas.height = 600;
    $scope.ctx.canvas.width = 900;

    $scope.drawAll();
}

//Display all elements
$scope.drawAll = function () {
    $scope.drawForeground();
    $scope.drawBackground();
}

$scope.drawForeground = function () {
    console.log('foreground');
    var url = $scope.personalisationOptions.noBackgroundImage;
    //var url = 'img link';
    var img1 = new Image();
    img1.src = url + '?' + new Date().getTime();
    img1.setAttribute('crossOrigin', '');
    img1.onload = function () {
        $scope.ctx.drawImage(img1, 0, 0, $scope.ctx.canvas.clientWidth, $scope.ctx.canvas.clientHeight);
    }
}

$scope.drawBackground = function () {
    console.log('background');
    var img2 = new Image();
    img2.src = 'img link';
    img2.onload = function () {
        $scope.ctxBg.drawImage(img2, 0, 0, $scope.ctx.canvas.clientWidth, $scope.ctx.canvas.clientHeight);
    }
}

//Erasing functionality
$scope.erasing = false;

$scope.erase = function (e) {

    var pos = getMousePos(canvas, e);
    $scope.cursorMove(pos.x, pos.y);

    if (!$scope.erasing) return;

    if (!$scope.pendingSnapshot) {
        $scope.pendingSnapshot = $scope.ctx.getImageData(0, 0, window.innerWidth, window.innerHeight);
    }

    if ($scope.options.isCircle) {
        console.log('B');
        // circular eraser
        $scope.ctx.save();
        $scope.ctx.beginPath();
        $scope.ctx.arc(e.offsetX, e.offsetY, $scope.options.brushSize / 2, 0, 2 * Math.PI, true);
        $scope.ctx.clip();
        //eraser with new coordinates pos.x / pos.y
        $scope.ctx.clearRect(pos.x - $scope.options.brushSize, pos.y - $scope.options.brushSize, $scope.options.brushSize * 2, $scope.options.brushSize * 2);
        console.log(pos.x, pos.y - $scope.options.brushSize);
        $scope.ctx.restore();
    } else {
        // square eraser
        $scope.ctx.beginPath();
        $scope.ctx.clearRect(e.offsetX, e.offsetY, $scope.options.brushSize, $scope.options.brushSize);
    }
}

$scope.startPosition = function (e) {
    $scope.erasing = true;
    $scope.erase(e);
    console.log('mouse down');
}

$scope.finishedPosition = function () {
    $scope.erasing = false;
    console.log('mouse up');
    $scope.ctx.beginPath();
    $scope.imageSnapshot = $scope.pendingSnapshot;

    //limit clickHistory to 5
    if ($scope.clickHistory.length > 4) {
        $scope.clickHistory.shift();
    }

    $scope.clickHistory.push($scope.pendingSnapshot);
    $scope.pendingSnapshot = null;
}

1 Ответ

0 голосов
/ 08 октября 2019

Оказывается, это был мой пользовательский курсор, который вызывал проблему. Div курсора находился перед холстом и мешал функции стирания работать правильно, потому что у него никогда не было доступа к холсту. Это объясняет, почему стирание работало только тогда, когда я быстро двигал мышью. Фактический курсор опережал пользовательский.

Я сейчас использую решение курсора svg из этого поста: Динамическое изменение SVG-курсора

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...