Как оформить оставшееся пространство в Canvas - PullRequest
1 голос
/ 04 апреля 2020

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

Ниже приведено то, чего я хотел бы достичь.

enter image description here

Вот что у меня есть:

enter image description here

Обратите внимание, что область лица должна быть прозрачной, а остальные размыты.

Вот мой код:

var ctx = context.drawImage(, 0, 0, 500, 500);
drawROI(ctx, width / 4, 50, 250, 350);

drawROI(ctx, x, y, w, h) {
  var kappa = 0.5522848,
    ox = (w / 2) * kappa,
    oy = (h / 2) * kappa,
    xe = x + w,
    ye = y + h,
    xm = x + w / 2,
    ym = y + h / 2;

  // Draw Oval
  ctx.beginPath();
  ctx.moveTo(x, ym);
  ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
  ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
  ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
  ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
  ctx.closePath();
  ctx.lineWidth = 5;
  ctx.stroke();
  ctx.strokeStyle = "#999";

  // Draw Rectange
  ctx.beginPath();
  ctx.rect(0, 0, this.video.width, this.video.height);
  ctx.fillStyle = "rgba(255, 255, 255, 0.5)";
  ctx.fill();
  ctx.stroke();
}

Примечание: drawROI - это то, где все происходит. У холста уже есть изображение, затем я рисую овал, а затем прямоугольник. Моя идея состояла в том, чтобы нарисовать прямоугольник сзади sh и четко отобразить лицо в овале.

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

Спасибо.

Ответы [ 2 ]

1 голос
/ 04 апреля 2020

Я предполагаю, что это в режиме реального времени. Вам нужно будет создать 2 холста, чтобы помочь с FX.

Матовое стекло на один слой. Чтобы не устанавливать заголовок фильтра размытия, фильтр всегда остается включенным.

Второй слой - это окно вставки. Эллипс рисуется, а затем изображение поверх этого с использованием составной операции "source-in" (изменяются только установленные пиксели)

Последний шаг dr aws двух слоев на холсте, а затем добавляет границу и подсвечивает как эллипс

const ctx = canvas.getContext("2d");
ctx.fillText("Loading image please wait..", 10,20)
Math.TAU = Math.PI * 2;
const img = new Image;
img.src = "http://www.createjs.com/demos/_assets/art/flowers.jpg";


img.onload = () => {

    // settings
    const blurAmount = 12; // in pixels
    const glassBlur = "blur(" + blurAmount + "px)"; // the blur filter
    const glassColor = "#EEE";
    const glassOpacity = 0.45;
    const faceRadius2 = canvas.height * (1/4);
    const faceRadius1 = canvas.width * (1/3);
    const borderWidth = canvas.width * (1/25);
    const insetBorderColor = "#999";
    const highlightColor = "255,255,255";

    // background image holds frosty glass
    const bg = document.createElement("canvas");
    bg.width = canvas.width;
    bg.height = canvas.height;
    bg.ctx = bg.getContext("2d");
    bg.ctx.drawImage(img, 0, 0);
    bg.ctx.filter = glassBlur;   // IMPORTANT TO SET FILTER EARLY or will cause
                                         // slowdown is done on the fly

    // create the mask for the window
    const windowMask = document.createElement("canvas");
    windowMask.width = canvas.width;
    windowMask.height = canvas.height;
    windowMask.ctx = windowMask.getContext("2d");

    // create the gradient for the highlights
    const highlight = ctx.createLinearGradient(
        0,
        canvas.height / 2 - faceRadius1 + borderWidth,
        0,
        canvas.height / 2 + faceRadius1 - borderWidth,
    );
    highlight.addColorStop(0,  "rgba("+highlightColor +",1)");
    highlight.addColorStop(0.25,"rgba("+highlightColor +",0.4)");
    highlight.addColorStop(0.5,"rgba("+highlightColor +",0)");
    highlight.addColorStop(0.75,"rgba("+highlightColor +",0.4)");
    highlight.addColorStop(1,  "rgba("+highlightColor +",1)");

    ctx.lineCap = "round"; // for the highlight

    var x,y; //position of image for demo

    // animate moving image
    requestAnimationFrame(loop);
    function loop(time) {
        x = -(Math.cos(time / 2000) * 20 + 20);
        y = -(Math.sin(time / 2000) * 20 + 20);
        frosty(img);
        faceWindow(img);
        drawFace();
        requestAnimationFrame(loop);
    
    }

    // draws frosted glass to bg canvas
    function frosty(img) {
         const w = bg.width;
         const h = bg.height;
         bg.ctx.drawImage(img, x, y);
         bg.ctx.fillStyle = glassColor;
         bg.ctx.globalAlpha = glassOpacity;
         bg.ctx.fillRect(-blurAmount, -blurAmount, w + blurAmount * 2, h + blurAmount * 2); 
         bg.ctx.globalAlpha = 1;

    }
   

    // creates inset window
    function faceWindow(img) {
        const w = windowMask.width;
        const h = windowMask.height;
        windowMask.ctx.clearRect(0, 0, w, h);
        windowMask.ctx.beginPath();
        windowMask.ctx.ellipse(w / 2, h / 2, faceRadius1, faceRadius2, 0, 0, Math.TAU);
        windowMask.ctx.fill();
        windowMask.ctx.globalCompositeOperation = "source-in";
        windowMask.ctx.drawImage(img, x, y,); // draw face 
        windowMask.ctx.globalCompositeOperation = "source-over";
    }


    // puts it all together.
    function drawFace() {
        const w = canvas.width;
        const h = canvas.height;
        ctx.drawImage(bg, 0, 0); // draw glass
        ctx.drawImage(windowMask, 0, 0); // draw face in window

        // draw border
        ctx.lineWidth = borderWidth;
        ctx.strokeStyle = insetBorderColor;

        ctx.beginPath();
        ctx.ellipse(w / 2, h / 2, faceRadius1, faceRadius2, 0, 0, Math.TAU);
        ctx.stroke();

        // draw highlights
        ctx.strokeStyle = highlight;  // gradient
        ctx.globalCompositeOperation = "lighter";
        ctx.globalAlpha = 0.65;

        ctx.beginPath();
        ctx.ellipse(w / 2, h / 2, faceRadius1 - borderWidth * 2, faceRadius2 - borderWidth * 2, 0, 0, Math.PI / 2);
        ctx.stroke();
        
        ctx.beginPath();
        ctx.ellipse(w / 2, h / 2, faceRadius1 - borderWidth * 2, faceRadius2 - borderWidth * 2, 0, Math.PI, Math.PI * 1.5);
        ctx.stroke();

        ctx.globalCompositeOperation = "source-over";
        ctx.globalAlpha = 1;
    }
}
canvas { border: 2px solid black; }
<canvas id="canvas" width="200" height="350"> </canvas>
0 голосов
/ 04 апреля 2020

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

С учетом исходного изображения:

original

Canvas filter свойство может быть использовано для создания всевозможных эффектов. Например, здесь мы включаем фильтры размытия и сепии, и после этого при рисовании изображения будут применены эти эффекты.

context.filter = 'blur(4px) sepia(1)';
context.drawImage(image, 0, 0, 400, 400);
context.filter = 'none';

filters

Затем нам нужно очистить фигуру (в вашем случае это эллипс ) от этого изображения, чтобы впоследствии ее можно было заполнить нефильтрованным чистым изображением. Это можно сделать с помощью свойства globalCompositeOperation canvas - оно позволяет объединять различные источники в окончательный чертеж.

destination-out значение имеет следующее поведение - существующий контент сохраняется там, где он не перекрывать новую фигуру, т. е. когда мы рисуем фигуру в этом режиме, отфильтрованное изображение останется прежним, но фигура будет очищена.

context.globalCompositeOperation = 'destination-out';
context.ellipse(200, 80, 80, 100, 0, 0, 2 * Math.PI);
context.fill();

clear shape

Наконец, мы можем использовать другую составную операцию, чтобы заполнить эту пустую форму чистым изображением.

destination-atop нарисует новое чистое изображение «позади» отфильтрованного изображения, но поскольку у нас есть « дыра "в отфильтрованном изображении, оно будет отображаться именно там, где нам нужно.

context.globalCompositeOperation = 'destination-atop';
context.drawImage(image, 0, 0, 400, 400);

final

См. JSBin для полного источника: https://jsbin.com/socexefawu/edit?html js, выход

...