How to save image from canvas with CSS filters

杀马特。学长 韩版系。学妹 提交于 2019-12-17 14:25:17

问题


I need to save an image after using CSS filters on the client-side (without using a backend). What I have so far:

  1. Use CSS filters
  2. Convert to canvas
  3. Save with var data = myCanvas.toDataURL("image/png");
  4. Crying. Image was saved without effects.

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;
    }

}

However, this produces an image without any filters.


回答1:


There is a little known property on the context object, conveniently named filter.

This can take a CSS filter as argument and apply it to the bitmap. However, this is not part of the official standard and it only works in Firefox so there is the limitation.. This has since this answer was originally written become a part of the official standard.

You can check for the existence of this property and use CSS filters if it does, or use a fallback to manually apply the filters to the image if not. The only advantage is really performance when available.

CSS and DOM is a separate world from the bitmaps that are used for images and canvas. The bitmaps themselves are not affected by CSS, only the elements which acts as a looking-glass to the bitmap. The only way is to work with at pixel levels (when context's filter property is not available).

How to calculate the various filters can be found in the Filter Effects Module Level 1. Also see SVG Filters and Color Matrix.

Example

This will apply a filter on the context it self. If the filter property does not exist a fallback must be supplied (not shown here). It then extracts the image with applied filter as an image (version to the right). The filter must be set before next draw operation.

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:


CSS filters applied to the canvas will not be applied to the image that is produced. You either need to replicate the filters in canvas or rather re apply the same filters to the generated image.

Try putting the generated image data into the source of an img tag & apply the same filters.




回答3:


Your CSS properties are not actually applied to the canvas data. Think of the CSS as being another layer placed over the canvas element. You can implement your own image filters by using context.getImageData to get an array of raw RGBA values, then do your filter work and then write it back with context.putImageData. However, I think you really just want to save the output of the CSS filters. You may be able to do this using a tool like rasterizeHTML




回答4:


Note, if src of img is not located at same origin calling var data = myCanvas.toDataURL("image/png") , may cause error

Uncaught SecurityError: Failed to execute 'toDataURL' on 'HTMLCanvasElement': tainted canvases may not be exported.

Note also that image at html at Question appear to be type jpg , not png

<img id="image1" src="./img/lusy-portret-ochki-makiyazh.jpg"/>

A possible "workaround" could be to set img src as a data URI of image ; calling

var data = myCanvas.toDataURL("image/jpg")

Though as noted , at Answers above , would not appear to preserve css filter set at img element.


Note, "workaround" ; "save image" here , would be "save html" ; as the "download" would be an objectURL of the DOM html img element.

Note also , img src within saved html file will still be original local or external src of image ; if not converted to data URI before loading.

Approach is to set window.location.href as an objectURL reference to DOM img element outerHTML , which should preserve style attribute set at .css("[vendorPrefix]-filter", filterVal)

Try utilizing URL.createObjectURL , URL.revokeObjectURL ; setting css filter at img , instead of canvas element ; creating Blob of img outerHTML , type:text/html ; create reference to URL.createObjectURL: objURL ; set window.location.href to objURL ; call URL.revokeObjectURL on objectURL reference objURL

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>


来源:https://stackoverflow.com/questions/30408939/how-to-save-image-from-canvas-with-css-filters

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!