Load from JSON with dynamic patterns

2019-05-03 11:29发布

问题:

I am trying to save Fabric.js canvas and reload it using loadFromJson. But I am getting error patternSourceCanvas is not defined. I thought I should make it global so I removed var. But when I fill the some other new shape with new pattern, this new pattern is applied to all the previously drawn shapes which have old patterns on the canvas. Kindly help me with dynamic patterns.

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

    <title>Dynamic patterns | Fabric.js Demos</title>




    <!--[if lt IE 9]>
      <script src="../lib/excanvas.js"></script>
    <![endif]-->

    <!-- <script src="base/prism.js"></script> -->
    <script src="http://fabricjs.com/lib/fabric.js"></script>
  </head>
  <body>





    <div id="bd-wrapper">
      <h2><span>Fabric.js demos</span>Dynamic patterns</h2>



<div>
  <p>
    <label>Repeat pattern?</label>
    <input type="checkbox" id="img-repeat" checked>
  </p>
  <p>
    <label>Pattern image width</label>
    <input type="range" min="50" max="1000" value="100" id="img-width">
  </p>
  <p>
    <label>Pattern left offset</label>
    <input type="range" min="0" max="500" value="0" id="img-offset-x">
  </p>
  <p>
    <label>Pattern top offset</label>
    <input type="range" min="0" max="500" value="0" id="img-offset-y">
  </p>
  <br>
  <p>
    <label>Pattern image angle</label>
    <input type="range" min="-90" max="90" value="0" id="img-angle">
  </p>
  <p>
    <label>Pattern image padding</label>
    <input type="range" min="-50" max="50" value="0" id="img-padding">
  </p>

</div>
<div><button id="toJson">TOJSON</button></div>
<div><button id="fromJson">LoadFromJSON</button></div>
<canvas id="c" width="500" height="500" style="border:1px solid #ccc"></canvas>
<script>






var canvas = new fabric.Canvas('c');
var padding = 0;

fabric.Image.fromURL('http://fabricjs.com/assets/pug.jpg', function(img) {

  img.scaleToWidth(100).set({
    originX: 'left',
    originY: 'top'
  });

  var patternSourceCanvas = new fabric.StaticCanvas();
  patternSourceCanvas.add(img);

  var pattern = new fabric.Pattern({
    source: function() {
      patternSourceCanvas.setDimensions({
        width: img.getWidth() + padding,
        height: img.getHeight() + padding
      });
      return patternSourceCanvas.getElement();
    },
    repeat: 'repeat'
  });

  canvas.add(new fabric.Polygon([
    {x: 185, y: 0},
    {x: 250, y: 100},
    {x: 385, y: 170},
    {x: 0, y: 245} ], {
      left: 220,
      top: 200,
      angle: -30,
      fill: pattern
    }));




  document.getElementById('img-width').onchange = function() {
    img.scaleToWidth(parseInt(this.value, 10));
    canvas.renderAll();
  };
  document.getElementById('img-angle').onchange = function() {
    img.setAngle(this.value);
    canvas.renderAll();
  };
  document.getElementById('img-padding').onchange = function() {
    padding = parseInt(this.value, 10);
    canvas.renderAll();
  };
  document.getElementById('img-offset-x').onchange = function() {
    pattern.offsetX = parseInt(this.value, 10);
    canvas.renderAll();
  };
  document.getElementById('img-offset-y').onchange = function() {
    pattern.offsetY = parseInt(this.value, 10);
    canvas.renderAll();
  };
  document.getElementById('img-repeat').onclick = function() {
    pattern.repeat = this.checked ? 'repeat' : 'no-repeat';
    canvas.renderAll();
  };
});
document.getElementById('toJson').onclick = function () {
    jsonData = JSON.stringify(canvas);
}
document.getElementById('fromJson').onclick = function () {
    canvas.clear();
    canvas.loadFromJSON(jsonData);
    canvas.renderAll();

}

</script>


</body>
</html>

回答1:

This may be the latest answer ever, but I figured I might as well answer it, since this is one of the first hits when searching for this issue on Google, and I have this exact issue too.

The problem is that the source function gets serialized as code, ex:

"fill":{
            "source":"function () {\r\n\t                    patternSourceCanvas.setDimensions({\r\n\t                        width: img.getWidth(),\r\n\t                        height: img.getHeight(),\r\n\t                    });\r\n\t                    return patternSourceCanvas.getElement();\r\n\t                }",
            "repeat":"repeat",
            "offsetX":0,
            "offsetY":0
         },

The best way around this so far is to create your own object that extends the shape that you need, and over-write the toObject() and initialize functions. This will allow you to to both save and load the custom data needed.

It would be something along the lines of this: http://jsfiddle.net/Ly9YY/ (that specific code does not seem to work)



回答2:

Use below code for convert the Image source into the UTFEncode format and store that JSON and reopen the same. Check Demo Link:

//override toObject of fabric.Pattern
var toFixed = fabric.util.toFixed;
fabric.Pattern.prototype.toObject = function(propertiesToInclude) {
  var NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS,
    source, object;
  if (typeof this.source === "function") {
    source = String(this.source);
  } else if (typeof this.source.src === "string") {
    source = this.source.src;
  } else if (typeof this.source === "object" && this.source.toDataURL) {
    source = this.source.toDataURL();
  }
  object = {
    type: "pattern",
    source: source,
    repeat: this.repeat,
    crossOrigin: this.crossOrigin,
    offsetX: toFixed(this.offsetX, NUM_FRACTION_DIGITS),
    offsetY: toFixed(this.offsetY, NUM_FRACTION_DIGITS),
    patternTransform: this.patternTransform ? this.patternTransform.concat() : null
  };
  fabric.util.populateWithProperties(this, object, propertiesToInclude);
  return object;
};



var imageUrl = 'https://upload.wikimedia.org/wikipedia/commons/2/22/Wikimapia_logotype.svg';
var canvas = new fabric.Canvas('canvas');
var rect = new fabric.Rect({
  width: 200,
  height: 200,
  strokeWidth: 2,
  stroke: '#000'
})
canvas.add(rect);

fabric.Image.fromURL(imageUrl, function(img) {
  //alert('t' + img);
  console.log('img', img);
  img.scaleToHeight(200);
  var patternSourceCanvas = new fabric.StaticCanvas();
  patternSourceCanvas.add(img);
  patternSourceCanvas.setDimensions({
    width: img.getWidth(),
    height: img.getHeight()
  });
  patternSourceCanvas.renderAll();
  var pattern = new fabric.Pattern({
    source: patternSourceCanvas.getElement()
  });
  rect.fill = pattern;
  canvas.renderAll();
}, {
  crossOrigin: 'annonymous'
});

$('#loadjson').on('click', function() {
  var json = canvas.toJSON();
  console.log('json', json['objects']);
  canvas.clear();
  setTimeout(function() {
    canvas.loadFromJSON(json, canvas.renderAll.bind(canvas));
  }, 3000)
})
canvas{
  border:2px solid #000;
}
<canvas id="canvas" width="300" height="300"></canvas><br>
<button  id="loadjson">loadfromjson </button>
<!-- <script src='https://rawgit.com/kangax/fabric.js/master/dist/fabric.js'></script> -->

<script src='https://www.multicastr.com/imageeditor/assets/js/fabric.unmin.js'></script>

<script src="https://www.multicastr.com/user/js/jquery.min.js"></script>