如何使用 JavaScript 在 html 画布内绘制图像以获取特殊过滤器

Posted

技术标签:

【中文标题】如何使用 JavaScript 在 html 画布内绘制图像以获取特殊过滤器【英文标题】:How to draw an image inside a html canvas with JavaScript for special filters 【发布时间】:2019-10-25 19:08:45 【问题描述】:

我已经成功地将 CSS 铅笔效果应用于来自这个伟大的 site 的图像。我需要在 html 画布中使用 javascript drawImage 复制效果。

上下文 (CanvasRenderingContext2D) 使用 filter 属性绘制图像,但我无法设置上下文背景大小、背景图像、背景混合模式、背景位置。 我需要将最终过滤后的图像保存在数据库中。图像处理必须在浏览器上,而不是在服务器端。

任何工作的 sn-p 都非常有用。

谢谢!

//  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"  >
<img class="pencil-effect"  >
<canvas id="canvas" style="background: red"></canvas>

【问题讨论】:

“但我无法设置上下文背景大小、背景图像、背景混合模式、背景位置” - 当然你不能,因为您不再在这里处理背景图像。您需要自己计算这些属性的大部分功能。 您也许可以使用 html2canvas 之类的库将带有背景图像的 div 渲染到画布上。 【参考方案1】:

您可以通过 2D 上下文 API 逐步复制您的 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";
&lt;canvas id="canvas" style="background: red"&gt;&lt;/canvas&gt;

第二步是将此图像与其自身的副本混合,并在另一个方向略有偏移。difference 混合模式也可通过其globalCompositeOperation 属性在 2d 上下文 API 中使用:

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";
&lt;canvas id="canvas" style="background: red"&gt;&lt;/canvas&gt;

最后一步是在此混合图像上应用 CSS 过滤器 brightness(2) invert(1) grayscale(1)。 再一次,2D 上下文 API 可以通过它的 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";
&lt;canvas id="canvas" style="background: red"&gt;&lt;/canvas&gt;

【讨论】:

感谢@Kaiido 的工作 sn-p 和 cmets,它真的帮助了我!虽然图片不完全一样,但我会玩滤镜选项以获得想要的效果。 @CristianGhinea 我的回答中有错字/脑放屁……抱歉。在第二遍中,我们应该使用 而不是 ,以避免绘制已经偏移的图像...

以上是关于如何使用 JavaScript 在 html 画布内绘制图像以获取特殊过滤器的主要内容,如果未能解决你的问题,请参考以下文章

如何在 JavaScript 中为画布指定颜色空间?

如何使用 JavaScript 调整画布大小?

如何使用javascript HTML5画布通过N个点绘制平滑曲线?

如何仅使用 HTML5 功能使画布移动?

如何使用javascript修复HTML画布对象中的扭曲/扭曲和剪切图像?

无法理解此 JavaScript 代码来绘制 HTML 画布? [关闭]