Я предполагаю, что это в режиме реального времени. Вам нужно будет создать 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>