I would like to animate the moving behavior of my thumbnails divs. I want each individual div to move differently. The grid itself is also infinite scrollable in both directions. At the moment I have the following problem:
The divs are moving all to the left side of the page
At some point the animation stops
Sometimes the grid flickers during the first initial scroll
You can see the result in this pen:
https://codepen.io/Dennisade/pen/OJymaKZ
This is how I set up the css:
CSS:
.grid-image {
text-align: center;
width: 50%;
margin-bottom: 18%;
pointer-events: none;
will-change: transform;
transition: 20s linear
}
JS
//MOVING ANIMATION
setInterval(function() {
$('.grid-image').each(function(index){
var yAxis= index * Math.floor(Math.random()-2);
var xAxis= index * Math.floor(Math.random()-1);
$('.grid-image').css('transform', 'matrix(1, 0, 0, 1, ' + yAxis + ',' + xAxis + ')');
});
}, 50);
//ENDLESS SCROLL
var origDocHeight = document.body.offsetHeight;
var clone = $(".wrapper").contents().clone();
clone.appendTo(".wrapper");
clone.prependTo(".wrapper");
$(document).scroll(function(){
var scrollWindowPos = $(document).scrollTop();
if(scrollWindowPos >= origDocHeight ) {
$(document).scrollTop(0);
}
if(scrollWindowPos <= 0 ) {
$(document).scrollTop(origDocHeight);
}
});
there seem to be two separate parts here. First, the divs are not moving correctly and then they stop moving altogether. Second, scrolling isn't working properly.
Looking at div movement first:
I am not familiar with jquery but one of your problems seems to be that this line
$('.grid-image').css('transform', 'matrix(1, 0, 0, 1, ' + yAxis + ',' + xAxis + ')');
does not update the transform settings for the current element but changes the CSS for all elements of class grid-image. To ensure each div gets its new matrix you can change the above line to
this.style.transform='matrix(1, 0, 0, 1, ' + yAxis + ',' + xAxis + ')';//incidentally did you want the x and y this way round?
Another problem is that all the divs apart from the first will still end up with the same xAxis and yAxis in their transform matrix. This is because Math.floor(Math.random()) is equivalent to Math.floor(Math.random()*1) which gives a random integer from 0 (inclusive) to 1 (exclusive), so it is always 0. Depending on how big you want these values to be you can use a higher integer. For example, Math.floor(Math.random()*10) gives a random integer from 0 to 9.
I do not know whether you intended this or not, but the first div will never have its transform changed as its index is 0 so these lines always set xAxis and yAxis = 0
var yAxis= index * Math.floor(Math.random()-2);
var xAxis= index * Math.floor(Math.random()-1);
Adding 1 to the index, for example, would ensure the first div gets new values.
Now looking more closely at the calculations for the axes, CSS transform matrix interprets the last two of its parameters as translateX and translateY. These are relative to the current position of the element. In the original the values were always negative so all the elements moved gradually to the left and (less noticeably as the increment was smaller) up.
Using your Codepen my laptop became very hot, with the fan going crazy - there is a lot of calculation going on with a matrix transform on so many elements 20 times a second. I have attempted to mitigate this by noticing that you are not scaling or skewing elements so a translate transform instead of a matrix transform would do.
Putting these things together if we replace the setInterval call with
setInterval(function() {
$('.grid-image').each(function(index){
var yAxis= (index+1) * Math.floor(Math.random()*1000) * (Math.random() < 0.5 ? -1 : 1);
var xAxis= (index+1) * Math.floor(Math.random()*1000) * (Math.random() < 0.5 ? -1 : 1);
this.style.transform='translate('+xAxis+'px,'+yAxis+'px)';
});
}, 50);
we get random-looking motion of all the divs. The amount of motion of course depends on both the time interval and the calculations for the x and y increments and I have *1000 here to get some easily visible motion. It needs to be more if you want elements to move across the screen more. I've kept the index in the calculations but I'm not sure if that's what you intended - elements further down move more than those at the top, and then as you scroll you find slower ones again (so the overall effect is to have a hint of a pattern rather than total randomness).
Using this code I have not seen the animation stop - and have run it for many minutes. The laptop got hot but not so much. I suppose there could be a question around whether less powerful processors can complete all the matrix transforms in the time interval. If that proves to be a problem then moving to setTimeout would be helpful as would any CSS changes that encouraged use of GPUs.
Hope this helps with your first two points.
Now to look at the scrolling. This code
var clone = $(".wrapper").contents().clone();
clone.appendTo(".wrapper");
clone.prependTo(".wrapper");
puts just one copy of the content div into the .wrapper div. I think this is because it gets appended, then prepended which means it isn't anymore appended i.e. it can't be in two places at once. Replacing it with this code gives 3 copies of a content div which I guess you know will be enough for the sizes of display your code will be used on
var clone = $(".wrapper").contents().clone();
var clone2 = $(".wrapper").contents().clone();
clone.appendTo(".wrapper");
clone2.prependTo(".wrapper");
Scrolling back now works but forward has a problem. In this code
$(document).scroll(function(){
var scrollWindowPos = $(document).scrollTop();
if(scrollWindowPos >= origDocHeight ) {
$(document).scrollTop(0);
}
if(scrollWindowPos <= 0 ) {
$(document).scrollTop(origDocHeight);
}
});
where scrollWindowPos >= origDocHeight it scrolls to 0 which means next time scrollWindowPos <=0 is true and it jumps. Replacing with this code enables scrolling forward
$(document).scroll(function(){
var scrollWindowPos = $(document).scrollTop();
if(scrollWindowPos >= origDocHeight ) {
$(document).scrollTop(1); /* used to be 0 - using 1 is a bit hacky but it does the job */
}
else if(scrollWindowPos <= 0 ) {
$(document).scrollTop(origDocHeight);
}
});
It is not perfect. Scrolling by grabbing the scroll bar and moving it downwards creates 'flashes' but scrolling by selecting the scroll bar's downward arrow gives smooth scrolling, no flashes (Windows 10, Edge and Chrome). I can't think at the moment how to investigate this further.
I had to remove the display:none on the body which is in the CSS on Codepen in order to get a scroll bar, so it may be that I have misunderstood what you wanted from scrolling.
Here is the complete JS code with all the above changes in it
//MOVING ANIMATION
setInterval(function() {
$('.grid-image').each(function(index){
var yAxis= (index+1) * Math.floor(Math.random()*1000) * (Math.random() < 0.5 ? -1 : 1);
var xAxis= (index+1) * Math.floor(Math.random()*1000) * (Math.random() < 0.5 ? -1 : 1);
this.style.transform='translate('+xAxis+'px,'+yAxis+'px)';
});
}, 50);
//ENDLESS SCROLL
var origDocHeight = document.body.offsetHeight;
var clone1 = $(".wrapper").contents().clone();
var clone = $(".wrapper").contents().clone();
clone.appendTo(".wrapper");
clone1.prependTo(".wrapper");
$(document).scroll(function(){
var scrollWindowPos = $(document).scrollTop();
if(scrollWindowPos >= origDocHeight ) {
$(document).scrollTop(1);
}
else if(scrollWindowPos <=0 ) {
$(document).scrollTop(origDocHeight);
}
});
Related
Code pen (available online, have not made any changes here)
https://codepen.io/SteveJRobertson/pen/zxEwrK
Javascript
var cube = document.getElementById('cube');
var min = 1;
var max = 24;
cube.onclick = function() {
var xRand = getRandom(max, min);
var yRand = getRandom(max, min);
cube.style.webkitTransform = 'rotateX('+xRand+'deg)
rotateY('+yRand+'deg)';
cube.style.transform = 'rotateX('+xRand+'deg)
rotateY('+yRand+'deg)';
}
function getRandom(max, min) {
return (Math.floor(Math.random() * (max-min)) + min) *
90;
}
What I want-
after the dice finishes transition, it finishes with a face facing you. Is there a way to get which face this is?(i am considering this face as the output of the dice throw)
What I did-
I could not find the solution. Do after the dice finishes transition, I force it to another transition I want so that it finishes where I want it to. (
#cube.show-front {
transform: translateZ(-100px) rotateY( 0deg);
}
will make the cube land on its front side
You could have some complex matrix maths to figure that out but a quick look at how things are done can give you a simple solution.
The first thing to make sure to note is the initial position of all the faces. The faces are not positioned like on a real dice (the sum of opposite faces would equal 7, e.g. 1 opposed to 6).
Another thing is that the rotation only happens on 2 axes, each by a multiple of 90 degrees (a quarter of turn). And 4 quarters of turns (i.e. 1 full turn) is equivalent to no turn at all, so it is a repeating pattern and we can work with modulos.
Now for the actual rotation, I find it easier to work with fixed origins (not moving with the object), which means you need to read the CSS transform values from right to left.
First you are rotating the cube around the Y axis (front face moving towards the left / right) a certain number of times.
Once that is done you are rotating the cube around the X axis (front face moving up /down).
If you try to picture that you might notice that no matter what we do during the first step the top face will stay at the top (5 here) and the bottom one at the bottom (6 here). Which means with the second and last rotation we can easily tell if the cube finished on 5, 6, or a different number.
For the other cases this is just a matter of picking the correct value based on the first Y rotation (while not forgetting that a 180 degrees rotation on the X axis will show the opposite face).
// modulo not giving negative results - see https://stackoverflow.com/q/4467539/1336843
function mod(n, m) {
return ((n % m) + m) % m;
}
function getResult(rotX, rotY) {
let countX = mod(rotX / 90, 4);
if (countX === 1) {
// Bottom face
return 6;
}
if (countX === 3) {
// Top face
return 5;
}
// We add countX here to correctly offset in case it is a 180 degrees rotation
// It can be 0 (no rotation) or 2 (180 degrees)
let countY = mod(rotY / 90 + countX, 4);
// Faces order
return [1, 4, 2, 3][countY];
}
Fork of the pen logging the result to the console: codepen
You will notice that this shuffling method will not give each result an equal probability. The top and bottom faces (5 and 6) will be more likely to appear (1 time out of 4 each, while all the other faces will appear 1 time out of 8).
I've created a pinch filter/effect on canvas using the following algorithm:
// iterate pixels
for (var i = 0; i < originalPixels.data.length; i+= 4) {
// calculate a pixel's position, distance, and angle
var pixel = new Pixel(affectedPixels, i, origin);
// check if the pixel is in the effect area
if (pixel.dist < effectRadius) {
// initial method (flawed)
// iterate original pixels and calculate the new position of the current pixel in the affected pixels
if (method.value == "org2aff") {
var targetDist = ( pixel.dist - (1 - pixel.dist / effectRadius) * (effectStrength * effectRadius) ).clamp(0, effectRadius);
var targetPos = calcPos(origin, pixel.angle, targetDist);
setPixel(affectedPixels, targetPos.x, targetPos.y, getPixel(originalPixels, pixel.pos.x, pixel.pos.y));
} else {
// alternative method (better)
// iterate affected pixels and calculate the original position of the current pixel in the original pixels
var originalDist = (pixel.dist + (effectStrength * effectRadius)) / (1 + effectStrength);
var originalPos = calcPos(origin, pixel.angle, originalDist);
setPixel(affectedPixels, pixel.pos.x, pixel.pos.y, getPixel(originalPixels, originalPos.x, originalPos.y));
}
} else {
// copy unaffected pixels from original to new image
setPixel(affectedPixels, pixel.pos.x, pixel.pos.y, getPixel(originalPixels, pixel.pos.x, pixel.pos.y));
}
}
I've struggled a lot to get it to this point and I'm quite happy with the result. Nevertheless, I have a small problem; jagged pixels. Compare the JS pinch with Gimp's:
I don't know what I'm missing. Do I need to apply another filter after the actual filter? Or is my algorithm wrong altogether?
I can't add the full code here (as a SO snippet) because it contains 4 base64 images/textures (65k chars in total). Instead, here's a JSFiddle.
One way to clean up the result is supersampling. Here's a simple example: https://jsfiddle.net/Lawmo4q8/
Basically, instead of calculating a single value for a single pixel, you take multiple value samples within/around the pixel...
let color =
calcColor(x - 0.25, y - 0.25) + calcColor(x + 0.25, y - 0.25) +
calcColor(x - 0.25, y + 0.25) + calcColor(x + 0.25, y + 0.25);
...and merge the results in some way.
color /= 4;
I'm making a 2D game in JavaScript. For it, I need to be able to "perfectly" check collision between my players(the game has two players, open the picture please) and the walls! I mean, I have a function that actually works, but when I make them jump against the walls they pass through the walls and keep moving until they reach another area or even leave the canvas!
Also, if they are falling down and I make them collide with a wall, they just stop there wich is also pretty bad!
I really need help with that!! It's a university project and I have to finnish it really soon!
My game looks like this
The collision detection function I have is here:
function blockRectangle (objA, objB) {
var distX = (objA.x + objA.width / 2) - (objB.x + objB.width / 2);
var distY = (objA.y + objA.height / 2) - (objB.y + objB.height / 2);
var sumWidth = (objA.width + objB.width) / 2;
var sumHeight = (objA.height + objB.height) / 2;
if (Math.abs(distX) < sumWidth && Math.abs(distY) < sumHeight) {
var overlapX = sumWidth - Math.abs(distX);
var overlapY = sumHeight - Math.abs(distY);
if (overlapX > overlapY) {
objA.y = distY > 0 ? objA.y + overlapY : objA.y - overlapY;
}
else {
objA.x = distX > 0 ? objA.x + overlapX : objA.x - overlapX;
}
}
}
I did the walls with a maze and I'm using a for cycle to check the collisions with all of the walls I have saved in an array!
As you can see here:
for (var i in walls) {
var wall = walls[i];
if ((player.x < (wall.x + wall.width)) && ((player.x + player.width) > wall.x) && (player.y < (wall.y + wall.height)) && ((player.height + player.y) > wall.y)) {
player.falling = false;
}
blockRectangle(player, wall);
}
Please help me!!! Thank you all!
In your case I doubt a pixel perfect collision is required.
You can maintain a boolean matrix to store the position of solid objects. Solid objects like walls or players. Then in every frame you can check if your player is trying to move to a position where there is a solid object, if it is then stop it. You don't have to create grid of width x height in pixels, but rather choose a largest block (single element in the grid) in which each solid object reasonably occupies most of the block.
For example you can choose block size to be player_width / 2 x player_height /2.
See following image with grid
Another simple way could be to just check the background pixel color. Since your game is simple, background and object colors are different. So you just have to check if the player is trying to move somewhere where pixel color is not of background, thus there is a solid object and player should stop. You don't have to test for a lot of pixels, just 1 pixel in the direction the player is trying to move. (1 for horizontal and 1 for vertical). This however can not be used if you don't have a clear background color. Background color here is kind of the boolean grid for us in the previous suggestion.
I'm trying to make a smooth javascript animation that makes a div slide from position left 900 to 550 on the screen..
With two variables; place and speed, I use place to locate where the div is on the screen, and speed to decide the speed.
To make this nice and flowy I've tried to make the speed go slower and slower, so the slide starts fast and goes slower and slower.
I don't know if I'm trying to do the right thing here, but basically, I want the speed to start at 100 %, lets say 50 px, and go a percentages slower for each time.
The speeds percentages should be equal to the number of my place.
So that place starts at 900, and ends at 550.
The speed should start at 50 and go a percentages slower for each time and end at 0 as the place ends at 550...
How do I set this up???
I've tried this:
function doit(place, speed, proc) {
var denne = document.getElementById("screen1");
if (place > 550) {
var speedproc = 100 - (place / 950 * 100); // (the reason that I'm using 950 here is because it should have a percentage to start from that isn't 0)
var newspeed = speed - (speed / 100 * proc);
speed = newspeed;
proc += speedproc;
place -= speed;
denne.style.marginLeft = place + "px";
setTimeout("doit(" + place + ", " + speed + ", " + proc + ")", 20);
}
}
And the body:
<body onload='doit(900, 50, 0);'>
But it acts out crazy... What am I doing wrong?
All in all I would do something like this to create the slow down effect:
newspeed = speed*proc; //Now, proc is a number between 0 and 1.
//the closer proc is to 1, the smaller the decrease will be.
// when proc is 1 - there will be no decrease in speed
For example:
<body onload='doit(900, 50, 0.9);'>
I'm trying to implement a first person movement using the mouse.
I do have it working with keyboard yet I'm having difficulties implementing it using mouse since movement to a specific side isn't that clear (i.e moving left can include moving up or down).
I want to use the matrix3d in order to receive changed values of the position.
EDIT #2 Here is a jsfiddle.
EDIT I've pasted the new code I've managed to resolve:
$(document).on('mousemove', function (e) {
var MOVE = 10; // how much to move
var XTURN = 1; // how much to rotate
var YTURN = 1; // how much to rotate
var transformer, origMat, translationMatrix, result;
transformer = document.getElementById("transformer");
if ($.browser.webkit)
origMat = new WebKitCSSMatrix(window.getComputedStyle(transformer).webkitTransform);
//turn left
if (e.pageX < xPrev) {
if (XTURN < 0) {
XTURN *= -1;
}
xPrev = e.pageX;
//turn right
} else {
if (XTURN > 0) {
XTURN *= -1;
}
xPrev = e.pageX;
}
//look up
if (e.pageY < yPrev) {
if (YTURN < 0) {
YTURN *= -1;
}
yPrev = e.pageY;
//look down
} else {
if (YTURN > 0) {
YTURN *= -1;
}
yPrev = e.pageY;
}
translationMatrix = new WebKitCSSMatrix("matrix3d(" + cos(XTURN).toFixed(10) + ",0," + sin(XTURN).toFixed(10) + ",0,0,"+ cos(-YTURN).toFixed(10) +","+ sin(YTURN).toFixed(10) +",0, " + sin(-XTURN).toFixed(10) + ","+ sin(-YTURN).toFixed(10) +"," + cos(XTURN).toFixed(10) + ",0,0,0,0,1)");
transformer.style.webkitTransform = translationMatrix.multiply(origMat).toString();
});
As you can see (Sorry for the one line matrix) I'm stating the changes of the X and Y rotations on the same matrix change and then committing it, the issue now is with the cos(XTURN).toFixed(10) which can be related to the X and Y rotations, so you can see it works but not perfect.
Would appreciate any tips/ideas.
P.S I don't want to use the Pointer Lock API, even though it's great, since I want it to support maximal number of browsers.
Pure JavaScript is mostly better than libraries (unless it's a "Code less do more" thing),
since you can understand what your code really does.
This is my entire JavaScript code:
var velocity = 0.5;
document.onmousemove = function(e) {
var angleX = e.pageY * velocity * Math.PI / 180;
var angleY = e.pageX * velocity * Math.PI / 180;
document.getElementById('transformer').style.webkitTransform = 'matrix3d(' + Math.cos(-angleY) + ',0,' + Math.sin(-angleY) + ',0,' + (Math.sin(angleX)*Math.sin(-angleY)) + ',' + Math.cos(angleX) + ',' + (-Math.sin(angleX)*Math.cos(-angleY)) + ',0,' + (-Math.cos(angleX)*Math.sin(-angleY)) + ',' + Math.sin(angleX) + ',' + (Math.cos(angleX)*Math.cos(-angleY)) + ',0,0,0,0,1)';
};
And this is the fiddle.
It works!
(I even made an example of this using the Pointer Lock API: fiddle (click the square to begin)
Explanation:
First, a velocity variable to easily set the rotation speed.
Then, a mousemove event which has the two rotation variabls set.
The last line is to convert from rotateX and rotateY transformations, to matrix3d as requested.
This Stackoverflow question helped me get to the following solution.
rotateX(angleX) is equal to the following matrix:
1 0 0 0
0 cos(angleX) -sin(angleX) 0
0 sin(angleX) cos(angleX) 0
0 0 0 1
rotateY(angleY) is equal to the following matrix:
cos(angleY) 0 sin(angleY) 0
0 1 0 0
-sin(angleY) 0 cos(angleY) 0
0 0 0 1
And to use them both together, you need to multiply the two matrices.
So I wrote a small JavaScript tool to give me the calculation I need to do to get the result of this multiplication.
The result:
cos(angleY) sin(angleX)*sin(angleY) cos(angleX)*sin(angleY) 0
0 cos(angleX) -sin(angleX) 0
-sin(angleY) sin(angleX)*cos(angleY) cos(angleX)*cos(angleY) 0
0 0 0 1
And that's the way to convert rotateX and rotateY to matrix3d.
Hope it helps :)
It's not quite clear to me what your high level goal is. It sounds like you're trying to implement a Counterstrike-like game in JS and CSS. Which is awesome! For the rest of this answer, I'm going to assume that you are trying to do something like that.
Realistically, you must use the Pointer Lock API. Otherwise, you won't be able to turn around by only moving the mouse left. You'll hit the edge of the browser window and stop turning. The browser support isn't great, but it's by far a better experience for the gamer!
In order to render your world with CSS transforms, you need to do a complicated series of transforms to generate the matrix for every side of every object visible in the game world. This is because the browser's perspective is always looking directly along the Z axis. So in order to animate things "around" the viewer's eye, you have to translate and rotate them around. After a bit of poking around, I came to the conclusion that doing all the transforms in CSS is prohibitively slow (and complicated!). But never fear, there's another way! WebGL or Canvas to the rescue!
Take a look at Isaac Sukin's game Nemesis. It's an excellent example, and he's written a tutorial to come up with something similar! The library it's based on, Three.js, is very widely used and has a very understandable API. It takes almost all of the hard part out, and lets you just make a 3D world!
Good luck with the game!
Using quaternions is really easier. I found an implementation in Google closure library so I made an example (also, check the jsFiddle):
goog.require('goog.vec.Quaternion');
var velocity = 0.5;
var lastX = null;
var lastY = null;
var angleX = 0;
var angleY = 0;
$(document).on('mousemove', function (e) {
if (lastX == null) lastX = e.pageX;
if (lastY == null) lastY = e.pageY;
angleX += (e.pageX - lastX) * velocity * Math.PI / 180;
angleY += (e.pageY - lastY) * velocity * Math.PI / 180;
lastX = e.pageX;
lastY = e.pageY;
var quat = goog.vec.Quaternion.concat(
goog.vec.Quaternion.fromAngleAxis(angleX, [0, 1, 0], []),
goog.vec.Quaternion.fromAngleAxis(-angleY, [1, 0, 0], []), []);
var matrix = goog.vec.Quaternion.toRotationMatrix4(quat, []);
$("#transformer").css({
webkitTransform: "matrix3d(" + matrix.join(",") + ")"
});
});