I have this code:
var rect2 = new drawRect({
h: 100,
w: 100,
x: 280,
y: 20,
colour: '8CB5CF',
name: 'Office 2'
}
Is it possible for read the properties that I am passing to drawRect?
My first thought was:
alert(rect2.h)
But that results in undefined, didn't expect it to work really but I don't know how else to approach this.
I am fairly new to javascript. Thanks.
EDIT: Sorry here is drawRect:
function drawRect(rectOptions){
var ctx = document.getElementById('canvas').getContext('2d');
ctx.fillStyle = rectOptions.colour;
ctx.fillRect(rectOptions.x,rectOptions.y,rectOptions.h,rectOptions.w);
ctx.font = '12px sans-serif';
ctx.fillText(rectOptions.name, rectOptions.w + rectOptions.x, rectOptions.h + rectOptions.y);
}
here is the full code: Full code
Inside of the drawRect function you could add a line to add an option property to objects constructed by drawRect.
function drawRect(rectOptions) {
this.options = rectOptions;
//...
}
Then you could do
var rect2 = new drawRect({ /* ... */ });
alert(rect2.options.h);
Alternatively, you could have drawRect return the options passed to it.
function drawRect(rectOptions) {
//...
return rectOptions;
}
Then you could do, as you originally tried,
alert(rect2.h);
var rect2 = new drawRect({
h: 100,
w: 100,
x: 280,
y: 20,
colour: '8CB5CF',
name: 'Office 2'
})
function drawRect(rectOptions){
//your code
return rectOptions;//you have to return the value
}
alert(rect2.h);//alerts 100
link to jsfiddle
Related
I'm trying to center a circle in a given rectangle. One of the approaches to do the job could be graphics working with Phaser.Display.Align.In.Center and I'm having trouble doing that. Here is the code.
class BootScene extends Phaser.Scene {
constructor() {
super({
key: 'BootScene'
});
}
create() {
let btn = this.add.rectangle(800, 600, 200, 150, '#000').setOrigin(1);
let txt = this.add.text(0, 0, 'click');
Phaser.Display.Align.In.Center(txt, btn, );
let graphics = this.add.graphics({
x: 750,
y: 550
});
var shape = new Phaser.Geom.Circle(0, 0, 24);
graphics.fillStyle(0xff0000, 1);
graphics.fillCircleShape(shape);
//Phaser.Display.Align.In.Center(graphics, btn);
}
}
var config = {
width: 800,
height: 600,
backgroundColor: '#555',
scene: [BootScene]
}
var game = new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
There are 3 visual parts in the code above: the btn rectangle, the txt text and the graphics.
Phaser.Display.Align.In.Center works well with btn and txt.
graphics works well with btn using
this.add.graphics({
x: 750,
y: 550
});
However, if I uncomment the following line below out
Phaser.Display.Align.In.Center(graphics, btn);
the graphics is gone, not matter I set the config as { x: 750, y: 550 } or { x: 0, y: 0 }.
What am I missing?
I know I could use something like
let circle2 = this.add.circle(0, 0, 32, 0xf00000);
Phaser.Display.Align.In.Center(btn_circle, circle2);
I'd just like to know why it doesn't work when I combine graphics and Phaser.Display.Align.In.Center together.
Update: it seems to be a bug of phaser (or a mistake in the documentation), since after calling Phaser.Display.Align.In.Center on the graphics object, the x and y properties are set to NaN, and acording the documenation Align should accept a GameObject, which the Graphics object is.
Here is a possible solution:
You could simply generate a texture out of the graphics object, with the function generateTexture on the graphics object
Align the newly created image with the btn
I just added a setDepth call to the txt object, so it would be visible
class BootScene extends Phaser.Scene {
constructor() {
super({
key: 'BootScene'
});
}
create() {
let btn = this.add.rectangle(300, 175, 200, 150, '#000').setOrigin(1);
let txt = this.add.text(0, 0, 'click');
Phaser.Display.Align.In.Center(txt, btn );
let graphics = this.make.graphics({
x: 24,
y: 24,
});
var shape = new Phaser.Geom.Circle(24, 24, 24);
graphics.fillStyle(0xff0000, 1);
graphics.fillCircleShape(shape);
graphics.generateTexture('gCircle', 48, 48);
let image = this.add.image(200, 200, 'gCircle');
Phaser.Display.Align.In.Center(image, btn);
// only to make the Button visible
txt.setDepth(2);
}
}
var config = {
width: 400,
height: 200,
backgroundColor: '#555',
scene: [BootScene]
}
var game = new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
I've been getting familiar w/ FabricJS on a project.
I have a custom class that extends fabric.Polyline. This custom class has many custom methods. My problem is : When I serialize the whole canvas and later on load it again with loadFromJSON(), the custom methods are not there anymore...
I noticed that the object before/after serialization are not exactly the same. Indeed, we can see that after serialization the method myCustomClassMethod() is not in the object's __proto__ anymore :
I know that loadFromJSON() has a reviver() method for re-adding event listeners, but how can I re-add methods? The tutorials say that I need to add a fromObject() method to save/restore objects, I tried playing with that but no luck.
It might also be that my de-serialized object is a "regular" object, and not a fabricjs object instance. But I haven't seen anywhere how to make it a fabric object again.
MRE down below. As you can see, method works if called before canvas reloading, but not if called afterwards.
// create a wrapper around native canvas element (with id="treeCanvas")
var canvas = new fabric.Canvas('treeCanvas', {
allowTouchScrolling: true,
});
// un-comment log below to inspect objects
canvas.on('mouse:up', function (e) {
if (e.target != null) {
//console.log(e.target);
}
});
// My TreeNode custom class
var TreeNode = fabric.util.createClass(fabric.Polyline, {
initialize: function (X, Y, armsArray, options) {
options || (options = {});
this.callSuper('initialize', options);
this.X = X;
this.Y = Y;
this.armsArray = armsArray;
this.set({ width: 160, height: 50, originX: 'center', originY: 'center' });
this.set({ left: this.X, top: this.Y, fill: 'rgba(255, 255, 255, 0)', stroke: 'black', selectable: true });
this.setCoords();
this.set({ pathOffset: { x: 0, y: 25 } });
for (point of this.armsArray) {
this.points.push({ x: point[0], y: point[1] });
this.points.push({ x: 0, y: 0 });
}
},
_render: function (ctx) {
this.callSuper('_render', ctx);
},
// this is the method that disappears after serializing
myCustomClassMethod: function () {
this.set({ stroke: 'green' });
canvas.renderAll();
}
});
// Adding an instance of my custom class to canvas
var node1 = new TreeNode(100, 100, [[-80, 50], [80, 50]], []);
canvas.add(node1);
canvas.renderAll();
// node1.myCustomClassMethod(); // method works before serializing + deserializing
var stringifiedCanvas = JSON.stringify(canvas);
canvas.loadFromJSON(stringifiedCanvas, canvas.renderAll.bind(canvas));
node1.myCustomClassMethod(); // method DOES NOT WORK anymore after deserializing
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.3.1/fabric.min.js"></script>
<div class="container">
<canvas id="treeCanvas" width="500" height="300"></canvas>
</div>
<script src="canvas.js"></script>
I am very far from being a fabricjs expert and I do things somewhat differently but you were missing a few important parts (marked with "** CHANGE" in the code below)
Hope this is of some help!
// create a wrapper around native canvas element (with id="treeCanvas")
var canvas = new fabric.Canvas('treeCanvas', {
allowTouchScrolling: true,
});
// un-comment log below to inspect objects
canvas.on('mouse:up', function(e) {
if (e.target != null) {
//console.log(e.target);
}
});
// ** CHANGE: define Treenode on the fabric instance
fabric.TreeNode = fabric.util.createClass(fabric.Polyline, {
// ** CHANGE: define the type
type: 'treeNode',
initialize: function(X, Y, armsArray, options) {
options || (options = {});
this.callSuper('initialize', options);
this.X = X;
this.Y = Y;
this.armsArray = armsArray;
this.set({
width: 160,
height: 50,
originX: 'center',
originY: 'center'
});
this.set({
left: this.X,
top: this.Y,
fill: null,
stroke: 'black',
selectable: true
});
this.setCoords();
this.set({
pathOffset: {
x: 0,
y: 25
}
});
for (point of this.armsArray) {
this.points.push({
x: point[0],
y: point[1]
});
this.points.push({
x: 0,
y: 0
});
}
},
_render: function(ctx) {
this.callSuper('_render', ctx);
},
// ** CHANGE: export the custom method when serializing
toObject: function() {
return fabric.util.object.extend(this.callSuper('toObject'), {
myCustomClassMethod: this.myCustomClassMethod
});
},
myCustomClassMethod: function(x, y, color) {
// console.log("myCustomClassMethod")
// console.log(this.myCustomClassMethod)
this.set({
left: x,
top: y,
stroke: color,
strokeWidth: 10
});
canvas.renderAll();
}
});
fabric.TreeNode.fromObject = function(object, callback) {
// console.log(object)
};
var node1 = new fabric.TreeNode(100, 100, [
[-80, 50],
[80, 50]
], []);
canvas.add(node1);
canvas.renderAll();
// DESERIALIZE
var stringifiedCanvas = JSON.stringify(canvas);
canvas.loadFromJSON(stringifiedCanvas, canvas.renderAll.bind(canvas));
// ** CHANGE: need to get a reference to the restored object
var restored = canvas.getObjects()[0]
///console.log(restored)
restored.myCustomClassMethod(200, 50, "red");
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.3.1/fabric.min.js"></script>
<div class="container">
<canvas id="treeCanvas" width="500" height="300"></canvas>
</div>
<script src="canvas.js"></script>
I'm trying create new shapes that drop down into the canvas when you press the button for that shape. I'm using a CDN. Only two pages are my index and app.js. My canvas appears along with the buttons and the first ball drops down but whenever I press the button nothing happens. The console says "$" is undefined. Any help is greatly appreciated. Also, on my index page my CDN is in a script tag and comes before my app.js script tag.
var Engine = Matter.Engine,
Render = Matter.Render,
World = Matter.World,
Bodies = Matter.Bodies;
var engine = Engine.create();
var render = Render.create({
element: document.body,
engine: engine,
options: {
width: 800,
height: 400,
wireframes: false
}
});
var topWall = Bodies.rectangle(400, 0, 810, 30, { isStatic: true });
var leftWall = Bodies.rectangle(0, 200, 30, 420, { isStatic: true });
var ball = Bodies.circle(460, 10, 40, 10);
var bottomWall = Bodies.rectangle(400, 400, 810, 30, { isStatic: true });
World.add(engine.world, [topWall, leftWall, ball, bottomWall]);
Engine.run(engine);
Render.run(render);
var addCircle = function () {
return Bodies.circle(Math.random() * 400 + 30, 30, 30);
};
var addSquare = function () {
return Bodies.rectangle(Math.random() * 400 + 30, 30, 60, 60);
};
var addRect = function () {
return Bodies.rectangle(Math.random() * 400 + 30, 30, 100, 60);
};
This is where my issue is. I followed the tutorial as well.
$('.add-circle').on('click', function () {
World.add(engine.world, addCircle());
});
$('.add-square').on('click', function () {
World.add(engine.world, addSquare());
})
$('.add-rect').on('click', function () {
World.add(engine.world, addRect());
})
I am making an application where different rectangles are painted on a canvas and I am trying to do it with Backbone. I have a model called box:
Box = Backbone.Model.extend({
defaults: {
x: 0,
y: 0,
w: 1,
h: 1,
color: "#FF9000",
linewidth: 3,
id: 0,
},
drawBox: function(ctx) {
ctx.fillStyle = "#FF9000";
ctx.globalAlpha = 0.1;
ctx.fillRect(this.get("x"), this.get("y"), this.get("w"), this.get("h")); //transparent box in the back
ctx.globalAlpha = 1;
ctx.strokeStyle = this.get("color");
ctx.lineWidth = this.get("linewidth");
ctx.strokeRect(this.get("x"), this.get("y"), this.get("w"), this.get("h")); //rectangle on top
}
});
And I also have a collection of this Box model:
BoxSet = Backbone.Collection.extend({
model: Box
});
What I have in mind is to have a view where I can put every Box model in the BoxSet collection on a canvas using the drawBox method in the Box model, but so far all the tutorials and examples deal with simple text templates and I cannot figure out how to acomplish this.
Any ideas on how could this be done using Backbone views?
Thanks in advance.
I would follow the separation of models and views offered by Backbone. Keep your models as data repositories :
var Box = Backbone.Model.extend({
defaults: {
x: 0,
y: 0,
w: 1,
h: 1,
color: "#FF9000",
linewidth: 3
// don't define a default id, that leads to strange behaviors
}
});
var BoxSet = Backbone.Collection.extend({
model:Box
});
And define the views to render the different pieces on a canvas:
var BoxView = Backbone.View.extend({
render: function() {
var model = this.model, ctx = this.options.ctx;
ctx.fillStyle = "#FF9000";
ctx.globalAlpha = 0.1;
ctx.fillRect(
model.get("x"), model.get("y"),
model.get("w"), model.get("h")
);
ctx.globalAlpha = 1;
ctx.strokeStyle = model.get("color");
ctx.lineWidth = model.get("linewidth");
ctx.strokeRect(
model.get("x"), model.get("y"),
model.get("w"), model.get("h")
);
}
});
var SetView= Backbone.View.extend({
initialize: function() {
this.listenTo(this.collection, "all", this.render);
},
render: function() {
var canvas = this.el, ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
this.collection.each(function(model) {
var view = new BoxView({ctx: ctx, model: model});
view.render();
})
}
});
And finally instantiate and render:
var c = new BoxSet();
c.add({x: 150, y: 150, w: 100, h: 100});
c.add({x: 10, y: 10, w: 100, h: 100});
var v = new SetView({
el: $("canvas"),
collection : c
});
v.render();
A Fiddle to view those two nice squares http://jsfiddle.net/JB9yg/
Another one where a change to the collection leads to re-rendering http://jsfiddle.net/JB9yg/1/
This example can probably be built upon to provide cleaner manipulations, but that should get you started.
Also you may try to use Backbone.KineticView plugin to add canvas support to Backbone.
It works via KineticJS, so may also use all power of event delegation for canvas nodes.
Example:
var MyView = Backbone.KineticView.extend({
// build Kineticjs object, then return it.
el : function(){
var rect = new Kinetic.Rect({
x : 100,
y : 100,
width : 50,
height : 50,
fill : 'green',
id : 'rect'
});
var circle = new Kinetic.Circle({
x : 200,
y : 100,
radius : 50,
fill : 'red',
name : 'circle'
});
var group = new Kinetic.Group();
group.add(rect).add(circle);
return group;
},
// setup events
events : {
'click #rect' : function(){
console.log("on rectangle clicked");
},
'mouseover .circle' : 'onMouseOverCircle'
},
onMouseOverCircle : function(){
console.log('Mouse is over circle');
},
render : function(){
// this.$el - cached kineticjs object.
this.options.layer.add(this.$el);
layer.draw();
}
});
var stage = new Kinetic.Stage({
container : 'container',
width : 300,
height : 300
});
var layer = new Kinetic.Layer();
stage.add(layer);
view = new MyView({layer:layer});
view.render();
im trying to learn how to use javascript objects, and got some issue here.
var playerImg = new Image();
playerImg.src = 'player_stay.png';
var player = {
posX: 50,
posY: 50,
draw: function(){
ctx.drawImage(playerImg, player.posX, player.posY);
}
}
It is posible to insert playerImg into player object ?
Something like player.img
EDIT:
I can make it this way
var player = {
posX: 50,
posY: 50,
draw: function(){
playerImg = new Image();
playerImg.src = 'player_stay.png';
ctx.drawImage(playerImg, player.posX, player.posY);
}
}
But i think it will create new Image(); all the time when i use player.draw();
So im asking if there is a better way to do this and have all parametrs inside a player object ?
Yes, why not:
var player = {
posX: 50,
posY: 50,
draw: function(){
ctx.drawImage(playerImg, player.posX, player.posY);
},
playerImage: playerImg
}
If I understand correctly you don't want to build the playerImg object outside/before of the player one.
Well this is one way you could do it, but I wouldn't recommend it because it's not so readable:
var player = {
posX: 50,
posY: 50,
draw: function(){
ctx.drawImage(player.img, player.posX, player.posY);
},
img: (function(){
var playerImg = new Image();
playerImg.src = 'player_stay.png';
return playerImg;
})()
}
This construct (function(){})(); is called a self-executing/self-invoked anonymous function, or an Immediately-Invoked Function Expression
BTW: this expression is executed only once, when the player object is constructed, so you don't have to worry about repeatedly creating the Image.
Yes it is possible.
In general, you can have nested objects or lists, like this:
var player = {
posX: 50,
posY: 50,
draw: function(){
ctx.drawImage(playerImg, player.posX, player.posY);
},
playerImage:{
src = 'player_stay.png'
},
playerList:['elem1', 'elem2']
}
to refer to it in your code you just write (example in case of value assignment)
player.playerImage.src='anotherplayer.jpg';
this link can be of some help
http://www.json.org/