I'm working on a simple game with Javascript, involving changes to an HTML5 canvas. Have a number (10+) of objects entered in an array:
var boxes = [];
boxes.push({
x: 0,
y:canvas.height - 370,
width: 400,
height: 20});
boxes.push({
x: 300,
y:canvas.height - 100,
width: 150,
height: 90});
The entire array is then run through an update function, which changes the x value depending on the player's position:
for (var i = 0; i < boxes.length; i++) {
if (boxes[1].x > -3000 + canvas.width) {
boxes[i].x -= robot.speed * modifier;
robot.x = canvas.width - 300;
};
};
When the player dies a reset function is run:
var reset = function () {
robot.x = 0;
robot.y = 500;
++deaths;
};
I'm looking for a way to reset all the values of the boxes array to the original values when this function runs, essentially resetting the map, without having to enter each one manually, ie boxes[1].x = 300
https://jsfiddle.net/to31b612/
Simply initialize from a single function:
var boxes;
initBoxes();
function initBoxes() {
boxes = [];
boxes.push({
x: 0,
y:canvas.height - 370,
width: 400,
height: 20});
boxes.push({
x: 300,
y:canvas.height - 100,
width: 150,
height: 90});
}
Then just recall initBoxes() every time you need to initialize it.
You could just initialize boxes inside the reset() function, and make sure to call call reset() before the first run of the game, too.
var deaths = -1;
var boxes = [];
function reset() {
robot.x = 0;
robot.y = 500;
boxes = [{
x: 0,
y:canvas.height - 370,
width: 400,
height: 20
}, {
x: 300,
y:canvas.height - 100,
width: 150,
height: 90
}];
++deaths;
}
It sounds like you want to use the Memento Pattern
just use 2 variables
var initBoxes = [];
initBoxes.push({
x: 0,
y:canvas.height - 370,
width: 400,
height: 20});
initBoxes.push({
x: 300,
y:canvas.height - 100,
width: 150,
height: 90});
var boxes = initBoxes;
Then when ever you need to reset boxes just
boxes = initBoxes;
Related
I'm trying to check for a collision between 2 text objects and using the intersectsWithObject. It's working but it's taking the bouding rect into account. Is it possible to check on pixel level?
Current behaviour:
Wanted behaviour:
const canvas = new fabric.Canvas('canvas');
canvas.setWidth(document.body.clientWidth);
canvas.setHeight(document.body.clientHeight);
const text = new fabric.Textbox('some text', {
width: 300,
fontSize: 70,
top: 120,
left: 100
});
const text2 = new fabric.Textbox('some more text', {
width: 350,
fontSize: 50,
top: 200,
left: 20,
})
if (text.intersectsWithObject(text2, true, true)) {
text.set('fill', 'red');
}
else {
text.set('fill', 'black');
}
canvas.on('after:render', function() {
canvas.contextContainer.strokeStyle = '#555';
canvas.forEachObject(function(obj) {
var bound = obj.getBoundingRect();
canvas.contextContainer.strokeRect(
bound.left + 0.5,
bound.top + 0.5,
bound.width,
bound.height
);
})
});
canvas.add(text);
canvas.add(text2);
https://jsbin.com/menadejato/edit?js,console,output
I try to add several times the same element in my paper.
Then I created it thanks to embed and then I cloned it with deep:true.
My problem is that deep:true change some properties (I don't know which) but I'm now enable to apply a translation to it.
here is my code, that work without deep:true but doesn't with. (but clone a rect and not my combined element) :
var width=400, height=1000;
var graph = new joint.dia.Graph; //define a graph
var paper = new joint.dia.Paper({ //define a paper
el: $('#myholder'),
width: width,
height: height,
gridSize: 1,
model: graph, //include paper in my graph
restrictTranslate: function(element) { //restriction of drag n drop to the vertical line (x never changes)
return {
x: element.model.attributes.position.x, //this represent the original x position of my element
y: 0,
width: 0,
height: height
};
}
});
var rect1 = new joint.shapes.basic.Rect({ //create first rect (with input and output)
position: { x: 100, y: 20 },
size: { width: 90, height: 90 },
attrs: {
rect: { fill: '#2ECC71' }
}
});
var add_block= new joint.shapes.basic.Circle({ //create add button
position: { x: 170, y: 20 },
size: { width: 20, height: 20 },
attrs: {
circle: { fill: '#2ECC71'},
text:{text:'+', "font-size":20}
}
});
rect1.embed(add_block);
graph.addCell([rect1,add_block]);
var clones=rect1.clone({deep:true});
graph.addCells(clones);
var line = V('line', { x1: 250, y1: 10, x2: 250, y2: 500, stroke: '#ddd' });
V(paper.viewport).append(line);
//Create a new block and link between two blocks
//chek if click on + button
//if click on this button, create a new block and link between them
paper.on('cell:pointerclick', function(cellView, evt, x, y) {
var elementBelow = graph.get('cells').find(function(cell) {
if (!cell.get('parent')) return false;//not interested in parent
if (cell instanceof joint.dia.Link) return false; // Not interested in links.
if (cell.getBBox().containsPoint(g.point(x, y))) {
return true;
}
return false;
});
if(elementBelow) //if we start from a block, let's go!
{
rect=rect1.clone({deep:true});
graph.addCell(rect);
rect.translate(200, 0);
}
});
I've looked through the collection of examples, that go with the library, but unfortunatelly, all examples, devoted to composite figures, are based on parsing json file.
reader.unmarshal(canvas, jsonDocument);
However, what I want to see is how to create composite figures from basic ones, something like:
var figure_1 = new draw2d.shape.basic.Rectangle(....);
var figure_1 = new draw2d.shape.basic.Diamond(....);
... then do something, so that figure_1 and figure_2 are now part of
... a composite figure
PS. I think what I need is StrongComposite, but I do not know how to use it. Hope someone can help!
EDIT
This is what I tried:
var baseRectangle = new draw2d.shape.composite.StrongComposite({width: 200, height: 200, x: conf.x, y: conf.y});
var top = new draw2d.shape.basic.Oval({width: 120, height: 40, x: conf.x, y: conf.y});
var bottom = new draw2d.shape.basic.Oval({width: 120, height: 40, x: conf.x, y: conf.y + 60});
baseRectangle.assignFigure(top);
baseRectangle.assignFigure(bottom);
canvas.add(baseRectangle);
But it does not work. I only see a gray box. I also tried this:
var baseRectangle = new draw2d.shape.composite.StrongComposite({width: 200, height: 200, x: conf.x, y: conf.y});
var top = new draw2d.shape.basic.Oval({width: 120, height: 40, x: conf.x, y: conf.y});
var bottom = new draw2d.shape.basic.Oval({width: 120, height: 40, x: conf.x, y: conf.y + 60});
baseRectangle.assignFigure(top);
baseRectangle.assignFigure(bottom);
canvas.add(top);
canvas.add(bottom);
But as a result, I got absolutely independent ovals.
You also have to add baseRectangle to canvas.
var baseRectangle = new draw2d.shape.composite.StrongComposite({
width: 200,
height: 200,
x: conf.x,
y: conf.y
});
canvas.add(baseRectangle);
var top = new draw2d.shape.basic.Oval({
width: 120,
height: 40,
x: conf.x,
y: conf.y
});
var bottom = new draw2d.shape.basic.Oval({
width: 120,
height: 40,
x: conf.x,
y: conf.y + 60
});
baseRectangle.assignFigure(top);
baseRectangle.assignFigure(bottom);
canvas.add(top);
canvas.add(bottom);
In addition to needing to put the child elements on the canvas (sorry, can't comment on existing answer yet): I found I need to wait a frame (nextTick with Vue) before assignFigure can work. Otherwise draw2d throws exception:
TypeError: Cannot read properties of null (reading 'paper')
at Class.createShapeElement (null:23614:34)
at Class.getShapeElement (null:8492:33)
at Class.toFront (null:8351:22)
at Class.toFront
I'm working with custom "buttons" in html5 canvas. Because I've got so many buttons I think it makes more sense to store then in an array. My dilemma is, I'm not quite sure on how to implement custom functions which are 'attached' to a particular button. I've seen this posting, not exactly sure if that is applicable here.
Clearly btn[i].function+"()"; isn't cutting it.
Here's a JSFiddle.
How can I store a custom function within the button array, and successfully call it upon mouseclick?
Code follows:
<!doctype html>
<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body {background-color: black; margin: 8px; }
#canvas {border: 1px solid gray;}
</style>
<script>
$(function() {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var $canvas = $("#canvas");
var canvasOffset = $canvas.offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var scrollX = $canvas.scrollLeft();
var scrollY = $canvas.scrollTop();
var cw = canvas.width;
var ch = canvas.height;
var btn = [{
x: 50,
y: 100,
width: 80,
height: 50,
display: "Hello There",
function: "functionA" // Is this right?
}, {
x: 150,
y: 100,
width: 80,
height: 50,
display: "Why Not?",
function: "functionB" // Is this right?
}, {
x: 250,
y: 100,
width: 80,
height: 50,
display: "Let's Go!",
function: "functionC" // Is this right?
}];
function drawAll() {
ctx.clearRect(0, 0, cw, ch);
for (var i = 0; i < btn.length; i++) {
drawButton(ctx, btn[i].x, btn[i].y, btn[i].width, btn[i].height, btn[i].display);
}
}
drawAll();
// listen for mouse events
$("#canvas").mousedown(function(e) {
handleMouseDown(e);
});
function handleMouseDown(e) {
// tell the browser we'll handle this event
e.preventDefault();
e.stopPropagation();
// save the mouse position
lastX = parseInt(e.clientX - offsetX);
lastY = parseInt(e.clientY - offsetY);
// hit all existing buttons
for (var i = 0; i < btn.length; i++) {
if ((lastX < (btn[i].x + btn[i].width)) &&
(lastX > btn[i].x) &&
(lastY < (btn[i].y + btn[i].height)) &&
(lastY > btn[i].y)) {
// execute button function
btn[i].function+"()"; // obviously this is just wrong.
console.log("Button #" + (i + 1) + " has been clicked!!" );
}
}
}
function drawButton(context, x, y, width, height, text) {
var myGradient = context.createLinearGradient(0, y, 0, y + height);
myGradient.addColorStop(0, '#999999');
myGradient.addColorStop(1, '#222222');
context.fillStyle = myGradient;
context.fillRect(x, y, width, height);
context.fillStyle = 'white';
// textAlign aligns text horizontally relative to placement
context.textAlign = 'center';
// textBaseline aligns text vertically relative to font style
context.textBaseline = 'middle';
context.font = 'bold 15px sans-serif';
context.fillText(text, x + width / 2, y + height / 2);
}
function functionA() {
alert("Yipee Function A!");
}
function functionB() {
alert("Yowza, it's Function B!");
}
function functionC() {
alert("Now showing Function C!");
}
})
</script>
</head>
<body>
<canvas id="canvas" width=400 height=300></canvas>
</body>
</html>
Try to change your buttons to:
{
display: "Hello There",
action: functionA
}
And to invoke:
btn[i].action();
I changed the name function to action because function is a reserved word and cannot be used as an object property name.
You can store references to the functions in your array, just lose the " signs around their names (which currently makes them strings instead of function references), creating the array like this:
var btn = [{
x: 50,
y: 100,
width: 80,
height: 50,
display: "Hello There",
function: functionA
}, {
x: 150,
y: 100,
width: 80,
height: 50,
display: "Why Not?",
function: functionB
}]
Then you can call either by writing btn[i].function().
Don't put the name of the function in the array, put a reference to the function itself:
var btn = [{
x: 50,
y: 100,
width: 80,
height: 50,
display: "Hello There",
'function': functionA
}, {
x: 150,
y: 100,
width: 80,
height: 50,
display: "Why Not?",
'function': functionB
}, {
x: 250,
y: 100,
width: 80,
height: 50,
display: "Let's Go!",
'function': functionC
}];
To call the function, you do:
btn[i]['function']();
I've put function in quotes in the literal, and used array notation to access it, because it's a reserved keyword.
I'm practicing some Javascript. I used a var to create an object (because before that I created a class) and now I'm trying to do the same, but instead of using a variable I want to use an array.
While this is working:
function start (){
var brick = new create_class_brick(10, 400, 10, 400, 0, 2.5, "brick");
window.setInterval(function(){brick.MOVE_BRICK();}, 25);
the MOVE_BRICK function doesn't work here:
function start (){
var i = 0;
vector_bricks[i++] = new create_class_brick(300, 500, 800, 600, 0, 2.5, "brick");
vector_bricks[i++] = new create_class_brick(200, 200, 600, 300, 0, 2.5, "brick");
for ( i = 0; i<vector_bricks.length; i++ ){
vector_bricks[i].create_brick();
vector_bricks[i].MOVE_BRICK();
}
}
Can anyone give me a hand?
Do you forget to call the function move_brick with window.setInterval? So it's not looping.
Simply change where you put the interval.
You get an error if you put it inside the for loop.
Meaning you should have this :
function start (){
var i = 0;
vector_bricks[i++] = new create_class_brick(300, 500, 800, 600, 0, 2.5, "brick");
vector_bricks[i++] = new create_class_brick(200, 200, 600, 300, 0, 2.5, "brick");
setInterval(function(){
for (var i = 0; i<vector_bricks.length; i++ ){
vector_bricks[i].create_brick(); // Not sure what this is for
vector_bricks[i].MOVE_BRICK();
}
}, 25);
}