fabricjs placement of images on canvas inconsistent - javascript

I have a series of 4 images which I'm attempting to place at specified coordinates in fabricjs, and I'm getting them placed inconsistently sometimes when the page loads. Refreshing once or twice will usually give the correct layout. Trying to prevent this from happening.
Anybody know how to solve this? Here's my code:
var objects = [
{ type: "image", filename : "tv.png" , x: 688, y: 184, angle: 0, zIndex: 10 },
{ type: "image", filename : "polaroid.jpg" , x: 347, y: 515 },
{ type: "image", filename : "polaroid.jpg" , x: 138, y: 643 },
{ type: "image", filename : "polaroid.jpg" , x: 429, y: 803 },
{ type: "text", text: "Your favorite band", x: 1168, y: 1163, angle: -17, canvasRef: null,zIndex: 50 }
];
for (var i=0; i<objects.length;i++){
var objRef = objects[i];
switch(objRef.type){
case 'image':
var url = "img/" + objRef.filename;
fabric.Image.fromURL(url, function(img) {
var objRef = objects[curObject];
img.set({
left: objRef.x,
top: objRef.y,
angle: objRef.angle,
hasBorders: false,
hasControls: false,
hasRotatingPoint: false,
lockMovementX: true,
lockMovementY: true
});
canvas.add(img).renderAll();
img.moveTo(objRef.zIndex);
curObject++;
//canvas.setActiveObject(img);
});
break;
case 'text':
var text = objRef.text;
var fabricText = new fabric.Text(text, {
left: objRef.x,
top: objRef.y,
angle: objRef.angle,
hasBorders: false,
hasControls: false,
hasRotatingPoint: false,
lockMovementX: true,
lockMovementY: true
});
objRef.canvasRef = fabricText;
addTextListeners(fabricText);
canvas.add(fabricText);
break;
}
}
I should mention also that none of this code happens until after window.ready, and after all the images that fabric is attempting to load to canvas have been preloaded using the imagesloaded plugin.
I should also mention that I've tried delaying the loading of each successive image using setTimeout between each load (instead of a loop), and saving the canvas.renderAll() until after the loop, with no success. Even tried running a loop to re-position the items after they were placed on screen. Below are images of the issue - correct, and incorrect, respectively.

It looks like you have a race condition from the images loading in different orders.
The fabric.Image.fromURL() method is using a callback function, which fires after the image has loaded, so you're not guaranteed to have that inner function fire in the same order you called it. But you're using curObject to increment upwards each time the function is called, which assumes they're called in order. And the objRef can get re-assigned to a new object by the time the image loads, which would be why the x/y/rotate values are off (using the values from the other objects in the array).
So, instead of using img.set() after the image had loaded, use the third parameter of the Image.fromURL() method to pass in your attributes:
for (var i=0; i<objects.length;i++){
var objRef = objects[i];
switch(objRef.type){
case 'image':
var url = "img/" + objRef.filename;
fabric.Image.fromURL(url, function(img) {
canvas.add(img).renderAll();
}, {
left: objRef.x,
top: objRef.y,
angle: objRef.angle,
hasBorders: false,
hasControls: false,
hasRotatingPoint: false,
lockMovementX: true,
lockMovementY: true
});
break;
case 'text':
// ...
break;
}
}

So the key to the answer is async call back. Just check when the Alert message called out.
fabric.Image.fromURL('hulk.png', function (img) {
alert('came1');
canvas.add(img).renderAll();
}, {
id: 'hulkid',
num: 1,
left: 10,
top: 10,
angle: 0
});
fabric.Image.fromURL('hulk.png', function (img) {
alert('came2');
canvas.add(img).renderAll();
}, {
id: 'hulkid',
num: 2,
left: 25,
top: 25,
angle: 0
});
alert('came Last');

I found out the way with setTimeout function.
I tested with MidnightLightning's sample
But still has some problem in image z-index in canvas(all settings are good). I think sometimes fabric.Image.fromURL callback function is waiting the image loading, so there would some mistakes in queue of drawing image.
So I added some timeout to function, like that.
var count_layer=0;
var image_src_list_array=['1.jpg', '2.jpg'];
function paint_layer(){
console.log(image_src_list_array[count_layer]);
if(image_src_list_array[count_layer]!=''){
fabric.Image.fromURL(
image_src_list_array[count_layer],
function(img){
console.log('rendering');
fabric_canvas.add(img).renderAll();
}, {
id: count_layer+1,
left: ...,
top: ...,
angle: ...,}
);
count_layer++;
setTimeout(paint_layer,3000);
}
}
Then the console log result is:
1.jpg
rendering
2.jpg
rendering
Before it outputs
1.jpg
2.jpg
rendering
rendering

Related

Stop animation at specific frame?

I'm making a game with Phaser, and I need to have an animation for the health bar going down dynamically stop on a frame, and I couldn't find any clear documentation on the stopOnFrame() method, or the this.anims.getFrame(index) method, both of which woulden't work. I believe that stopOnFrame accepts a frame object, not a frame number but I coulden't figure out how to get that specific frame, as the getFrame() method returned undefined. If there's something I'm missing, my ideal solution looks something like this:
this.hpBar.play({key: 'damageAnimation', startFrame: this.hp})
this.hpBar.stopOnFrame(this.hpBar.getFrame(this.hp - amountOfDamage))
Thanks for any suggestions, cheers!
PS: I know there's some more nuance to how I would use the animations in forwards and reverse to properly create this effect, the example is purely for demonstration.
Two points, you would have to pass a Phaser.Textures.Frame to the stopOnFrame function, and as far as I know the function getFrame doesn't exist (atleast on the phaser sprite GameObject, as shown in your code).
You can use the function getFrameAt on the Animation object, the index has to be here only a valid integer, in the range from 0 - to max frame of the animation.
(btw.: here is the link to the documenation of the stopOnFrame function)
Info: the frame index-count, is based on the frames of the specific animation, not the frame in the spritesheet. (In this example the sprite frame index of the kick is 12, but in the kick animation the index for that specific frame is 2)
Here a Demo:
document.body.style = 'margin:0;';
var config = {
type: Phaser.AUTO,
width: 536,
height: 183,
scene: {
preload,
create
}
};
function preload(){
this.load.spritesheet('brawler',
'http://labs.phaser.io/assets/animations/brawler48x48.png',
{ frameWidth: 48, frameHeight: 48 }
);
this.load.image('brawler-sheet',
'http://labs.phaser.io/assets/animations/brawler48x48.png');
}
function create () {
this.add.text(10,10, 'Click, to Stop on kick (third frame)')
.setScale(1.5)
.setOrigin(0)
.setStyle({fontStyle: 'bold', fontFamily: 'Arial'});
this.anims.create({
key: 'kick',
frames: this.anims.generateFrameNumbers('brawler',
{ frames: [ 10, 11, 12, 13, 10 ] }),
frameRate: 8,
repeat: -1,
repeatDelay: 0
});
this.add.image(140, 20, 'brawler-sheet')
.setOrigin(0)
const cody = this.add.sprite(100, 90);
cody.setScale(3);
cody.play('kick');
this.input.on('pointerdown', () => {
cody.stopOnFrame(cody.anims.currentAnim.getFrameAt(2))
});
}
new Phaser.Game(config);
<script src="//cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
But for a simple healt-bar, I would use a phaser tween, not an animation, like this you can set the value more precise, you can easily update the max-health and "animation" is easy.
Here a demo how I would do it (two visula alternatives):
(I would opt for the second version, if you have some nice heart images or so, I could find a better image for the demo)
document.body.style = 'margin:0;';
var config = {
type: Phaser.AUTO,
width: 536,
height: 183,
scene: {
create,
preload
},
banner: false
};
function preload ()
{
this.load.image('mushroom',
'https://labs.phaser.io/assets/sprites/mushroom16x16.png');
}
let healthMax = 32*5
function create () {
this.add.text(10,10, 'Click, to animate healthbar')
.setScale(1.5)
.setOrigin(0)
.setStyle({fontStyle: 'bold', fontFamily: 'Arial'});
this.healthbarBG = this.add.rectangle(10, 50, 32*5, 10, 0xffffff)
.setOrigin(0);
this.healthbar = this.add.rectangle(12, 52, healthMax - 4, 10 - 4, 0xff0000)
.setOrigin(0);
this.add.text(10, 75, 'Or abit more flashy')
.setOrigin(0)
.setStyle({fontStyle: 'bold', fontFamily: 'Arial'});
let tilespriteBG = this.add.tileSprite(10, 100, 16*5, 16, 'mushroom')
.setOrigin(0)
.setScale(2);
tilespriteBG.setTint(0x2a2a2a)
let tilesprite = this.add.tileSprite(10, 100, 16*5, 16, 'mushroom')
.setOrigin(0)
.setScale(2);
this.input.on('pointerdown', () => {
// remove 50% health
let newHealthWidth = healthMax * .5;
this.tweens.add({
targets: this.healthbar,
width: newHealthWidth,
duration: 750,
ease: 'Power2',
/* this two following properties must be remove, just for demo
yoyo: true,
repeat:-1*/
});
this.tweens.add({
targets: tilesprite,
width: { to: 16 * 5 * .5, from: 16 * 5},
duration: 750,
/* this two following properties must be remove, just for demo
yoyo:true,
repeat:-1*/
});
})
}
new Phaser.Game(config);
<script src="//cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
modifying the ease and/or the duration property (and others), you can make the animation suitable (and spicy) for the most usecases.

cannot use multiple doc.html() for the same document

To my understanding after going through GitHub, documentation and stack overflow:
doc.html() is supposed to be able to take in a string with html formatting (AKA rich text)
There has been work done to ensure that doc.html() is able to produce multiple html snippets within the same pdf document
To produce more than one html in the same document, one is supposed to utilize the callback function and nest any further edits to the pdf.
doc.html() does not behave in the same sense as the other functions (text, rect, etc.) where you can add as many as you want.
var pageWidth = 1000,
lineHeight = 1,
margin = 20,
maxLineWidth = pageWidth - margin * 2,
fontSize = 11,
ptsPerMM = 72 / 25.6,
oneLineHeight = (fontSize * lineHeight) / ptsPerMM;
var doc = new jspdf.jsPDF({
orientation: 'p',
unit: 'pt',
format: 'letter',
lineHeight: lineHeight
});
var field1 = "<b>this is field 1 </b>";
var field2 = "<b>this is field 2 </b>";
//add first html
doc.html("<body>" + field1 + "</body>", {
callback: function (doc) {
//do nothing
},
width: maxLineWidth ,
windowWidth: maxLineWidth ,
html2canvas: {
backgroundColor: 'lightyellow',
width: maxLineWidth ,
height: 150
},
backgroundColor: 'lightblue',
x: 10,
y: 10,
autoPaging: 'text'
});
//add second html
doc.html("<body>" + field2 + "</body>", {
callback: function (doc) {
//do nothing
},
width: maxLineWidth ,
windowWidth: maxLineWidth ,
html2canvas: {
backgroundColor: 'lightyellow',
width: maxLineWidth ,
height: 150
},
backgroundColor: 'lightblue',
x: 10,
y: 10,
autoPaging: 'text'
});
//export pdf
window.open(doc.output('bloburl'));
The code above does not work. how can I fix this issue where I cannot export multiple html snippets in the same pdf document?
I went over GitHub, documentation and stack overflow and for the first time ever, whenever someone asks a question related to this topic, they don't seem to get an answer.
I would really appreciate it if someone could help me figure this out.
I've tried returning doc within the callback and reusing that but that didn't seem to work
Found it!
just use await and make sure to return doc inside the callback
something like this
var doc = new jspdf.jsPDF({
orientation: 'p',
unit: 'pt',
format: 'letter'
});
var field = "<b>html test </b>";
doc.text(10, 10, "test");
//add first html
await doc.html(field, {
callback: function (doc) {
return doc;
},
width: 210,
windowWidth: 210,
html2canvas: {
backgroundColor: 'lightyellow',
width: 210,
height: 150
},
backgroundColor: 'lightblue',
x: 10,
y: 50,
autoPaging: 'text'
});
window.open(doc.output('bloburl'));

Problem with plotly 3d animation using a swift webView on ipad, points aren't disappearing

Ok, here's my problem, I'm actually reading a json file in my local html file to make an animation.
The animation works pretty well on chrome, mozilla and safari (even if it's lagging a bit on safari but i think it's because my mac is old but nvm).
So here it's what i want, I created an ios app, the app is what i'm using to create the json and i wanted to be able to see the animation from the json in the app.
So i created an WKWebView that allows me to see my web page and here's the problem.
During the animation on my ipad, if i press the plotly animation, points that was here at the moment stay till i reload the web page. Moreover, this is also happening when i enter a number in the field i created that allows me to change the speed animation (a litlle thing that just increase my index by the number entered)
So Here's my webView code (even if i don't think the problem comes from there):
import UIKit
import WebKit
class WebViewVC: UIViewController {
#IBOutlet weak var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
let url = Bundle.main.url(forResource: "test", withExtension: "html")
let myRequest = NSURLRequest(url: url!)
webView.load(myRequest as URLRequest)
}
#IBAction func leaveButtonPressed(){
let menuVC = self.storyboard?.instantiateViewController(withIdentifier: "MenuVC") as! MenuVC
menuVC.modalPresentationStyle = .fullScreen
self.present(menuVC, animated: true, completion: nil)
}
}
and here a part of my plotly js code:
function createDico(i) {
return {
x: [i],
y: [i],
z: [i],
mode: 'markers',
marker: {
size: 12,
line: {
color: 'rgba(217, 217, 217, 0.14)',
width: 0.5
},
opacity: 0.8
},
type: 'scatter3d'
};
}
function setDatas() {
for (const [key] of datas[0]) {
if (key != 'bodyOrientation') {
joints[key] = createDico(0)
}
}
var data = getDataStep(0)
console.log(data)
var layout = {
margin: {
l: 0,
r: 0,
b: 0,
t: 100,
},
scene: {
xaxis: {
range: [xMin - 3, xMax + 3],
},
yaxis: {
range: [yMin - 3, yMax + 3],
},
zaxis: {
range: [zMin - 0.5, zMax + 0.5],
},
aspectratio: {
x: 1,
y: 1,
z: 1
},
width: 1000,
},
autoexpand: false,
title: {
text: "jsonPlot",
xanchor: "center"
},
width: 700,
height: 500,
autosize: false
};
Plotly.newPlot('myDiv', data, layout);
}
And i don't even know where the problem comes from so i'm not able to solve anything :/

why can't I retrieve svg or png by relative pathing pptxjs

I have this code :
const createBrandedPresentation = () => {
let pptx = new pptxgen();
let slide = pptx.addSlide({ sectionTitle: 'ZG test' });
slide.background = { color: '#1c1c21' };
slide.addText('Title', {
y: 1.67,
fontSize: 88,
bold: true,
color: '#eb34a8',
isTextBox: true,
align: 'center',
});
slide.addImage ({
path: "./Logosvg.svg",
w: 1.5,
h: 1.5,
x: 7.5,
y: 2
})
return pptx;
};
but every time I generate a new presentation, the photo is empty. I'm calling this function from a react component, (dev server) and I can't understand why it can't add images even tho thse images are served statically with my app. This question goes double for PNG (which don't work either)

Change object value in javascript from another file

I am using this code
$.fn.lightbox.defaults = {
adminBarHeight:28,
overlayOpacity: 0.8,
borderSize: 10,
imageArray: new Array,
activeImage: null,
inprogress: false, //this is an internal state variable. don't touch.
widthCurrent: 250,
heightCurrent: 250,
xScale: 1,
yScale: 1,
displayTitle: true,
disableNavbarLinks: true,
loopImages: true,
imageClickClose: true,
jsonData: null,
jsonDataParser: null,
followScroll: false,
isIE8: false //toyNN:internal value only
};
When I adjust borderSize I get the effect I want however I need to be able to adjust this from another script rather than just changing the value above because I need to leave the code above unchanged as it may be overwritten during updates. How can I do this
I tried
$.fn.lightbox.defaults[borderSize] = 0;
but it had no effect.
The code is from WP Lightbox 2.
It's:
$.fn.lightbox.defaults['borderSize']
$.fn.lightbox.defaults['borderSize'] = 0;
or
$.fn.lightbox.defaults.borderSize = 0;
And ensure that this script is loaded after

Categories

Resources