Как добавить рамку вокруг замаскированного изображения с помощью canvas в JavaScript?

Вопрос или проблема

Я пытаюсь замаскировать изображение с помощью другого изображения (использующегося в качестве маски) и добавить обводку вокруг формы, определяемой маской, с помощью элемента в JavaScript.

Вот код, который я использую:

function applyFiltersAndConvertToBase64(imageUrl, materialImage) {
    return new Promise((resolve, reject) => {
        const maskImg = new Image();
        const mainImg = new Image();
        maskImg.crossOrigin = "anonymous"; // Важно для изображений с другого домена
        mainImg.crossOrigin = "anonymous";

        let imagesLoaded = 0;

        const onLoad = () => {
            imagesLoaded++;
            if (imagesLoaded === 2) {
                // Создаем элемент canvas
                const canvas = document.createElement('canvas');
                const ctx = canvas.getContext('2d');

                // Устанавливаем размеры canvas в размер маски
                canvas.width = maskImg.width;
                canvas.height = maskImg.height;

                // Рисуем маску
                ctx.drawImage(maskImg, 0, 0);

                // Меняем режим композитинга для использования маски для обрезки
                ctx.globalCompositeOperation = 'source-in';

                // Рисуем основное изображение для обрезки
                ctx.drawImage(mainImg, 0, 0, maskImg.width, maskImg.height);

                // Сбрасываем режим композитинга
                ctx.globalCompositeOperation = 'source-over';

                // Преобразуем canvas в Base64
                const base64Image = canvas.toDataURL('image/png');
                resolve(base64Image);
            }
        };

        maskImg.onload = onLoad;
        mainImg.onload = onLoad;

        maskImg.onerror = reject;
        mainImg.onerror = reject;

        // Устанавливаем источники
        maskImg.src = imageUrl; // Изображение маски
        mainImg.src = materialImage; // Основное изображение
    });
}

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

Как я могу добавить обводку, которая будет следовать форме, определяемой маской, а не прямоугольным границам canvas? Я хочу, чтобы обводка точно следовала краям маски.

Любая помощь будет оценена!

Как я могу добавить обводку, которая будет следовать форме, определяемой маской, а не прямоугольным границам canvas? Я хочу, чтобы обводка точно следовала краям маски.

Ответ или решение

Чтобы добавить рамку, которая обрисовывает форму маски, а не прямоугольные границы канваса, можно использовать несколько техник на основе Canvas API. Основная идея заключается в том, чтобы сначала нарисовать маску на канвасе с помощью метода ctx.clip(), а затем нарисовать рамку после завершения обрезки. Вот как это можно реализовать:

function applyFiltersAndConvertToBase64(imageUrl, materialImage, borderWidth = 5, borderColor = 'black') {
    return new Promise((resolve, reject) => {
        const maskImg = new Image();
        const mainImg = new Image();
        maskImg.crossOrigin = "anonymous"; // Важно для кросс-доменных изображений
        mainImg.crossOrigin = "anonymous";

        let imagesLoaded = 0;

        const onLoad = () => {
            imagesLoaded++;
            if (imagesLoaded === 2) {
                // Создаем элемент canvas
                const canvas = document.createElement('canvas');
                const ctx = canvas.getContext('2d');

                // Устанавливаем размеры canvas по размерам маски
                canvas.width = maskImg.width;
                canvas.height = maskImg.height;

                // Рисуем маску
                ctx.drawImage(maskImg, 0, 0);

                // Применяем композитный режим для обрезки
                ctx.globalCompositeOperation = 'source-in';
                ctx.drawImage(mainImg, 0, 0, maskImg.width, maskImg.height);
                ctx.globalCompositeOperation = 'source-over'; // Сбрасываем композитный режим

                // Рисуем рамку
                ctx.lineWidth = borderWidth; // Устанавливаем ширину рамки
                ctx.strokeStyle = borderColor; // Устанавливаем цвет рамки

                // Создаем путь для рамки, который повторяет форму маски
                ctx.beginPath();
                ctx.drawImage(maskImg, 0, 0); // Рисуем маску для определения её контура
                ctx.globalCompositeOperation = 'destination-atop'; // Применяем режим, который позволит рисовать только внутри маски
                ctx.rect(0, 0, maskImg.width, maskImg.height); // Рисуем прямоугольник
                ctx.stroke(); // Рисуем рамку
                ctx.globalCompositeOperation = 'source-over'; // Сбрасываем композитный режим

                // Конвертируем канвас в Base64
                const base64Image = canvas.toDataURL('image/png');
                resolve(base64Image);
            }
        };

        maskImg.onload = onLoad;
        mainImg.onload = onLoad;

        maskImg.onerror = reject;
        mainImg.onerror = reject;

        // Устанавливаем источники изображений
        maskImg.src = imageUrl; // Маска
        mainImg.src = materialImage; // Основное изображение
    });
}

Пояснение к коду

  1. Создание канваса: Размер канваса совпадает с размерами маски.
  2. Рисование маски и основного изображения: Маска и основное изображение рисуются на канвасе. Композитный режим source-in используется для обрезки основного изображения по форме маски.
  3. Рисование рамки: Рамка рисуется после того, как основное изображение было обрезано. Сначала устанавливаем ширину и цвет рамки. Мы используем globalCompositeOperation = 'destination-atop', чтобы рамка применялась только в области, где есть форма маски. Это гарантирует, что рамка будет следовать контурам маски.
  4. Конвертация в Base64: После всех операций канвас преобразуется в Base64 изображение, которое можно использовать по вашему усмотрению.

Теперь рама будет корректно отображаться, следуя контурам маски, а не создавая прямоугольник.

Оцените материал
Добавить комментарий

Капча загружается...