要实现撤消/重做功能,我构造了一个数组“ history
”,该数组填充了基于canvas.on()
事件的最新更改。
console.log转储:
History:
(3) […]
0: Object { target: {…} } //first object added
1: Object { e: mouseup, target: {…}, transform: {…}, … } //first object modified
2: Object { target: {…} } //another object added
length: 3
要返回更改堆栈,我想使用history[step].target
,它包含现阶段(step
)的修改后的对象。
我现在正在寻找一种方法来覆盖fabric对象数组中的对象。
函数canvas.item(0)
将对象赋予当前画布上的位置0,但如何用另一个对象(来自history[].target
数组的对象)覆盖该对象?
注意:我发现的关于撤消/重做的解决方案似乎是基于将整个画布序列化为JSON并将其保存到JSON数组中的。我不想执行此操作,因为似乎总是不进行序列化/反序列化整个画布(其中包含x个对象)只是为了撤消很小的更改,并且历史记录中包含有关更改的内容和对象的许多有用信息,这似乎并不复杂。
注2:当然,我可能只能先canvas.remove(canvas.item(0))
然后再canvas.add(history[x].target)
,但是不幸的是,当有多个对象时,这会弄乱我的对象堆栈,因为canvas.item(1)
变成了{{1} },现在还原的更改将变为canvas.item(0)
(或2、3 ... ect,具体取决于画布上的项目数)。然后,当我根据对象堆栈中的位置显示对象列表时,该对象将重新排列,并可能使用户感到困惑。
答案 0 :(得分:2)
这是一个简单的例子。
给定2在画布上,一个函数将在这2个中间添加一个,而另一个将替换位置0处的那个。
canvas#insertAt确实可以按照@ gabriele-petroli的建议工作
var c = new fabric.Canvas('c');
c.add(new fabric.Rect({ fill: 'blue', width: 100, height: 100 }));
c.add(new fabric.Rect({ fill: 'green', left: 150, width: 100, height: 100 }));
var redRect = new fabric.Rect({ fill: 'red', left: 75, width: 100, height: 100 });
var purpleRect = new fabric.Rect({ fill: 'purple', left: 0,
width: 100, height: 100 });
setTimeout(function() {
c.insertAt(purpleRect, 0, true);
c.insertAt(redRect, 1);
}, 1000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.5.0/fabric.min.js"></script>
<canvas id="c" width="500" height="500" ></canvas>
答案 1 :(得分:1)
据我所知,不可能覆盖对象。
但这可能对您的Note2有帮助:
除了Gabriele Petriolis注释,您还可以在插入后将对象放置在堆栈中:
void handlePurchase(Purchase purchase) {
BillingClient client = BillingClient.newBuilder(NewAdActivity.this)
.enablePendingPurchases()
.setListener(this)
.build();
if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) {
System.out.println("item successfully purchased");
if (!purchase.isAcknowledged()) {
ConsumeParams consumeParams = ConsumeParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken())
.setDeveloperPayload(purchase.getDeveloperPayload())
.build();
ConsumeResponseListener consumeResponseListener = new ConsumeResponseListener() {
@Override
public void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && purchaseToken != null) {
System.out.println("SUCCESSFULLY consumed PURCHASE");
providecontent();
}
else {
System.out.println("FAILED TO consume:”);
}
}
};
client.consumeAsync(consumeParams, consumeResponseListener);
}
}
}