I have a class, which contains a parameter called values. This is used to save values of points that represent specific shapes on a canvas.
I need to implement a functionality that lets me drag those shapes around, so I need to modify each specific point of the shape, removing from them the amount that was dragged.
So I decided that, as I trigger my mousedown event (which is the method StartMove), i would save the values of my points on a startValues variable, and as I move my mouse around (method move), I would then update the values, using startValues and the distance between the starting point and the current mouse position to determine my new point location.
The problem is, this.startValues is actually getting changed to match this.values every time my cursor moves, and I have no idea why. Is there anything simple I'm missing?
Since I store my values as values, and not coordinates (helps me with panning and zooming on the canvas), I first convert the values to position, then modify the position and then convert it back to a value. I've included the parent class, Grf, so you can see the methods which change values to position and position to values.
Class with the problems
class Test {
constructor(grf){
this.grf = grf; // Another class, which contains important methods
this.values = [];
this.startValues = [];
}
startMove(p0){ // p0 = [x,y]
const {grf} = this;
this.startValues = [...this.values]; //I also tried this.startValues = this.values
this.p0 = p0;
grf.canvas.addEventListener('mousemove',this.move);
grf.canvas.addEventListener('mouseup', this.endMove);
}
move = (evt) => { // arrow function so 'this' is bound to Test class instead of grf.canvas
const {grf, p0, values, startValues} = this;
const coords = grf.canvas.getBoundingClientRect();
const px = evt.clientX - coords.left;
const py = evt.clientY - coords.top;
for (let i = 0, iMax = this.values.length; i < iMax; i++){
values[i][0] = grf.valX(grf.posX(startValues[0]) - (p0[0] - px));
values[i][1] = grf.valY(grf.posY(startValues[1]) - (p0[1] - py));
}
console.log(this.startValues); // It changes to the same as this.values
}
endMove = (evt) => { // arrow function so 'this' is bound to Test class instead of grf.canvas
const {grf} = this;
grf.canvas.removeEventListener('mousemove',this.move);
grf.canvas.removeEventListener('mouseup',this.endMove);
}
}
The other class
class Grf {
constructor(canvas){ // Not the actual constructor, just an example of what the values could look like
this.translateX = 1000;
this.translateY = 1000;
this.scaleY = 10.7;
this.scaleX = 11.2;
this.canvas = canvas;
}
posX (value){
return (value-this.translateX)*this.scaleX;
}
posY (value){
return (this.canvas.height-(100*(value))-this.translateY)*this.scaleY;
};
valX(pos){
return (pos/this.scaleX) + this.translateX
}
valY(pos){
return (-1)*((pos/this.scaleY) + this.translateY - this.canvas.height)/100
}
}
How values are inserted into startValues and values in Test class? You probably insert exactly the same object in both without coping it so both arrays hold the same instances.
Take a look at the example:
const obj = { a : 10 };
const a = [];
a.push(obj);
const b = [...a]; // creates new array, but with same objects
a[0].a = 20;
console.log(b[0]) // gives "{ a : 20 }"
To make it separate you need to make a copy of a object:
a.push({...obj})
i have been try to solve the sudoku with Blacktracking algo, everything is good, canvar is called and i able to see the number but the things is number are not moving i.e the logic is not exectuing
current.i === 0; is where i'm get the error! even i have declared a sperate variable for the num also the problem is not sloved. only if i remove the .num current == 0 than its not showing any error but still the number is not moving
enter image description here
var cell = [];
var stack = [];
var sudoku = [2,3,0,9,4,0,6,7,0,
8,0,0,3,2,5,9,1,4,
9,0,0,7,6,0,3,2,0,
1,0,0,0,0,0,7,9,2,
5,0,3,2,1,0,4,8,6,
4,0,0,6,8,0,5,3,1,
7,0,0,1,0,0,0,0,9,
6,5,9,8,7,2,1,4,3,
3,0,0,0,9,0,0,0,7];
var current;
var number = 1;
function setup(){
createCanvas(450,450);
var a=0;
var b=0;
for(var i=0;i<81;i++){
if(a%9==0 && i!=0){
b = b+50;
a = 0;
}
each[i] = new each(a,b,i,sudoku[i]);
a = a+50;
}
current = cell[0];
}
function draw(){
background(10);
for(var i=0;i<81;i++){
each[i].show();
}
if(current.num === 0){ //the error is typeerror can't read the property of num
if(! sameColumn(current.i,number) && ! sameRow(current.i,number) && ! sameSquare(current.i,number) && number<(10)){
current.num = number;
stack.push(current);
number = 0;
current.each[current.i+1];
}
else {
if(number > 8){
current.num = 0;
current = stack.pop();
number = current.num;
current.num = 0;
}
}
}
else{
current = each[current+1];
number = 0;
}
number++;
}
function each(a,b,i,num){
this.a = a;
this.b = b;
this.i = i;
this.num = num;
this.show = function(){
noFill();
stroke(255);
rect(this.a,this.b,50,50);
textSize(32);
text(this.num,a+12,b+40);
}
}
The error is pretty much straight forward. current = cell[0]; becomes undefined since you defined cell as an empty array and didn't manipulated it after that.
From what I have observed so far, many parts of your code logically does not work, for example,
same Column(current.i,number) && ! sameRow(current.i,number) && ! sameSquare(current.i,number)
will definitely throw you an error is it is executed (it is not since the execution does not reach to that line), unless you have a separate js file that contains these functions.
Another one is
current = cell[current+1];
if the current variable is to store the cell object, it does not make sense to add 1 to it, and vice versa.
Now I believe this is how setup function was meant to look like:
function setup(){
createCanvas(450,450);
var a=0;
var b=0;
for(var i=0;i<81;i++){
if(a%9==0 && i!=0){
b = b+50;
a = 0;
}
cell[i] = new Cell(a,b,i,sudoku[i]); //changed each[i] to cell[i], also renamed the 'each' class
a = a+50;
}
current = cell[0];
}
If possible, please edit in a little more information about what exactly does your code do. Cheers :)
I have a game I'm making in adobe animate HTML canvas. In my code below Is there a way to combine all of the stateItems[].stateplace1 =this.state so I don't have like 50 different variations? I tried make the state1s the child of the StateItems but it still feels very derivative.
If stateItems and state1s were two different arrays is there a way to call the first item (Florida) of stateItems to the first target(state1)and so forth so they are "linked" so speak. Would something like that work and how would I go about do that?
I'm very new to javascript so my apologies if there is a super easy solution.
Edit:
The StateItems are individual symbols in Animate of the actual United States of America example: Florida is a state in the literal United States so it's part of my StateItem array. The stateplace is where the states will be placed and stateplace is one symbol and stateplace 1 2 and 3 are instances of stateplace. I just wanted to know if there was a way to cleanup the stateplace1s in the StateItems loop so there isn't 50 stateItems[0].stateplace1 = this.stateplace1;
I just couldn't figure out another way to connect each StateItem to its corresponding stateplace. The code works the way I need it to I just don't know if theres a way to clean it up a bit. I hope that clears things up.
var stateItems = [this.florida, this.alabama, this.southcarolina]
// we apply the same code for each symbol with the for loop
for(var i = 0; i<stateItems.length; i++){
stateItems[i].on("mousedown", onMouseDown.bind(this));
stateItems[i].on("pressmove", onMouseMove.bind(this));
stateItems[i].on("pressup", onMouseUp.bind(this));
stateItems[0].stateplace1 = this.stateplace1;
stateItems[1].stateplace1 = this.stateplace2;
stateItems[2].stateplace1 = this.stateplace3;
stateItems[i].originX = stateItems[i].x;
stateItems[i].originY = stateItems[i].y;
}
// mouse down event
function onMouseDown(evt){
var item = evt.currentTarget;
item.offset = {x:0, y:0};
var pt = item.parent.globalToLocal(evt.stageX, evt.stageY);
item.offset.x = pt.x - item.x;
item.offset.y = pt.y - item.y;
item.drag = true;
}
// mouse up event
function onMouseUp(evt){
var item = evt.currentTarget;
item.drag = false;
var pt = item.localToLocal(item.dot.x, item.dot.y, item.state1.hitBox);
if(item.stateplace1.hitBox.hitTest(pt.x, pt.y) ){
item.x = item.stateplace1.x;
item.y = item.stateplace1.y;
item.mouseEnabled = false; // prevents object from being move when place correctly
}else{
item.x = item.originX;
item.y = item.originY;
}
}
function onMouseMove(evt){
var item = evt.currentTarget;
if (item.drag){
var pt = item.parent.globalToLocal(evt.stageX, evt.stageY);
item.x = pt.x - item.offset.x;
item.y = pt.y - item.offset.y;
}
// mouse move event
}
I have this closure :
function CFetchNextData(ofs, pag, fetchFunction) {
var offset = ofs;
var limit = pag;
return function(options, cb) {
//do stuff to create params
fetchFunction(params, cb);
offset += limit;
};
}
I then create a variable this way:
var fetchInfo = CFetchNextData(0, 10, specificFetchFunction);
fetchInfo(options, myCB);
So that everytime I call fetchInfo, pagination is automatically set to the next set of data. That works great, althought
I'd like to have multiple instance of : "fetchInfo", each one having its own scope.
var A = fetchInfo; // I'd like a clone with its own scope, not a copy
var B = fetchInfo; // I'd like a clone with its own scope, not a copy
I could do:
var A = new CFetchNextData(ofs, pag, fetchFunction);
var B = new CFetchNextData(ofs, pag, fetchFunction);
But obviously I would have to setup "ofs" and "pag" each time, whereas by cloning fetchInfo, I'd have a stable pagination, set only once and for good.
Do you know how to achieve that ?
Thanks in advance
There isn't a concept of cloning a function in JavaScript. You need to call CFetchNextData (or another function) multiple times if you want to create multiple closures.
You could have CFetchNextData return a factory function instead of returning the actual function. But I'm not sure that's really an improvement.
function CFetchNextDataFactory(ofs, pag, fetchFunction) {
return function() {
var offset = ofs;
var limit = pag;
return function(options, cb) {
//do stuff to create params
fetchFunction(params, cb);
offset += limit;
};
};
}
var fetchInfoFactory = CFetchNextData(0, 10, specificFetchFunction);
var A = fetchInfoFactory();
var B = fetchInfoFactory();
This may not answer all of your question but just to pitch in , you could try assigning your parameters to a default / fallback value which will allow you to avoid setting ofs and pag each declaration . Below is a prototype of what I came up with . Its using oop :
class CFetchNextData {
constructor(ofs, pag){
this.OFS = 1; //default value
this.PAG = 10; //default value
this.ofs = ofs;
this.pag = pag;
if(ofs == null || ofs == undefined){
this.ofs = this.OFS;
}
if(pag = null || pag == undefined){
this.pag = this.PAG;
}
}
fetchInfo(){
var data = this.ofs += this.pag;
return data;
}
}
var task1 = new CFetchNextData(); // Falls back to default values..
var task2 = new CFetchNextData(32,31); // Uses values from specified in args...
document.write(task1.fetchInfo() + "\n")
document.write(task2.fetchInfo())
Hope this helps...
I have a mighty strange JavaScript problem. I have made an object oriented maze generator, which works well, but only if I call "this" (or the alias "self") right before the generator.
See code below:
// Constructor for a maze
function Maze(mazeWidth, mazeHeight) {
// Always working reference to this
var self = this;
// Has the maze been generated?
var generated = false;
// Default dimensions
var width = 20;
var height = 20;
// Check if dimensions are given
if (!isNaN(mazeWidth) && mazeWidth >= 1) {
width = parseInt(mazeWidth);
}
if (!isNaN(mazeHeight) && mazeHeight >= 1) {
height = parseInt(mazeHeight);
}
// The maze itself
var maze = {};
// Populate the maze
for (var y = 0; y < height; y++) {
maze[y] = {};
for (var x = 0; x < width; x++) {
maze[y][x] = new MazeCell(x, y);
}
}
// Function to get a cell
this.getCell = function(x, y) {
return maze[y][x];
}
// For some mighty strange reason "self" (or "this") needs to be called here for the code below to work
self;
// Generate the maze
(function generateMaze() {
// Map directions to its reverse
var directionMap = {};
directionMap[Maze.prototype.N] = Maze.prototype.S;
directionMap[Maze.prototype.E] = Maze.prototype.W;
directionMap[Maze.prototype.S] = Maze.prototype.N;
directionMap[Maze.prototype.W] = Maze.prototype.E;
// Depth-first search to generate the maze
(function DFS(cell, entryDirection) {
// Set the cell as discovered and open the entry direction
cell._setDiscovered();
cell._open(entryDirection);
// Find the neighbour cells
var neighbours = {};
neighbours[Maze.prototype.N] = cell.getNeighbourCell(Maze.prototype.N);
neighbours[Maze.prototype.E] = cell.getNeighbourCell(Maze.prototype.E);
neighbours[Maze.prototype.S] = cell.getNeighbourCell(Maze.prototype.S);
neighbours[Maze.prototype.W] = cell.getNeighbourCell(Maze.prototype.W);
// Check the neighbour cells in random order
for (var i = 0; i < 4; i++) {
var direction = (function() {
var result;
var count = 0;
for (var direction in neighbours) {
if (Math.random() < 1/++count)
result = direction;
}
return result;
})();
var nextCell = neighbours[direction];
delete neighbours[direction];
if (nextCell == false)
continue;
if (nextCell._isDiscovered())
continue;
// Set exit opening of this cell
cell._open(direction);
// Process next cell
DFS(nextCell, directionMap[direction]);
}
})(self.getCell(Math.floor(Math.random()*width), Math.floor(Math.random()*height)), null); // This line is the problem
})();
// ......
If I don't call "self" above the generation code, this.getCell will be called, but the first parameter will be a reference to the generateMaze-function itself. The second parameter will be unset.
It also work if I change the dummy line from "self" to "this".
Just writing "self" (or "this") on an otherwise empty line doesn't really do anything, does it? Why is it needed?
You should add semicolons after assigning a value to an attribute or variable, even if the value is a function, like here:
// Function to get a cell
this.getCell = function(x, y) {
return maze[y][x];
}
It should look like this:
// Function to get a cell
this.getCell = function(x, y) {
return maze[y][x];
};
I think that might be the cause of your problem.