Как создать холст неправильной формы HTML, используя Fabri cJS? - PullRequest
0 голосов
/ 01 февраля 2020

Я пытался создать холст треугольной формы в течение дня или около того, и мне не повезло. Холст всегда квадратный / прямоугольный. Я использую Fabri cJS, но я также могу напрямую управлять холстом, если это вариант.

Я пытался использовать .clipTo (ctx), чтобы обрезать холст, как описано здесь: Холст различной формы с фабрикой cjs плагин

Я также пытался манипулировать холстом напрямую, как я видел здесь: https://www.html5canvastutorials.com/tutorials/html5-canvas-custom-shapes/

То, что я пытаюсь сделать sh - это чтобы пользователь перетаскивал изображения на холст в форме треугольника, чтобы не было «выплескивания» изображения за пределы формы треугольника. Я легко выполнил это с помощью прямоугольника, но я не могу понять, как изменить форму холста. ИЛИ, если у кого-то есть «хитрое» решение, которое выглядело бы так, будто холст был треугольником, но под капотом оставался квадрат, это тоже сработало бы.

1 Ответ

1 голос
/ 01 февраля 2020

Использование чистого API

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

Холст всегда 4-сторонний. 2D и 3D преобразования могут изменить форму, но это также изменит форму содержащихся пикселей.

У вас есть 2 простых варианта. Есть и другие способы сделать это, но они сложны и имеют проблемы с совместимостью.

Только визуальный

Маскировка

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

Используйте свойство CanvasRenderingContext2D.globalCompositeOperation, чтобы определить способ применения маски.

например

function createTriangleMask(w, h) {
    const mask = document.createElement("canvas");
    mask.width = w;
    mask.height = h;
    mask.ctx = mask.getContext("2d");
    mask.ctx.beginPath();
    mask.ctx.lineTo(w / 2, 0);
    mask.ctx.lineTo(w    , h);
    mask.ctx.lineTo(0    , h);
    mask.ctx.fill();
    return mask;
}
const mask = createTriangleMask(ctx.canvas.width, ctx.canvas.height);
ctx.drawImage(myImg, 0, 0, ctx.canvas.width, ctx.canvas.height); 
ctx.globalCompositeOperation = "destination-in";
ctx.drawImage(mask, 0, 0, ctx.canvas.width, ctx.canvas.height);  
ctx.globalCompositeOperation = "source-over";

Использование 2D clip

Или вы можете использовать 2D API CanvasRenderingContext2D.clip, чтобы создать область клипа и нарисовать содержимое пока клип активен. Не забудьте выдвинуть 2D-состояние, когда закончите с клипом,

function triangleClip(ctx, w, h) {
    ctx.save();
    ctx.beginPath();
    mask.ctx.lineTo(w / 2, 0);
    mask.ctx.lineTo(w    , h);
    mask.ctx.lineTo(0    , h);
    ctx.clip();
}

triangleClip(ctx, ctx.canvas.width, ctx.canvas.height);
ctx.drawImage(myImg, 0, 0, ctx.canvas.width, ctx.canvas.height); 
ctx.restore(); // Turn off clip. Must do before calling triangle clip again.

Все еще прямоугольным angular!

Это не изменило форму холста. Это все еще прямоугольник, просто некоторые пиксели прозрачны. DOM по-прежнему видит прямоугольник, и пользовательские взаимодействия с холстом будут по-прежнему использовать весь прямоугольник angular canvas.

CSS clip-path

Вы можете использовать свойство style clip-path для определения формы элемента. Это обрезает элементы визуального содержимого и интерактивную область элементов. Эффективное превращение любого применимого элемента в элемент неправильной формы.

Использование JS Декларативное

canvas.style.clipPath = "polygon(50% 0, 100% 100%, 0% 100%)"

Использование JS

function clipElement(el, shape) {
    var rule = "polygon(", i = 0, comma = "";
    while (i < shape.length) { 
        rule += comma + shape[i++] + "% " + shape[i++] + "%";
        comma = ",";
    }
    el.style.clipPath = rule + ")";
}

clipElement(canvas, [50, 0, 100, 100, 0, 100]);

Использование CSS rule

canvas {
    clip-path: polygon(50% 0, 100% 100%, 0% 100%);
}

С обтравочным контуром на месте холст будет подчиняться своей форме через пользовательский интерфейс

canvas.style.cursor = "pointer";  // Pointer change only inside clipped area
canvas.title = "foo"; // appears only when over clipped area
canvas.addEventListener("mouseover", () => console.log("foo")); // fires when crossing
                                                                // clip boundary

Demo

Создает анимированный клип с помощью JS на элементе canvas с содержимым, отображаемым один раз.

Существуют ограничения

  • Примечание , что CSS определенный цвет фона (желтый) и тень также обрезаются. Многие другие визуальные свойства также будут обрезаны.

  • Обратите внимание , что анимация JS не обновляет события пользовательского интерфейса, если нет итерации пользователя.

  • Анимация также может быть достигнута через CSS.

  • Совместимость с Fabri c мне неизвестна, проверьте их документация .

var clearConsole = 0;
const s = 2 ** 0.5 * 0.25, clipPath = [0.5, 0, 0.5 + s, 0.5 + s,  0.5 - s, 0.5 + s], img = new Image;
img.src = "https://i.stack.imgur.com/C7qq2.png?s=328&g=1";
img.addEventListener("load",() => canvas.getContext("2d").drawImage(img, 0, 0, 300, 300), {once: true});

requestAnimationFrame(animateLoop);
function clipRotate(el, ang, scale, path) {
    const dx = Math.cos(ang) * scale;
    const dy = Math.sin(ang) * scale;
    var clip = "polygon(", i = 0, comma = "";
    while (i < path.length) {
        const x = path[i++] - 0.5;
        const y = path[i++] - 0.5;
        clip += comma;
        clip += ((x * dx - y * dy + 0.5) * 100) + "% ";
        clip += ((x * dy + y * dx + 0.5) * 100) + "%";
        comma = ",";
    }
    el.style.clipPath = clip + ")";    
}

function animateLoop(time) {
    clipRotate(canvas, time / 1000 * Math.PI, 0.9, clipPath);
    requestAnimationFrame(animateLoop);
    if (clearConsole) {
       clearConsole --;
       !clearConsole && console.clear();
    }
}

canvas.addEventListener("pointerenter", () => (clearConsole = 30, console.log("Pointer over")));
body {
  background-color: #49C;
}
canvas {
    cursor: pointer;
    background-color: yellow;
    box-shadow: 12px 12px 4px rgba(0,0,0,0.8);
}
<canvas id="canvas" width="300" height="300" title="You are over the clipped canvas"></canvas>
...