如何使用CSS过滤器从画布保存图像

7

我需要在客户端使用CSS过滤器后保存图片(不使用后端)。 我目前所拥有的:

  1. 使用CSS过滤器
  2. 转换为画布
  3. 使用 var data = myCanvas.toDataURL("image/png"); 保存
  4. 哭泣。 图像没有效果地保存了。

Index.html

<div class="large-7 left">
    <img id="image1" src="./img/lusy-portret-ochki-makiyazh.jpg"/><br>
    <canvas id="myCanvas"></canvas>
</div>

Photo.js

var buttonSave = function() {
    var myCanvas = document.getElementById("myCanvas");
    var img = document.getElementById('image1');
    var ctx = myCanvas.getContext ? myCanvas.getContext('2d') : null; 
    ctx.drawImage(img, 0, 0, myCanvas.width, myCanvas.height);
        var grayValue = localStorage.getItem('grayValue');
        var blurValue = localStorage.getItem('blurValue');
        var brightnessValue = localStorage.getItem('brightnessValue');
        var saturateValue = localStorage.getItem('saturateValue');
        var contrastValue = localStorage.getItem('contrastValue');
        var sepiaValue = localStorage.getItem('sepiaValue');

        filterVal = "grayscale("+ grayValue +"%)" + " " + "blur("+ blurValue +"px)" + " " + "brightness("+brightnessValue+"%)" + " " + "saturate(" + saturateValue +"%)" + " " + "contrast(" + contrastValue + "%)" + " " + "sepia(" + sepiaValue + "%)" ;
        $('#myCanvas')
          .css('filter',filterVal)
          .css('webkitFilter',filterVal)
          .css('mozFilter',filterVal)
          .css('oFilter',filterVal)
          .css('msFilter',filterVal);

    var data = myCanvas.toDataURL("image/png");
    localStorage.setItem("elephant", data);
    if (!window.open(data)) {
        document.location.href = data;
    }

}

然而,这样生成的图像没有任何过滤器。
4个回答

11

在上下文对象中有一个鲜为人知的属性,方便地命名为filter

它可以将CSS过滤器作为参数并将其应用于位图。然而,这不是官方标准的一部分,它只在Firefox浏览器中有效,因此有限制。自从最初编写本答案以来,它已成为官方标准的一部分。

您可以检查此属性的存在并在可用时使用CSS过滤器,否则使用回退方法手动应用过滤器到图像上。唯一的优势就是性能。

CSS和DOM是与用于图像和画布的位图分开的世界。位图本身不受CSS影响,只有充当镜子的元素受到影响。当context的过滤属性不可用时,唯一的方法是按像素级别处理。

如何计算各种滤镜的方法可以在过滤效果模块1级中找到。还可以查看SVG FiltersColor Matrix

示例

这将在上下文本身上应用一个滤镜。如果过滤器属性不存在,则必须提供回退(此处未显示)。然后提取带有应用滤镜的图像作为图像(右侧版本)。必须在下一次绘制操作之前设置过滤器。

var img = new Image();
img.crossOrigin = ""; 
img.onload = draw; img.src = "//i.imgur.com/WblO1jx.jpg";

function  draw() {
  var canvas = document.querySelector("canvas"),
      ctx = canvas.getContext("2d");

  canvas.width = this.width;
  canvas.height = this.height;
  
  // filter
  if (typeof ctx.filter !== "undefined") {
    ctx.filter = "sepia(0.8)";
    ctx.drawImage(this, 0, 0);
  }
  else {
    ctx.drawImage(this, 0, 0);
    // TODO: manually apply filter here.
  }

  document.querySelector("img").src = canvas.toDataURL();
}
canvas, img {width:300px;height:auto}
<canvas></canvas><img>


2
该属性现在已成为标准的一部分,并且在Firefox和Chrome中都得到了支持。 - metarmask
请注意,您必须使用 drawImage 才能使滤镜应用于图像数据。另一方面,如果您使用 putImageData,则是手动设置像素,因此滤镜将不会应用。但是,您可以从图像数据中获取图像位图以便在目标画布上绘制,或者使用另一个画布,在其中放置图像数据 - 然后可以在目标画布上绘制该画布。 - Pandaiolo

3
Canvas上应用的CSS过滤器不会被应用到生成的图像上。您需要在Canvas中复制这些过滤器,或者重新将相同的过滤器应用于生成的图像。尝试将生成的图像数据放入img标签的源中,并应用相同的过滤器。

什么意思?请举个例子澄清。 - Bikram Kumar

1
你的CSS属性实际上并没有应用到画布数据上。可以将CSS看作是覆盖在画布元素上的另一层。你可以使用context.getImageData获取原始RGBA值数组来实现自己的图像滤镜,然后进行滤镜处理并使用context.putImageData写回。不过,我认为你只想保存CSS滤镜的输出结果。你可以使用类似于rasterizeHTML的工具来实现这一点。

1
注意,如果imgsrc不在同一源中,则调用var data = myCanvas.toDataURL("image/png")可能会导致error
Uncaught SecurityError: Failed to execute 'toDataURL' on 'HTMLCanvasElement': tainted canvases may not be exported.

请注意问题中的图像似乎是jpg类型,而不是png
<img id="image1" src="./img/lusy-portret-ochki-makiyazh.jpg"/>

一个可能的“解决方法”是将 imgsrc 设置为图像的 data URI;调用
var data = myCanvas.toDataURL("image/jpg")

尽管如上所述,在上面的答案中,似乎不能保留在元素上设置的css过滤器。
注意,“workaround”;这里的“save image”应该是“save html”,因为“下载”将是DOM html img元素的objectURL。同时请注意,保存的html文件中的img src仍将是原始的本地或外部src图像;如果在加载之前没有转换为data URI。方法是将window.location.href设置为DOM img元素的outerHTMLobjectURL引用,这应该保留在.css("[vendorPrefix]-filter", filterVal)中设置的style属性。
尝试使用URL.createObjectURLURL.revokeObjectURL;在img元素上设置css filter,而不是canvas元素;将imgouterHTML创建为Blobtype:text/html;创建对URL.createObjectURL的引用:objURL;将window.location.href设置为objURL;在objectURL引用objURL上调用URL.revokeObjectURL

var buttonSave = function() {
  var img = document.getElementById("image1");
  // filters
  var grayValue = "0.2";
  var blurValue = "1px";
  var brightnessValue = "150%";
  var saturateValue = "0.2";
  var contrastValue = "0.2";
  var sepiaValue = "0.2";
  // `filterVal`
  var filterVal = "grayscale(" + grayValue + ") "
                  + "blur(" + blurValue + ") "
                  + "brightness(" + brightnessValue + ") "
                  + "saturate(" + saturateValue + ") "
                  + "contrast(" + contrastValue + ") "
                  + "sepia(" + sepiaValue + ")";
  // set `img` `filter` to `filterVal`
  $(img)
  .css({
    "webkit-filter": filterVal,
    "moz-filter": filterVal,
    "ms-filter": filterVal,
    "o-filter": filterVal
  });
  // create `blob` of `img` `outerHTML` ,
  // `type`:`text/html`
  var blob = new Blob([img.outerHTML], {
    "type": "text/html"
  });
  // create `objectURL` of `blob`
  var objURL = window.URL.createObjectURL(blob);
  console.log(objURL);
  // download `filtered` `img` as `html`
  var download = $("<a />", {
    "download": "image-" + $.now(),
    "href": objURL,
    // notify file is type `html` , not image
    "title":"click to download image as `html` file"
  }).appendTo("body");
  $(img).appendTo("a");
    
  $("a").on("click", function() {
    // set `location.href` to `objURL`
    window.location.href = objURL;
    $(window).one("focus", function() { 
      // revoke `objURL` when `window` regains `focus`
      // after "Save as" dialog
      window.URL.revokeObjectURL(objURL);
    });
  });
  
}

window.onload = buttonSave;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<div class="large-7 left">
  <img id="image1" src="http://lorempixel.com/200/200/cats" />
</div>


谢谢您详细的回答。是的,在.html文件中有带有过滤器的图片,但是在计算机上保存时,该图片不包含过滤器。 - John Smit
@ЕвгенийУсов 尝试将初始图像的src设置为数据URI,而不是URL?另请参阅https://dev59.com/horda4cB1Zd3GeqPNYav,https://dev59.com/5Irda4cB1Zd3GeqPLFip。 - guest271314

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接