I am currently working on a CMS project to create perspective objects using CSS transform properties (scale, rotate, translate...) in 3d: XYZ.
When an object is created it has these characteristics in its matrix3d(-16 values-) returned by window.getComputedStyle(element) -> style['transform']
The next step is to create animations: through a set of input[range], the user can modify these objects' characteristics and get their final state once the animation is finished.
It works very well. The question now is to create the complete dynamic CSS system, object by object.
I have its matrix1 (initial matrix3d) and its matrix2 (final matrix3d). By executing
#keyframes anim {
from { transform: matrix1 }
to { transform: matrix2; }
}
document.getElementById('object').style.animation = 'anim 2s linear';
it should work on its own... The prob is to create the CSS instruction. Using:
document.styleSheets[0].insertRule('
#keyframes anim {
from { transform: matrix1 }
to { transform: matrix2; }
}'
);
works well but once it's created in the main CSS file, if user modifies the matrix2 I can't rewrite the same instruction twice...
My idea is to create a CSS file for each objet on the server then import it with JS: if user is satisfied I keep it, and if not I remove it and create a new one with its new matrix2.
the advantage is that I can keep the word 'anim' without risking conflict between objects since each will call its own CSS (i.e 'object1.css').
Is this the best way to proceed or do you recommend another one?
Another question: despite my research I can't find what the 16 values of matrix3d correspond to. Translate XYZ is in [12], [13], [14] but I don't have them all. If you know a more explicit resource than https://developer.mozilla.org/fr/docs/Web/CSS/transform-function/matrix3d() it may help.
Finally found the solution... maybe not the best but it works.
We create the first matrix3D (object in its initial state):
getObjectValues('objID', 0); // 0 for matrix1
The function
function getObjectValues(div, n){ // 0 for matrix1, 1 for matrix2
let element = document.getElementById(div);
let myTransform = window.getComputedStyle(element,null)
let matrix = myTransform.getPropertyValue("-webkit-transform");
if (matrix === 'none' || typeof matrix === 'undefined') {
// native HTML objects (as divs) are not in a 3D dimension space
// if necessary we create its 3D environment
element.style.transform = 'translateZ(1px)';
getObjectValues(div, n); // then reload the function
}
else {
matrixObj[n] = matrix;
}
}
// Array matrixObj at this step:
[0] -> matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1)
After playing with the different input type="range" (for position, rotation, ... in XYZ dimension) we create the second matrix3D (final state) by clicking on the "TEST" button, which calls
getObjectValues('objID', 1); // 1 for matrix2
// Array matrixObj at this step:
[0] -> matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1)
[1] -> matrix3d(0.945519, 0, -0.325568, 0, 0, 1, 0, 0, 0.325568, 0, 0.945519, 0, 0, 0, -48, 1)
Now the trick: we create a pseudo CSS sheet called "anim-update"
includeCSS("anim-update");
let styleSheet = document.getElementById("anim-update");
// the function
function includeCSS(css) {
let head = document.getElementsByTagName('head')[0];
let sheet = document.createElement('style');
sheet.setAttribute('id',css);
sheet.setAttribute('rel', 'stylesheet');
sheet.setAttribute('type', 'text/css');
head.appendChild(sheet);
}
Once its done we play these 3 sequences:
let delay = 2; // duration
// 1. returns the object to its initial state
setTimeout(function(){
document.getElementById(div).style.transform = matrixObj[0];
},100);
// 2. play the scenario
setTimeout(function(){
styleSheet.innerHTML = '#keyframes anim {
from { transform: '+matrixObj[n][0]+' }
to { transform: '+matrixObj[n][1]+' }
}';
document.getElementById(div).style.animation = 'anim '+delay+'s linear';
},500);
// 3. removes the temporary CSS at the end of the animation
setTimeout(function(){
document.getElementById('anim-update').outerHTML = "";
delete document.getElementById('anim-update');
},500+2000); // +2 seconds -> delay*1000
In this way User can change the final state of the object as much as he wants until he gets the desired animation, which he checks by clicking on the "TEST" button. A "SAVE" button retrieves the data from matrixObj and saves the 2 matrix values. Then move to the next object.
Related
I am trying to implement a chess game in javascript. I created a chessboard and a rook
let board1 = [
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, "br", 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
];
//the following function creates a cheesboard:
function createBoard(board, boardEl) {
board.forEach((row, i) => {
row.forEach((col, j) => {
const square = document.createElement("div");
boardEl.appendChild(square);
const piece = document.createElement("img");
if (col) {
piece.src = `pieces/${col}.png`;
piece.style.userSelect = "none";
piece.dataset.color = col[0];
piece.dataset.symbol = col[1];
piece.dataset.col = j;
piece.dataset.row = i;
square.appendChild(piece);
}
square.style.display = "flex";
square.style.alignItems = "center";
square.style.justifyContent = "center";
square.style.userSelect = "none";
square.dataset.row = i;
square.dataset.col = j;
if (i % 2 == j % 2) square.style.backgroundColor = "green";
else square.style.backgroundColor = "white";
});
});
}
<body
style="
display: flex;
align-items: center;
justify-content: center;
width: 100vw;
height: 100vh;
background-color: white;
overflow:hidden;
"
>
<div
class="board"
style="
width: 560px;
height: 560px;
display: grid;
grid-template-columns: repeat(8, 1fr);
grid-template-rows: repeat(8, 1fr);
border: 1px solid white;
"
></div>
</body>
and the following event Listeners are responsible for dragging the piece in the chessboard
let draggedPiece;
let currentPiece;
document.addEventListener("dragstart", function (event) {
draggedPiece = event.target;
const { row, col, color, symbol } = draggedPiece.dataset;
currentPiece = new Piece(row, col, color, symbol);
console.log(currentPiece);
});
document.addEventListener("drop", function (event) {
if (
currentPiece.possibleMoves.find(
(square) =>
square.row == event.target.dataset.row &&
square.col == event.target.dataset.col
)
) {
draggedPiece.parentNode.removeChild(draggedPiece);
event.target.appendChild(draggedPiece);
currentPiece.updateCoords(
event.target.dataset.row,
event.target.dataset.col
);
currentPiece.getPossibleMoves();
}
});
document.addEventListener("dragover", function (event) {
event.preventDefault();
});
in the drop eventListener I am check if the piece can move to the square that we want to drop to;
but when I update the the coords of the current piece they don't change. By the way here is The piece class:
class Piece {
constructor(row, col, color, symbol) {
this.row = row;
this.col = col;
this.color = color;
this.symbol = symbol;
this.possibleMoves = [];
this.getPossibleMoves();
}
getPossibleMoves() {
this.possibleMoves = [];
for (let i = 0; i < 8; i++) {
if (i != this.col) {
this.possibleMoves.push({ row: i, col: Number(this.col) });
this.possibleMoves.push({ row: Number(this.row), col: i });
}
}
}
updateCoords(row, col){
this.row = row;
this.col = col;
}
}
Can you find some improvement on my code and help me solve this problem. Thank You.
Note: can you provide some hints, guidances and reference to help me in the creation of this nice game, And I don't know if the image will display when you run the code.
Here are some other things to consider while programming:
At the least you should have more classes: Board, Square, base class Piece from which you derive Rook, Bishop, Knight, etc...
Boards:
How a Board is represented internally (maybe as a 1 dimensional array) has no bearing on how it's drawn (2 dimensionally). Let the html and css worry about that.
Boards draw Squares.
Squares
Have a fixed location on a Board
Squares draw Pieces.
Pieces
know their current Square
All Pieces move But derived Pieces determine how
Rooks, Knights, etc (derived Pieces)
Know their own legal moves. eg: a Rook cannot move through an occupied Square.
Can draw themselves (.png)
You could have piece classes for each piece instead of one piece class. Although you don't necessarily need separate classes for each piece type, you could get away with three:
Pawns,
Jumping pieces (kings and knights), and
Sliding pieces (rooks, bishops, and queens)
The move generation breaks down nicely in that way. Pawn movement is completely unique to pawns plus you have to handle move generation for double pushes from their starting rank, en passant, and promotions. Kings and knights move in the exact same way, they just target different squares. Similarly, rooks, bishops, and queens all move in the same basic way, just in different directions.
This way a king is created by injecting a king attack map into a jumping piece class, while a knight is created by injecting a knight attack map into the same class.
From an OOP perspective, it seems natural that a piece should keep track of where it is, however in practice this doesn't work very well (In general, OOP isn't the best way to approach chess programming, but it can work). In order to generate moves for a piece, you need to know both where the piece is, and where all the other pieces are. So can pick one of three options either:
the piece knows where it is and doesn't know how it moves, i.e., you have a separate class that contains movement rules for all pieces and applies it by extracting position information from piece classes, or
the piece knows where it is and how it moves, which would require very complex relationships in communicating positions between the various piece classes, or
the piece knows how it moves but not where it is, and position information is fed in from outside.
For option one, it doesn't really make sense to have a class that only knows where the piece is, there are better ways to store this information if that is all the class is doing. Option two, as described above, becomes very clunky and convoluted whenever I have tried to implement it. So, I think that option three is the best. The board knows where the pieces are, and the piece classes do not represent the pieces themselves, but instead represent the movement rules for those pieces.
I think it makes more sense to have a board class that tracks the pieces, piece classes that hold movement rules, and a move generator class that holds the rules for each piece. When the move generator wants to generate moves for a particular piece it can look at the board and see where the pieces of that type are (as well as other required information such as empty squares, opponent pieces, ep target, and castling rights) then reaches to its shelf of rule books (piece classes) gets the movement rules for the required piece and feeds in the information it obtained from the board.
The downside here is that you have piece classes that contain no data at all, just methods that implement the rules. So you would have to ask yourself if maybe they are better implemented as functions. Although, the class option does give you the option of run time polymorphism in rule generation, which you may find beneficial.
In terms of board representation, a good option, to begin with, are bitboards (which you can read about here). I don't know much Javascript and don't know about how bit twiddling works for Javascript. However, even if you are not comfortable with using a single integer to represent a particular view of the entire board you could do something similar with a 64-element array of 0s and 1s.
I'm currently using Phaser 3 to represent my server's state.
Every x amount of time, I am sent the server's game state, this is what the client looks like:
var t1 = Date.now();
var serverUpdateDelta = 0;
Client.socket.on('usersPool', usersPool => {
// usersPool is an object containing all the user data of sockets connected on the server. Looks something like this:
/*
usersPool = {
"user1234": { x: 0, y: 0, direction: "right", moving: true },
"testuser": { x: 200, y: 250, direction: "down", moving: false }
}
*/
// keeping count of milliseconds between updates (usually around 500m)
serverUpdateDelta = Date.now() - t1;
// for every user connected on the server...
for(id in usersPool) {
let data = usersPool[id]; // this is the user's data
if(/* the player exists as a sprite in the game...*/) {
// THIS IS WHERE THE MAGIC SHOULD HAPPEN
} else {
genSprite(player);
}
}
});
The player's data contains a movementQueue, which is just an array of coordinates the user has been at. It might look a little like this:
[
{ x: 0, y: 0, direction: 'down', moving: false },
{ x: 5, y: 0, direction: 'right', moving: true },
{ x: 6, y: 0, direction: 'right', moving: false }
]
This is calculated on the server, but each movementStack (item in the movementQueue`) is generated every 25 milliseconds or so on the server.
The job now is, when receiving this movementQueue, to interpolate the values and move the sprite accordingly...
Attempt 1
I first tried making a function which would interpolate once the update was received, like so:
// THIS IS WHERE THE MAGIC SHOULD HAPPEN
// user's state on the client is set to an interpolated version of the one on the server
player.movementQueue = buffer(data.movementQueue);
The buffer will simply generate an interpolated array based on the serverUpdateDelta and game.loop.actualFps
then, in the Game.update function I ran the following:
for(id in spawnedPlayers) {
// this will remove the first movementStack from the queue and returns in
movementStack = spawnedPlayers[id].movementQueue.shift();
// we then take this movementStack and update the user to that position (and play the walking animation)
spawnedPlayers[id].update(movementStack);
}
So every game loop, we would remove a stack from the queue and set the user to it.
This did NOT work. The game loop seemed to run Way more times than there were frames in the queue, making the player look like they were moving a small distance very slowly...*:
player.movementQueue = player.movementQueue.concat(buffer(data.movementQueue));
But then something weird happened, where the game loop could not keep up with the movementQueue and the player would move extremely slowly...
Attempt 2
I then tried using tweens which would be really easy to implement, simply run:
// THIS IS WHERE THE MAGIC SHOULD HAPPEN
_this.tweens.timeline({
targets: player.sprite,
tweens: data.movementQueue, // [{x, y}, {x, y}, {x, y}]
duration: serverDeltaTime/movementQueue.length, // duration between each tween in ms
});
This worked ALMOST perfectly, except for one small detail:
Before, we would run a method for the player on each movementStack: player.update(movementStack), this method would take the direction of the user and animate the sprite accordingly. Now we have no way of doing this...
SO
What methods or techniques could I use? What am I missing? What could I implement? I ask this because I'm stuck at this point.
Thank you in advance for the help.
I want to increment values for days of week and slots within each day, i.e. Morning, Afternoon, Evening and Night, as I iterate over a collection where I get the time and then evaluate which timeslot the time is in.
I need to be able to iterate over the end result in the front end but want a nicer solution that the one i currently have represented but the totals array. It has been suggested I use a map but unsure of how to implement this with a map.
var totals = [
// Tot M T W T F S S
[0, 0, 0, 0, 0, 0, 0, 0], // Totals
[0, 0, 0, 0, 0, 0, 0, 0], // Morning
[0, 0, 0, 0, 0, 0, 0, 0], // Afternoon
[0, 0, 0, 0, 0, 0, 0, 0], // Evening
[0, 0, 0, 0, 0, 0, 0, 0] // Night
];
var collectionOfData = entiredataset;
collectionOfData.forEach(function (item) {
var localDate = getLocalDate(item);//gets users local date and determines timeslot id - ie Morning,afternoon, evening, or night
var dayOfWeek = localDate.day();
var timeslotId = item.timeslotId;
totals[timeslotId][dayOfWeek]++; // Increase sessions per slot and day
totals[0][dayOfWeek]++; // Increase total sessions per day
totals[timeslotId][0]++; // Increase total sessions per slot
totals[0][0]++; // Increase total sessions
}
Any suggestions would be much appreciated.
One of the ways I would design the data structure is as follows - see demo with input, output and total calculation methods:
var slots={'Monday':{'Morning':0,'Afernoon':0,'Evening':0,'Night':0},'Tuesday':{'Morning':0,'Afernoon':0,'Evening':0,'Night':0},'Wednessday':{'Morning':0,'Afernoon':0,'Evening':0,'Night':0},'Thursday':{'Morning':0,'Afernoon':0,'Evening':0,'Night':0},'Friday':{'Morning':0,'Afernoon':0,'Evening':0,'Night':0},'Saturday':{'Morning':0,'Afernoon':0,'Evening':0,'Night':0},'Sunday':{'Morning':0,'Afernoon':0,'Evening':0,'Night':0},}
// input data
function insertSlotFor(day, slot) {
slots[day][slot]++;
}
// output data
function getSlotFor(day, slot) {
return slots[day][slot];
}
// get total for a day
function totalForDay(day) {
return Object.keys(slots[day]).reduce(function(prev,curr){
return prev + slots[day][curr];
},0);
}
// get total for a slot
function totalForSlot(slot) {
return Object.keys(slots).reduce(function(prev,curr){
return prev + slots[curr][slot];
},0);
}
insertSlotFor('Monday', 'Morning');
insertSlotFor('Monday', 'Night');
insertSlotFor('Tuesday', 'Morning');
console.log(slots);
console.log(totalForDay('Monday'));
console.log(totalForSlot('Morning'));
.as-console-wrapper{top:0;max-height:100%!important;}
You need to define an object of e.g. DayOfWeek, and have methods in that object that will do all that logic. This will encapsulate the logic and make it easier to manage the code. Then, your array will hold instances of that object, and you will just refer to objects by their index (dayOfWeek) and call methods to get the totals.
Your getLocalDate() function should do what it says - get data, and not set anything (i.e. timeslotId) in the item. This is confusing and mixes responsibilities. It can be a method of the DayOfWeek object, who's constructor will get an item and return the values as needed.
Hello Stackoverflow community
As I am trying to build a small game, i came to a problem.
Somehow, when i try to fade out multiple shapes, respectivly a group with the shapes in it, some of the shapes will not fade out or browser gets a stack overflow.
So as i tried out several hours to fix the problem i need your help.
Heres the link to a little fiddle i made: http://jsfiddle.net/hnBPT/
As you can see theres a function newFadeShapesOut() which needs the nodes that should be fade out and also the layer of the nodes.
It moves the nodes into a group and fades the group out. Somehow and sometimes, one or more shapes will not fade out or there occurs a fatal error.
Function for fadeout:
function newFadeShapesOut(shapes, layer, callback, speed){
if(typeof(speed) == 'undefined'){
speed = 1;
}
var g = new Kinetic.Group();
console.log(layer.getChildren().length);
console.log(shapes.length);
layer.add(g);
shapes.each(function(shape){
shape.moveTo(g);
});
console.log(layer.getChildren().length);
console.log(shapes.length);
var tween = new Kinetic.Tween({
node: g,
opacity: 0,
duration: speed,
onFinish: function(){
if(typeof(callback) != 'undefined'){
callback();
tween.destroy();
}
}
}).play();
}
PS: Google Chrome is recommend, firefox tends to crash.
Thank you for your help.
EDIT: Sorry i forgot about that, you can activate the script by clicking the red square.
There's some strange behaviour going on here. Look at my comments as I tried to rewrite your function:
function fadeShapesOut(layer, callback, speed) {
var children = layer.children;
//The layer here already shows some children have moved.
//2 children remain, 1 text and 1 rect.
console.log("LAYER");
console.log(layer);
//Again, children shows that there are only 2 children of layer at this point: Test 2 and Button Rect
console.log('CHILDREN');
console.log(children);
if(typeof(speed) == 'undefined'){
speed = 1;
}
var group = new Kinetic.Group();
layer.add(group);
children.each(function(child) {
console.log("CHILD");
console.log(child); //This spits out Test 1, Test 3 and the newly added Group. (Strange order???
child.moveTo(group);
});
//Since group is already added to the layer, you're all of layer's children to group, including group itself. Which is causing a never ending loop of references to group including itself - causing the stack overflow.
var tween = new Kinetic.Tween({
node: group,
opacity: 0,
duration: speed,
onFinish: function(){
if(typeof(callback) != 'undefined'){
callback();
tween.destroy();
}
}
}).play();
}
What's messing you up is that the group is being considered a child of layer (Even though it hasn't been added yet in the order of function calls, which is strange behaviour to me). So when you loop through the children of layer in the each function, you're trying to move group --> group which screws up the reference in a never ending loop.
I logged a bunch of things in my fiddle, so go ahead and take a look to see some of the strange behaviour I was talking about above.
Anyways, if your callback is going to destroy the layer, what is the point of moving everything to a new group in the function? That Group is messing your code up and I don't see the point of it if you're just going to destroy the layer.
Instead you can achieve the effect you want by just tweening the layer itself:
function fadeLayer(layer, callback, speed) {
var tween = new Kinetic.Tween({
node: layer,
opacity: 0,
duration: 2,
onFinish: function(){
layer.destroy();
tween.destroy();
}
}).play();
}
If you must stick with your original function format, then you can grab children by using names:
newsobj[n] = new Kinetic.Text({
nid: n,
x: 140,
y: ((n == 0) ? 294.5 : 304.5 ),
text: news[n],
fill: 'white',
fontFamily: 'Corbel W01 Regular',
fontSize: 11.26,
name: 'fadeThisAway'
});
button = new Kinetic.Rect({
x: 10,
y: 10,
width: 100,
height: 100,
fill: 'red',
name: 'fadeThisAway'
});
In my example, I used the name fadeThisAway. And then, using your old function:
function newFadeShapesOut(layer, callback, speed){
var shapes = layer.get('.fadeThisAway');
if(typeof(speed) == 'undefined'){
speed = 1;
}
var g = new Kinetic.Group();
console.log(layer.getChildren().length);
console.log(shapes.length);
layer.add(g);
shapes.each(function(shape){
shape.moveTo(g);
});
console.log(layer.getChildren().length);
console.log(shapes.length);
var tween = new Kinetic.Tween({
node: g,
opacity: 0,
duration: speed,
onFinish: function(){
if(typeof(callback) != 'undefined'){
callback();
tween.destroy();
}
}
}).play();
}
Instead of passing shapes through the function, just call
var shapes = layer.get('.fadeThisAway');
at the beginning of the function (you're passing layer through the function already anyways) to grab the children that are named fadeThisAway. (Note: This works because the group is not named fadeThisAway)
Working example and comments inside: JSFIDDLE
UPDATE
Okay so I made a basic example of the issue with layer.children
2nd JSFIDDLE
And it looks like that's just how the children of layer works. This proves that you definitely have to distinguish between shapes and group, because the group will always be considered a child of layer.
The naming method works to distinguish your shapes between layers by giving all shapes a common name that excludes groups.
After several attempts to bend projeqht's function to my way i finally did it!
Somehow, the collection shapes just updates itself when adding the group to the layer!
If i use an array instead, it works.
Hope it helps someone!
So here my solution which works like a charm.
function fadeShapesOut(shapes, callback, speed){
layer = shapes[0].getLayer();
if(typeof(speed) == 'undefined'){
speed = 1;
}
var g = new Kinetic.Group();
layer.add(g);
for(i in shapes){
shapes[i].moveTo(g);
}
var tween = new Kinetic.Tween({
node: g,
opacity: 0,
duration: speed,
onFinish: function(){
if(typeof(callback) != 'undefined'){
callback();
}
tween.destroy();
}
}).play();
}
If you have further questions, don't mind contacting me.
If I have an image that I apply a filter to, e.g. Lomo filter, is there way to make that the current Caman instance?
Meaning, if I then want to then play about with the brightness on the image that I applied the filter to, and use this.revert(); to reset the brightness, I want it to revert to the canvas with the filter on it that I just applied.
Is this possible?
I'm having a nightmare with trying to apply many effects, only one at once (except for preset filters), and carry the state through...
If i understand, you want to apply filter ("Lomo") as shown on their example page and then fiddle with some properties (like brightness) and then revert changes back to "Lomo" filter?
Why not just click on filter ("Lomo") again?
EDIT:
You should probably take a look at guide and implement your own method with default values like in filters.
u.Filter.register("lomo", function (D) {
if (D == null) {
D = true
}
this.brightness(15);
this.exposure(15);
this.curves("rgb", [0, 0], [200, 0], [155, 255], [255, 255]);
this.saturation(-20);
this.gamma(1.8);
if (D) {
this.vignette("50%", 60)
}
return this.brightness(5)
});
I dont think your requirement comes "out of the box".
If i understand you correctly , You want to apply a filter and play with other effects like brightness and contrast etc.,
I made some code which will work according to your need
Caman('#canvas-camanImage',"./../media/caman.png", function () {
this.revert(false);
for(var i = 0 ;i<selectedPresets.length;i++){
this[selectedPresets[i]]();
}
for(var key in effect){
this[key](effect[key].value);
}
this.render(function () {
});
in the above code i am storing all effects like brightness contrast in effect variable like effect = {
brightness : {
min : -100,
max: 100,
value : 0
},
contrast : {
min : -100,
max: 100,
value : 0
},
saturation : {
min : -100,
max: 100,
value : 0
}
};
and presets in an array
presets = [
{filter:'vintage',name : 'Vintage'},
{filter:'lomo',name:'Lomo'},
{filter: 'clarity', name:'Clarity'},
{filter:'sinCity', name:'Sin City'}
];
So every time you add any preset or change any effect value i am changing the values in variable and rendering canvas again
It is working very fine for me Let me know if your concern is something else