I'm asking this question continuing from How can I fix bitmapdata to the camera in Phaser? I tried it and it worked, however I found that the bitmapdata wouldn't update. My current code:
bitmap = game.add.bitmapData(800, 100);
bitmapSprite = game.add.sprite(0, 0, bitmap);
bitmapSprite.fixedToCamera = true;
I've tried:
bitmap = game.add.bitmapData(800, 100);
bitmapSprite = game.add.sprite(0, 0);
bitmapSprite.addChild(bitmap);
bitmapSprite.fixedToCamera = true;
And:
bitmap = Game.add.bitmapData(800, 100);
bitmap.dirty = true;
bitmapSprite = Game.add.sprite(0, 0);
bitmapSprite.fixedToCamera = true;
var group = Game.add.group();
group.add(bitmap);
group.add(bitmapSprite);
This is the error I get for the first:
Uncaught TypeError: this.children[t].updateTransform is not a function
This is the second error:
Uncaught TypeError: e.preUpdate is not a function
I am using Phaser CE v2.8.7.
EDIT:
Found a solution:
Whenever I want to update the bitmapdata I simply use this:
bitmapSprite.loadTexture(bitmapSprite)
You have to make sure the dirty property is set to true:
bitmap.dirty = true
Thanks.
Related
I wanted to find out if there is any way to record all method calls(with arguments) and property access in Javascript.
For example:
1- I want to be informed when a canvas is being created by document.createElement("CANVAS");
2- I need to be informed when a script is trying to access navigator.plugins or window.screen.
Thank you in advance.
You can add hooks to functions, for example like this:
let calls = (function(){
let calls = 0;
let fun = document.createElement;
document.createElement = function(){
calls++;
return fun.apply(document, arguments);
}
return ()=>calls;
})();
let canvas = document.createElement("canvas");
document.body.appendChild(canvas);
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'rgb(200, 0, 0)';
ctx.fillRect(10, 10, 50, 50);
console.log("calls of document.createElement:", calls());
Small notice: don't try in a SO snippet to do a console.log from a hooked document.createElement as console.log itself is hooked by the snipped engine to build elements...
Another approach is to set proxies for object:
Object.defineProperty(window, "navigator", {
value: new Proxy(window.navigator, {
get: function(target, name) {
console.log("navigator property", name, "is read")
return target[name];
}
})
});
console.log("plugins:", window.navigator.plugins);
I want to let a user drag and drop as many images as they wish onto an html5 canvas. From tutorials online I gather its something like:
var canvas = document.getElementById("canvas"),
context = canvas.getContext("2d"),
imgs = arr.map(function callback(currentValue, index, array) {
img = img = document.createElement("img");
}[, thisArg])
mouseDown = false,
brushColor = "rgb(0, 0, 0)",
hasText = true,
clearCanvas = function () {
if (hasText) {
context.clearRect(0, 0, canvas.width, canvas.height);
hasText = false;
}
};
for (var i=0;i<imgs.length;i++){
imgs[i].addEventListener("load",function(){
context.drawImage(img,0,0);
})
}
I know line 4 is completely wrong..., but usually people seem to create their images as variables and then change the source with the dragged image. Since I want as many images as the user wishes to add, that would be an array without any size?
Furthermore, Since this code gets called once, on page load, this array cannot be appended to later (like when the user decides to add another image). So maybe this entire paradigm is incorrect in this case?
Can someone advise me on how to go about this? Thanks!
EDIT:
proposed solution:
imgs_arr = [];
var canvas = document.getElementById("canvas"),
context = canvas.getContext("2d"),
imgs = imgs_arr.map(function callback(src) {
var img = document.createElement("img");
img.src = src;
return img;
});
mouseDown = false,
brushColor = "rgb(0, 0, 0)",
hasText = true,
clearCanvas = function () {
if (hasText) {
context.clearRect(0, 0, canvas.width, canvas.height);
hasText = false;
}
};
// Adding instructions
context.fillText("Drop an image onto the canvas", 240, 200);
context.fillText("Click a spot to set as brush color", 240, 220);
function drawImage(element,index,array){
element.addEventListener("load",function(){
clearCanvas()
context.drawImage(element,0,0)
})
}
imgs_arr.forEach(drawImage);
Currently throws : Cannot read property 'appendChild' of null(anonymous function) because my imgs_arr is blank - no one has added images yet.
Assuming your arr is an array of image URLs, your map should look like this:
imgs = arr.map(function callback(src) {
var img = document.createElement("img");
img.src = src;
return img;
});
I renamed currentValue to src to make sense as to what it is, and I removed the extra parameters, but that was all technically optional.
The important parts are:
Create a new img element and assign it to a variable.
Assign the URL to the src of that `img.
Return the image.
The map() function gathers up all the values returned from the callback and turns those into an array. That'll give you an array of Image objects (which are the JS form of the HTML <img> element).
The rest looks more or less correct (though you really should use ; instead of , for ending your lines to avoid possible weird things).
Also, for adding more after it is initialized, you should just have to push() a new Image onto the array and then redraw it.
So I have this little static class which does some image transformation:
// static class ImageFactory
var ImageFactory = (function () {
var ImageFactory = {};
ImageFactory.flip = function (image) {
return invert(image, false, true);
};
ImageFactory.mirror = function (image) {
return invert(image, true, false);
};
// private
function invert(image, isMirror, isFlip) {
var canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
var context = canvas.getContext("2d");
context.translate(isMirror ? canvas.width : 0, isFlip ? canvas.height : 0);
context.scale(isMirror ? -1 : 1, isFlip ? - 1 : 1);
context.drawImage(image, 0, 0);
return canvas;
}
return ImageFactory;
})();
The problem is: sometimes it produces 'blank' (wholly transparent) images instead of flips and mirrors, both in Chrome and Firefox. I suspect it has something to do with asynchronous operations which sometimes don't get done in time. According to some literature canvas drawing should be treated synchronously by browsers, but this issue tells me the other way.
Anyway, is there a safe way to draw to a secondary canvas and then use that canvas as an image to draw on the main canvas?
This is the code which should ensure no ImageFactory method is called before all input images are ready:
function load() {
for (var i = 0; i < images.length; ++i) {
var image = images[i];
if (!image.complete) {
var timeout = setTimeout(onTimeout, LOADING_TIMEOUT);
return;
}
}
scene = sceneFactory();
loop();
function onTimeout() {
load();
}
}
Where images is just an array containing all images in the DOM. Only after loop() is called I have some calls to the ImageFactory methods.
Im not sure exactly what may cause the problem , but i guess it is caused by the image not being ready when the function gets called.
Try calling the image mirroring function after the image has finished loading.
Use image onload callback to check if image has finished loading and is ready to be used.
You may also try drawing the canvas again after a second or so, just to make sure it gets drawn properly.
(Using Firefox32, and Win7. But in other browsers I need it to work, too.)
I can't find a command to retrieve the content of the pattern-object that I set on the 2D-context.
Is there no direct way to get the value array and the width and height?
And if there is really no direct way, is there a workaround?
I could just use fillRect with the pattern on a hidden canvas and then reading out the canvas. But then, how to get the correct height and width?
Pattern properties
The only method exposed on the CanvasPattern object is to handle transformations:
interface CanvasPattern {
// opaque object
void setTransform(SVGMatrix transform);
};
This means all other properties has to be tracked externally.
Workaround 1 - manually keep track of properties
The workaround is to read the width and height from the image you used for the pattern, as well as mode and optionally transforms.
Just keep a reference to them for later:
var img = ...; // image source
var patternMode = "repeat"; // store repeat mode
var patternWidth = img.naturalWidth; // width and height of image
var patternHeight = img.naturalHeight; // = width and height of pattern
var pattern = ctx.createPattern(img, patternMode); // use to create pattern
Workaround 2 - create a custom object
You can create a custom object which wraps up the pattern creation process and exposes methods that can hold width, height etc.
Example
An object could look like this:
function PatternExt(ctx, image, mode) {
var ptn = ctx.createPattern(image, mode || "repeat");
this.setTransform = ptn.setTransform ? ptn.setTransform.bind(ptn) : null;
this.width = image.naturalWidth;
this.height = image.naturalHeight;
this.image = image;
this.mode = mode;
this.pattern = ptn;
}
Then it's just a matter of creating an instance almost the same way as with createPattern():
var p = new PatternExt(ctx, img, "repeat");
ctx.fillStyle = p.pattern;
To read information do:
var w = p.width;
var h = p.height;
...
Rename/extend as you want/need.
Demo for custom object
// load an image for pattern
var img = new Image();
img.onload = demo;
img.src = "http://i.imgur.com/HF5eJZS.gif";
function demo() {
var ctx = document.querySelector("canvas").getContext("2d"), p;
// create a pattern instance
p = new PatternExt(ctx, img, "repeat");
// use as fill-style
ctx.fillStyle = p.pattern;
ctx.fillRect(0, 0, 300, 150);
// show some properties
ctx.font = "24px sans-serif";
ctx.fillStyle = "#fff";
ctx.fillText([p.width, p.height, p.mode].join(), 10, 30);
}
function PatternExt(ctx, image, mode) {
var ptn = ctx.createPattern(image, mode || "repeat");
this.setTransform = ptn.setTransform ? ptn.setTransform.bind(ptn) : null;
this.width = image.naturalWidth;
this.height = image.naturalHeight;
this.image = image;
this.mode = mode;
this.pattern = ptn;
}
<canvas></canvas>
If your desired pattern is currently used as the fillStyle, then you can fetch it by fetching the fillStyle:
myPattern=context.fillStyle;
Otherwise you can't fetch your pattern object because the context keeps any pattern objects you've created as private properties.
So typically you keep a reference to your pattern until it's not needed anymore.
If you also need the original imageObject used to create your pattern then you typically save a reference to that image also.
// create an imageObject for use in your pattern
var myImageObject=new Image();
myImageObject.onload=start; // call start() when myImageObject is fully loaded
myImageObject.src="";
function start(){
// myImageObject has now been fully loaded so
// create your pattern and keep a reference to it
var myPattern = context.createPattern(myImageObject, 'repeat');
}
... and later when you need the pattern ...
// use your pattern object reference to apply the pattern as a fillStyle
context.fillStyle = myPattern;
... and later if you need the original image object
// get the original image object's size
var imgWidth=myImageObject.width;
var imgHeight=myImageObject.height;
// draw the original image object to the context -- or whatever you need it for
context.drawImage(myImageObject,50,50);
I'm trying to get a simple cairo drawing drawn to a clutter window using the javascript bindings. My problem is, apart from half the functions being named slightly differently, is no matter what I try, the cairo drawing does not show up. I used a example from python, which does work, and ported it to javascript. I'm also using introspection to get the Clutter module instance. I'm also using gjs version 0.7.14. Can anyone tell me what is going wrong.
Below is the example code I'm using.
const cairo = imports.cairo;
const clutter = imports.gi.Clutter;
function on_button_press_event (stage, event) {
clutter.main_quit();
}
function draw(cairo_tex) {
var context = cairo_tex.create();
context.scale(200, 200);
context.setLineWidth(0.1);
var colour2 = new clutter.Color();
colour2.from_string('#dd000088');
clutter.cairo_set_source_color(context, colour2);
context.translate(0.5, 0.5);
context.arc(0, 0, 0.4, 0, Math.PI * 2);
context.stroke();
}
function main () {
clutter.init(0, null);
var stage = new clutter.Stage();
var colour = new clutter.Color();
colour.from_string("#ffccccff");
stage.set_color(colour);
stage.set_size(400, 300);
stage.connect('button-press-event', on_button_press_event);
stage.connect('destroy', clutter.main_quit);
var cairo_tex = new clutter.CairoTexture.new(200, 200);
cairo_tex.set_position((stage.get_width() - 200) / 2,
(stage.get_height() - 200) / 2);
draw(cairo_tex);
var center_x = cairo_tex.get_width() / 2;
var center_z = cairo_tex.get_height() / 2;
cairo_tex.set_rotation(clutter.AlignAxis.Y_AXIS, 45.0, center_x, 0, center_z);
stage.add_actor(cairo_tex);
cairo_tex.show();
stage.show();
clutter.main();
}
main();
I think the reason this is not working has to do with the deletion of the cairo context in javascript. context.destroy does not exist and using delete doesn't either. Infact if I use delete then I get the warning
WARNING: 'applying the 'delete' operator to an unqualified name is deprecated'
which does not help at all. According to what some of the developers involved in gjs have posted about it, assigning to null should have the same effect, due to it being garbage collected. I'm having my doubts as to whether there is anything to collect behind the scenes.
If someone could say if this is true or not, then I would accept this as a answer.
UPDATE:
I have narrowed down the problem area to imports.gi.Clutter. I tried another example, but this time using Gtk instead of Clutter, and the following code actually works
cairo = imports.cairo;
Gtk = imports.gi.Gtk;
Gdk = imports.gi.Gdk;
function draw_arc(drawing_area){
var cr = Gdk.cairo_create(drawing_area.get_window());
cr.scale(2, 2);
cr.operator = cairo.Operator.CLEAR;
cr.paint();
cr.operator = cairo.Operator.OVER;
cr.setSourceRGB(0,255,0);
cr.arc(128, 128, 76.8, 0, 2*Math.PI);
cr.fill();
return false;
}
Gtk.init(0, null);
var w = new Gtk.Window();
w.connect('delete-event', Gtk.main_quit);
var d = new Gtk.DrawingArea();
w.add(d);
w.resize(500,600);
w.decorated = false;
d.connect('draw', draw_arc);
w.show_all();
Gtk.main();
This leads me to believe that the problem is not with the gjs implementation of cairo, but with the gjs introspection methods for Clutter Cairo implementation. I'm thinking that clutter.CairoTexture.new or clutter.CairoTexture.create is not implemented properly. I suspect it is the clutter.CairoTexture.create which is causing the problem.
Using the newer integration between Clutter, and Cairo, specifically the Clutter.Canvas this will draw a circle to the screen:
const Clutter = imports.gi.Clutter;
const Cairo = imports.cairo;
const draw_stuff = function (canvas, cr, width, height) {
cr.save ();
cr.setOperator (Cairo.Operator.CLEAR);
cr.paint ();
cr.restore ();
cr.setOperator (Cairo.Operator.OVER);
cr.scale (width, height);
cr.setLineCap (Cairo.LineCap.ROUND);
cr.setLineWidth (0.1);
cr.translate (0.5, 0.5);
cr.arc (0, 0, 0.4, 0, Math.PI * 2);
cr.stroke ();
return true;
};
const test = function () {
Clutter.init(null);
let stage = new Clutter.Stage();
stage.set_title ("Circle!");
let color = new Clutter.Color({
red : 255,
green : 0,
blue : 0,
alpha : 128 // Just for the heck of it.
});
stage.set_background_color(color);
stage.set_size(300, 300);
let canvas = new Clutter.Canvas ();
canvas.set_size (155, 155);
let dummy = new Clutter.Actor ();
dummy.set_content (canvas);
dummy.set_size(155, 155);
stage.add_child (dummy);
stage.connect ("destroy", Clutter.main_quit);
canvas.connect ("draw", draw_stuff);
canvas.invalidate ();
stage.show_all();
Clutter.main ();
};
test ();