使用 fabric js 的 stickman 示例在从 json 加载后不会保留线坐标逻辑
stickman example using fabric js does not persist line coordinate logic after load from json
我正在尝试使用 JSON.stringify 保存 fabricjs stickman 示例并使用 canvas.loadFromJSON 加载它,我什至使用以下方式
将 circle line1、line2、line3 和 line4 的属性显式添加到 JSON
var jsonSave = JSON.stringify(canvas.toJSON(['line1', 'line2', 'line3', 'line4']));
虽然我可以看到保存的 JSON 包含上述属性,但是当我从 JSON 加载并尝试移动圆圈时,我得到一个错误 Uncaught TypeError: p.line1.set 不是函数.
我不明白为什么在保存之前 set 是一个函数但是在保存并从 JSON 加载之后它不是?
还有没有其他方法可以让火柴人在不使用 set 方法 的情况下工作,这样在我们保存它并从 JSON 加载它后它就不会损坏.
我确实查找过类似的问题,这个 link 是我能找到的最相似的问题,但此答案中提供的解决方案似乎也没有解决保存和加载的问题。
我还附上了下面的 fiddle link。重现场景
- 尝试在 fiddle 加载后移动任何圆圈。
- 点击保存按钮。 (您可以转到开发人员工具并在资源选项卡下查看存储在会话存储中的 JSON。)
- 点击加载按钮。
- 尝试移动任何圆圈并查看开发人员工具中的控制台选项卡。
http://jsfiddle.net/hkundnani/h0sf3x5h/5/
任何帮助将不胜感激..谢谢..
当您的代码创建圆形对象时,您的代码会向圆形对象添加额外的属性(例如 line1、line2、line3、line4)。这些额外的属性是对结构 canvas 上现有线对象的引用。您遇到的问题是 JSON 格式没有对通过引用序列化对象的内置支持。对象总是按值序列化。因此,对同一对象的多个引用将作为具有相同值的多个对象序列化到 JSON 字符串。当 JSON 字符串被反序列化时,您将拥有多个具有相同值的对象,而不是对同一对象的多个引用。为了进一步复杂化您的问题,FabricJS 的 loadFromJSON() 方法将顶级对象转换为 FabricJS 对象,但将内部 属性 对象(例如 circle.line1)保留为普通对象。这些普通对象将没有 FabricJS 方法(例如没有设置方法)。
如果您对 "JSON object references" 进行 Google 搜索,您将找到讨论以 JSON 格式保留对象引用问题的可能变通方法的文章。
在您的情况下,一个可能的解决方案可能是将自定义 属性(例如“$id”)添加到自定义 属性 包含线条对象的唯一标识符的线条对象.将此自定义 属性 保存到 JSON 字符串。加载 JSON 字符串后,您可以扫描 FabricJS canvas 以查找线对象并创建从标识符到线对象的映射。然后,您可以扫描 FabricJS canvas 以查找圆形对象,并将它们的线 属性 值替换为地图中的线对象引用。这不是一个简单的解决方案,但应该可行。
fabricjs 的可能解决方法:
http://jsfiddle.net/h0sf3x5h/9/
(snippet没有运行,有知道怎么解决的请告诉我)
重点是在保存和加载函数中编写自定义代码,以再次将线条附加到您的圈子。
var canvas = this.__canvas = new fabric.Canvas('c', { selection: false }), saveNow, loadNow;
fabric.Object.prototype.originX = fabric.Object.prototype.originY = 'center';
(function() {
saveNow = (function(){
var jsonSave = JSON.stringify(canvas.toJSON(['linesID', 'selection', 'id']));
sessionStorage.canvase = jsonSave;
});
function _callBack() {
var objs = canvas.getObjects();
var lines = {};
for(var i = 0; i < objs.length; i++) {
if (objs[i].type === 'line') {
lines[objs[i].id] = objs[i];
}
}
for(i = 0; i < objs.length; i++) {
if (objs[i].type === 'circle') {
var circle = objs[i];
if (circle.linesID) {
circle.line1 = lines[circle.linesID[0]];
circle.line2 = lines[circle.linesID[1]];
circle.line3 = lines[circle.linesID[2]];
circle.line4 = lines[circle.linesID[3]];
}
}
}
canvas.renderAll();
}
loadNow = (function(){
var jsonLoad = sessionStorage.canvase;
canvas.loadFromJSON(jsonLoad, _callBack);
});
document.getElementById('save').addEventListener('click', saveNow);
document.getElementById('load').addEventListener('click', loadNow);
function makeCircle(left, top, line1, line2, line3, line4) {
var c = new fabric.Circle({
left: left,
top: top,
strokeWidth: 5,
radius: 12,
fill: '#fff',
stroke: '#666'
});
c.hasControls = c.hasBorders = false;
c.line1 = line1;
c.line2 = line2;
c.line3 = line3;
c.line4 = line4;
c.linesID = [line1 && line1.id, line2 && line2.id, line3 && line3.id, line4 && line4.id];
return c;
}
function makeLine(coords, id) {
return new fabric.Line(coords, {
fill: 'red',
stroke: 'red',
strokeWidth: 5,
selectable: false,
id: id
});
}
var line = makeLine([ 250, 125, 250, 175 ], 'line1'),
line2 = makeLine([ 250, 175, 250, 250 ], 'line2'),
line3 = makeLine([ 250, 250, 300, 350], 'line3'),
line4 = makeLine([ 250, 250, 200, 350], 'line4'),
line5 = makeLine([ 250, 175, 175, 225 ], 'line5'),
line6 = makeLine([ 250, 175, 325, 225 ], 'line6');
canvas.add(line, line2, line3, line4, line5, line6);
canvas.add(
makeCircle(line.get('x1'), line.get('y1'), null, line),
makeCircle(line.get('x2'), line.get('y2'), line, line2, line5, line6),
makeCircle(line2.get('x2'), line2.get('y2'), line2, line3, line4),
makeCircle(line3.get('x2'), line3.get('y2'), line3),
makeCircle(line4.get('x2'), line4.get('y2'), line4),
makeCircle(line5.get('x2'), line5.get('y2'), line5),
makeCircle(line6.get('x2'), line6.get('y2'), line6)
);
canvas.on('object:moving', function(e) {
var p = e.target;
p.line1 && p.line1.set({ 'x2': p.left, 'y2': p.top });
p.line2 && p.line2.set({ 'x1': p.left, 'y1': p.top });
p.line3 && p.line3.set({ 'x1': p.left, 'y1': p.top });
p.line4 && p.line4.set({ 'x1': p.left, 'y1': p.top });
canvas.renderAll();
});
})();
<script src="http://fabricjs.com/lib/fabric.js"></script>
<canvas id="c" width="500" height="500"></canvas>
<button id="save">Save </button>
<button id="load">Load </button>
我正在尝试使用 JSON.stringify 保存 fabricjs stickman 示例并使用 canvas.loadFromJSON 加载它,我什至使用以下方式
将 circle line1、line2、line3 和 line4 的属性显式添加到 JSONvar jsonSave = JSON.stringify(canvas.toJSON(['line1', 'line2', 'line3', 'line4']));
虽然我可以看到保存的 JSON 包含上述属性,但是当我从 JSON 加载并尝试移动圆圈时,我得到一个错误 Uncaught TypeError: p.line1.set 不是函数.
我不明白为什么在保存之前 set 是一个函数但是在保存并从 JSON 加载之后它不是?
还有没有其他方法可以让火柴人在不使用 set 方法 的情况下工作,这样在我们保存它并从 JSON 加载它后它就不会损坏.
我确实查找过类似的问题,这个 link 是我能找到的最相似的问题,但此答案中提供的解决方案似乎也没有解决保存和加载的问题。
我还附上了下面的 fiddle link。重现场景
- 尝试在 fiddle 加载后移动任何圆圈。
- 点击保存按钮。 (您可以转到开发人员工具并在资源选项卡下查看存储在会话存储中的 JSON。)
- 点击加载按钮。
- 尝试移动任何圆圈并查看开发人员工具中的控制台选项卡。
http://jsfiddle.net/hkundnani/h0sf3x5h/5/
任何帮助将不胜感激..谢谢..
当您的代码创建圆形对象时,您的代码会向圆形对象添加额外的属性(例如 line1、line2、line3、line4)。这些额外的属性是对结构 canvas 上现有线对象的引用。您遇到的问题是 JSON 格式没有对通过引用序列化对象的内置支持。对象总是按值序列化。因此,对同一对象的多个引用将作为具有相同值的多个对象序列化到 JSON 字符串。当 JSON 字符串被反序列化时,您将拥有多个具有相同值的对象,而不是对同一对象的多个引用。为了进一步复杂化您的问题,FabricJS 的 loadFromJSON() 方法将顶级对象转换为 FabricJS 对象,但将内部 属性 对象(例如 circle.line1)保留为普通对象。这些普通对象将没有 FabricJS 方法(例如没有设置方法)。
如果您对 "JSON object references" 进行 Google 搜索,您将找到讨论以 JSON 格式保留对象引用问题的可能变通方法的文章。
在您的情况下,一个可能的解决方案可能是将自定义 属性(例如“$id”)添加到自定义 属性 包含线条对象的唯一标识符的线条对象.将此自定义 属性 保存到 JSON 字符串。加载 JSON 字符串后,您可以扫描 FabricJS canvas 以查找线对象并创建从标识符到线对象的映射。然后,您可以扫描 FabricJS canvas 以查找圆形对象,并将它们的线 属性 值替换为地图中的线对象引用。这不是一个简单的解决方案,但应该可行。
fabricjs 的可能解决方法: http://jsfiddle.net/h0sf3x5h/9/ (snippet没有运行,有知道怎么解决的请告诉我)
重点是在保存和加载函数中编写自定义代码,以再次将线条附加到您的圈子。
var canvas = this.__canvas = new fabric.Canvas('c', { selection: false }), saveNow, loadNow;
fabric.Object.prototype.originX = fabric.Object.prototype.originY = 'center';
(function() {
saveNow = (function(){
var jsonSave = JSON.stringify(canvas.toJSON(['linesID', 'selection', 'id']));
sessionStorage.canvase = jsonSave;
});
function _callBack() {
var objs = canvas.getObjects();
var lines = {};
for(var i = 0; i < objs.length; i++) {
if (objs[i].type === 'line') {
lines[objs[i].id] = objs[i];
}
}
for(i = 0; i < objs.length; i++) {
if (objs[i].type === 'circle') {
var circle = objs[i];
if (circle.linesID) {
circle.line1 = lines[circle.linesID[0]];
circle.line2 = lines[circle.linesID[1]];
circle.line3 = lines[circle.linesID[2]];
circle.line4 = lines[circle.linesID[3]];
}
}
}
canvas.renderAll();
}
loadNow = (function(){
var jsonLoad = sessionStorage.canvase;
canvas.loadFromJSON(jsonLoad, _callBack);
});
document.getElementById('save').addEventListener('click', saveNow);
document.getElementById('load').addEventListener('click', loadNow);
function makeCircle(left, top, line1, line2, line3, line4) {
var c = new fabric.Circle({
left: left,
top: top,
strokeWidth: 5,
radius: 12,
fill: '#fff',
stroke: '#666'
});
c.hasControls = c.hasBorders = false;
c.line1 = line1;
c.line2 = line2;
c.line3 = line3;
c.line4 = line4;
c.linesID = [line1 && line1.id, line2 && line2.id, line3 && line3.id, line4 && line4.id];
return c;
}
function makeLine(coords, id) {
return new fabric.Line(coords, {
fill: 'red',
stroke: 'red',
strokeWidth: 5,
selectable: false,
id: id
});
}
var line = makeLine([ 250, 125, 250, 175 ], 'line1'),
line2 = makeLine([ 250, 175, 250, 250 ], 'line2'),
line3 = makeLine([ 250, 250, 300, 350], 'line3'),
line4 = makeLine([ 250, 250, 200, 350], 'line4'),
line5 = makeLine([ 250, 175, 175, 225 ], 'line5'),
line6 = makeLine([ 250, 175, 325, 225 ], 'line6');
canvas.add(line, line2, line3, line4, line5, line6);
canvas.add(
makeCircle(line.get('x1'), line.get('y1'), null, line),
makeCircle(line.get('x2'), line.get('y2'), line, line2, line5, line6),
makeCircle(line2.get('x2'), line2.get('y2'), line2, line3, line4),
makeCircle(line3.get('x2'), line3.get('y2'), line3),
makeCircle(line4.get('x2'), line4.get('y2'), line4),
makeCircle(line5.get('x2'), line5.get('y2'), line5),
makeCircle(line6.get('x2'), line6.get('y2'), line6)
);
canvas.on('object:moving', function(e) {
var p = e.target;
p.line1 && p.line1.set({ 'x2': p.left, 'y2': p.top });
p.line2 && p.line2.set({ 'x1': p.left, 'y1': p.top });
p.line3 && p.line3.set({ 'x1': p.left, 'y1': p.top });
p.line4 && p.line4.set({ 'x1': p.left, 'y1': p.top });
canvas.renderAll();
});
})();
<script src="http://fabricjs.com/lib/fabric.js"></script>
<canvas id="c" width="500" height="500"></canvas>
<button id="save">Save </button>
<button id="load">Load </button>