Как нарисовать изображение внутри HTML-холста с помощью JavaScript для специальных фильтров - PullRequest
0 голосов
/ 11 июня 2019

Мне удалось применить CSS-карандашный эффект к изображению с этого замечательного сайта .Мне нужно повторить эффект, используя JavaScript drawImage внутри HTML-холста.

Контекст (CanvasRenderingContext2D) рисует изображение, используя свойство фильтра, но я не могу установить для контекста background-size, background-image, background-blend-mode, background-position.Мне нужно, чтобы окончательно отфильтрованное изображение было сохранено в базе данных.Обработка изображений должна выполняться в браузере, а не на стороне сервера.

Любой рабочий фрагмент действительно полезен.

Спасибо!

//  Must be onload, otherwise canvas is not ready
window.onload = function() {

    let imageWidth = 800
    let imageHeight = 600

    let canvas = document.getElementById("canvas")
    canvas.width = imageWidth
    canvas.height = imageHeight

    let context = canvas.getContext("2d");
    let img = new Image() 
    img.width = imageWidth
    img.height = imageHeight
    img.src = "https://bennettfeely.com/image-effects/css/photo.jpg"

    let cssfilter = "brightness(2) invert(1) grayscale(1)" 
    context.filter = cssfilter 
          
    /*
    // Tried, but it does not work
    img.style.backgroundSize = "cover"
    img.style.backgroundImage = "url('https://bennettfeely.com/image-effects/css/photo.jp'), url('https://bennettfeely.com/image-effects/css/photo.jp')"
    img.style.backgroundPosition = "calc(50% - 1px) calc(50% - 1px), calc(50% + 1px) calc(50% + 1px)"
  
    // img.style = "background-blend-mode: difference; background-position: calc(50% - 1px) calc(50% - 1px), calc(50% + 1px) calc(50% + 1px); filter: brightness(2) invert(1) grayscale(1); box-shadow: inset 0 0 0 1px black;"
            */

    // Draw image
    context.drawImage(img, 0, 0, imageWidth, imageHeight)
}
@supports (filter: invert(1)) and (background-blend-mode: difference) {
    .pencil-effect {
        background-size: cover;
        background-image: url("https://bennettfeely.com/image-effects/css/photo.jpg"), url("https://bennettfeely.com/image-effects/css/photo.jpg");
        background-blend-mode: difference;
        background-position: calc(50% - 1px) calc(50% - 1px), calc(50% + 1px) calc(50% + 1px);
        filter: brightness(2) invert(1) grayscale(1);
    }
}
<img id="original-img" src="https://bennettfeely.com/image-effects/css/photo.jpg" width="800" height="600">
<img class="pencil-effect" width="800" height="600">
<canvas id="canvas" style="background: red"></canvas>

1 Ответ

1 голос
/ 12 июня 2019

Вы можете добиться того же эффекта от API 2D-контекста, воспроизводя то, что делает ваш CSS, шаг за шагом.

Первый шаг - нарисовать изображение (первое фоновое изображение) со смещением -1 -1. Это может быть легко достигнуто с помощью drawImage().

const img = new Image();
img.onload = function() {
  const imageWidth = 800
  const imageHeight = 600
  const canvas = document.getElementById("canvas");
  canvas.width = imageWidth;
  canvas.height = imageHeight;
  const context = canvas.getContext("2d");

// first pass without any filter nor blending
  // simple offset
  context.drawImage(img, -1, -1, imageWidth, imageHeight)
};
img.src = "https://upload.wikimedia.org/wikipedia/commons/thumb/5/56/Point_Reyes_Lighthouse_%28April_2012%29.jpg/593px-Point_Reyes_Lighthouse_%28April_2012%29.jpg";
<canvas id="canvas" style="background: red"></canvas>

Второй шаг - смешать это изображение с его копией с небольшим смещением в другом направлении.
Режим смешивания difference также доступен в 2d context API через его свойство globalCompositeOperation:

const img = new Image();
img.onload = function() {
  const imageWidth = 800
  const imageHeight = 600
  const canvas = document.getElementById("canvas");
  canvas.width = imageWidth;
  canvas.height = imageHeight;
  const context = canvas.getContext("2d");

// first pass without any filter nor blending
  // simple offset
  context.drawImage(img, -1, -1, imageWidth, imageHeight)
// second pass, do the blending without filter
  context.globalCompositeOperation = 'difference';
  // note how we draw the canvas over itself with the counter offset
  context.drawImage(img, 1, 1, imageWidth, imageHeight);
};
img.src = "https://upload.wikimedia.org/wikipedia/commons/thumb/5/56/Point_Reyes_Lighthouse_%28April_2012%29.jpg/593px-Point_Reyes_Lighthouse_%28April_2012%29.jpg";
<canvas id="canvas" style="background: red"></canvas>

Последний шаг - применить CSS-фильтр brightness(2) invert(1) grayscale(1) к этому смешанному изображению.
И снова, API 2D-контекста может сделать это через свойство filter.

const img = new Image();
img.onload = function() {
  const imageWidth = 800
  const imageHeight = 600
  const canvas = document.getElementById("canvas");
  canvas.width = imageWidth;
  canvas.height = imageHeight;
  const context = canvas.getContext("2d");
  const cssfilter = "brightness(2) invert(1) grayscale(1)" 

// first pass without any fiter nor blending
  // simple offset
  context.drawImage(img, -1, -1, imageWidth, imageHeight)
// second pass, do the blending without filter
  context.globalCompositeOperation = 'difference';
  // note how we draw the canvas over itself with the counter offset
  context.drawImage(img, 1, 1, imageWidth, imageHeight);

// third pass, apply the filter on the blended result
  context.filter = cssfilter;
  // since there is no transparency we could also have set it to 'source-over'
  context.globalCompositeOperation = 'copy';
  // here we don't set any offset: we only apply the filter
  context.drawImage(context.canvas, 0, 0, imageWidth, imageHeight)
};
img.src = "https://upload.wikimedia.org/wikipedia/commons/thumb/5/56/Point_Reyes_Lighthouse_%28April_2012%29.jpg/593px-Point_Reyes_Lighthouse_%28April_2012%29.jpg";
<canvas id="canvas" style="background: red"></canvas>
...