I want to make a particle system for my home page. Blue little circular dots should go from left to right and then reapear in the left so that it makes a loop.
Image of particles
The code below will generate 150 dots that all have different properties as speed color and size and will apear at random positions when loading the page. Either by looping the animation or by adding new dots I want to continue the animation infinitely.
// ========== JAVASCRIPT ==========
function Dot(){
var colors = [
"#313146",
"#36364f",
"#3d3d5c",
"#404066"
];
var speed = Math.floor(Math.random() * 20) + 2;
this.obj = document.createElement("div");
this.obj.classList.add("dot");
this.obj.style.top = (window.innerHeight * Math.random()) + 'px'; // random Y-position after page load
this.obj.style.left = (window.innerWidth * Math.random()) + 'px'; // random X-position after page load
this.size = Math.floor(Math.random() * 5) + 4; // random size
this.obj.style.height = this.size + 'px';
this.obj.style.width = this.size + 'px';
this.obj.style.backgroundColor = colors[Math.floor(Math.random()*colors.length)]; // random color
this.obj.style.animation = `move ${speed}s linear`; // start animation
document.body.appendChild(this.obj);
setTimeout(del, speed*1000, this.obj); // THIS FUNCTION SHOULD BE REMOVED IF ANIMATION GETS A LOOP
function del(element) {
element.parentNode.removeChild(element);
};
};
for(var i = 0 ; i < 151 ; i++ ){ // creating 150 dots
new Dot();
};
// ========== CSS ==========
.dot {
border-radius: 50%;
z-index: -1;
}
#keyframes move {
0% {
transform: translateX(0vw);
}
100% {
transform: translateX(100vw);
}
}
My problem is that as the dots apear with random positions, and all of them get a transform: translateX(100vw);, they will move out of the screen for a while before being deleted or reapeared at the beginning. My second image shows in red where the dot is moving to, and where it should move to.
image
What I tried allready:
1.
JS:
this.obj.style.animation = `move ${speed}s linear infinite`; added infinite and deleted the code that deletes the dots.
CSS:
#keyframes move {
0% {
transform: translateX(0vw);
}
100% {
transform: translateX(right);
}
}
<= Does not exist, and couldn't find working code equal to this idea.
This would have been a solution.
2.
Adding a second animation with dots coming from the left when other ones where deleted.
Ended in a gap between the 150 dots of the first animation and the incoming dots of the second animation.
Is there any other possibility of moving the dots from left to right with different properties?
best regards
Since you are setting the position with JS so you can know exactly where each element will appear and use this information to adjust the animation.
Here is a basic example to illustrate:
.dot {
background: blue;
position:fixed;
width: 50px;
height: 50px;
border-radius: 50%;
z-index: -1;
left:var(--x,0px);
animation:move 2s linear infinite;
}
#keyframes move {
0% {
transform: translateX(0vw);
}
100% {
transform: translateX(calc(100vw - var(--x,0px)));
}
}
<div class="dot" style="top:10px;--x:80px;"></div>
<div class="dot" style="top:20px;--x:150px;"></div>
<div class="dot" style="top:100px;--x:350px;"></div>
The variable --x will define left and will get substracted from the 100vw
For better support and since you are using JS, you can get rid of calc() and CSS variables. Simply do a small calculation to find the value of transform.
Here is an example where I am using jQuery for simplicity but you can easily make it a JS-only code:
$('.dot').each(function() {
$(this).css('transform','translateX('+ ($(window).width() - parseInt($(this).css('left')))+'px)');
});
.dot {
background: blue;
position:fixed;
width: 50px;
height: 50px;
border-radius: 50%;
z-index: -1;
animation:move 2s linear infinite;
}
#keyframes move {
0% {
transform: translateX(0px);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="dot" style="top:10px;left:80px;"></div>
<div class="dot" style="top:20px;left:150px;"></div>
<div class="dot" style="top:100px;left:350px;"></div>
Worth to note that you need to update the value on window resize
Another idea to keep the loop effect is to have the same position and the same animation for all and you adjust the delay to simulate the different position:
.dot {
background: blue;
position:fixed;
width: 50px;
height: 50px;
border-radius: 50%;
z-index: -1;
left:0;
animation:move 2s linear infinite;
}
#keyframes move {
0% {
transform: translateX(0px);
}
100% {
transform: translateX(100vw);
}
}
<div class="dot" style="top:10px;animation-delay:-1s;"></div>
<div class="dot" style="top:20px;animation-delay:-0.1s;animation-duration:1s"></div>
<div class="dot" style="top:100px;animation-delay:-0.5s;animation-duration:4s"></div>
The calculation is easy. If the animation duration is D then a delay of -D/2 will place the element in the center intially. -D*0.1 will place the image at 10% and so on.
I would suggest you to use requestAnimationFrame to animate your particles. Take a look at the following example. I've added the move method to the particle which is called from the animation loop and changing the particle's position. It also checks if the particle has reached the end of the screen and resets its position to -10 in this case.
function Dot(){
var colors = [
"yellow",
"red",
"green",
"black"
];
this.x = window.innerWidth * Math.random();
this.speed = Math.floor(Math.random() * 20) + 2;
this.obj = document.createElement("div");
this.obj.classList.add("dot");
this.obj.style.position = "fixed";
this.obj.style.top = (window.innerHeight * Math.random()) + 'px';
this.obj.style.left = this.x + 'px';
this.size = Math.floor(Math.random() * 5) + 4; // random size
this.obj.style.height = this.size + 'px';
this.obj.style.width = this.size + 'px';
this.obj.style.background = colors[Math.floor(Math.random()*colors.length)]; // random color
document.body.appendChild(this.obj);
this.move = function() {
this.x += this.speed;
if (this.x > window.innerWidth) {
this.x = -10;
}
this.obj.style.left = this.x + 'px';
};
};
var dots = Array.apply(null, Array(150)).map(a => new Dot());
requestAnimationFrame(paint);
function paint() {
requestAnimationFrame(paint);
for (dot of dots) {
dot.move();
}
}
.dot {
border-radius: 50%;
z-index: -1;
}
I also recommend you this great book about particle systems. It shows how to implement forces, interaction and complex behavior.
Related
I don't know how to form the question. I have old animation that was done in Flash, that I'm not able to translate to CSS animation.
This is an animation of a Spinning Wheel.
The question is: How can I implement the CSS animation for the crank/handle that drives the wheel? It should work like a Piston but connected to a circe in id="rotator" (inside SVG). The handle is located at 0,0 on the image above. Both small circles should match when the rotator is rotating. I already tried to use translate, and used transform-origin but I have no idea how to implement the animation. I have Action Script code as a reference, but I don't know how to map it to CSS or SCSS.
I was able to convert SWF into SVG, using pyswf. And I have a hard time understanding the math behind the logic I've created long ago and translating it into CSS (I've also tried with JS approach).
The code in Action Script I've extracted long ago, probably some decompiler from the swf file.
// copyright (c) 2006 Jakub "JCubic" Jankiewicz
var
factor1 = 180.0 / Math.PI, // współczynniki dla zamiany
factor2 = Math.PI / 180.0; // radiany stopinie i odwrotnie
// radiany na katy
function rad2deg(rad){
return rad * factor1;
}
// katy na radiany
function deg2rad(deg){
return deg * factor2;
}
// dlugosc odcinka
function line_length(x1, y1, x2, y2){
return Math.sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
}
// funkcja testowa przenoszaca krzyzyk
function show(x, y){ cross._x = x; cross._y = y; }
// funkcja testowa rysująca linie
function line_(x1, y1, x2, y2, ang){
line._height = Math.sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
line._x = (x2 + x1) / 2;
line._y = (y2 + y1) / 2;
line._rotation = ang;
}
function getSpeed(){
return step;
}
function setSpeed(value){
// inicjacja zmiennych
var Ox = 0,
Oy = 360, // punkt zaczepienia
r = 83.0, // promien odleglosc od rotatora
angle = 0,
step = value,
rad = deg2rad(step), // cosinnus i sinus stopnia
sinA = Math.sin(rad), // jednostkowego
cosA = Math.cos(rad),
sinAlpha;
onEnterFrame = function() {
if (angle == 0) {
napedzacz._x = 0; // poczatek obrotu
napedzacz._y = r;
} else {
Bx = napedzacz._x * cosA + napedzacz._y * sinA;
By = napedzacz._y * cosA - napedzacz._x * sinA;
napedzacz._x = Bx;
napedzacz._y = By;
sinAlpha = (Ox - Bx) / line_length(Bx, By, Ox, Oy);
napedzacz._rotation = - rad2deg(Math.asin(sinAlpha));
kolo._rotation = 180 - angle;
szpulka._rotation = 180 - angle * 2;
rotator._rotation = 180 - angle;
}
angle = (angle+step) % 360;
}
}
It has Polish comments but they don't explain how to modify the code.
Here is my CodePen demo where I've tried to create SVG+CSS animation. I was able to rotate part of the spinning wheel, but don't know how to animate the handle (that is used to drive the wheel).
I've wanted to use SCSS and trigonometry functions (included in Pen) to generate every frame of the animation, but I'm not sure how I should go about it.
I have the original SWF file but I'm not able to play it, since the only SWF player I've found doesn't execute code. And I'm not able to install Gnash on Fedora (even that I've written article how to do that long go, the solution doesn't work anymore). That's why I want to create something modern with SVG.
If you have something to open the SWF file here is the link to the original file:
https://jcubic.pl/kolowrotek.swf
the problem is that constants in the Action Script code use different coordinate systems and different scales, I have no idea how to map that code into JavaScript (I was able to run it, but it has issues, there are two commented-out lines in Pen). I also have no idea why the animation rotate in different direction than CSS, I was not able to reverse it. I would prefer not to use JavaScript since it will hard to match CSS animation with JavaScript, and doing aninmation in JavaScript make some delay when handle is in wrong position.
This is a basic implementation of the movement that you want.
I have set only keyframes at every 90 deg , it will get better as you add values.
I have guessed this values, bout you should calculate them and set the appropiate values in the counter rotation.
#wheel {
width: 300px;
height: 300px;
border-radius: 100%;
border: solid 1px blue;
animation: rotate 15s infinite linear;
}
#dot {
width: 5px;
height: 5px;
border-radius: 100%;
border: solid 1px red;
position: absolute;
top: 2px;
left: 150px;
}
#arm {
width: 500px;
height: 5px;
border: solid 1px green;
position: absolute;
top: 2px;
left: 150px;
transform-origin: 0px 0px;
animation: crotate 15s infinite linear;
}
#keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
#keyframes crotate {
0% {
transform: rotate(15deg);
}
25% {
transform: rotate(-90deg);
}
50% {
transform: rotate(-195deg);
}
75% {
transform: rotate(-270deg);
}
99.99% {
transform: rotate(-345deg);
}
100% {
transform: rotate(15deg);
}
}
<div id="wheel">
<div id="dot"></div>
<div id="arm"></div>
</div>
Note, following answer content may be subject to edits in the near future, and is only intended as a starting point to assist the OP's author.
I believe what ya may need for turning the crank is the transform-origin CSS property.
In regards to rotate() direction, the StackExchange -- How to determine direction of rotation in CSS3 transitions? answers may be of use. TLDR; positive numbers rotate clockwise, and negative numbers rotate anticlockwise, however this may be modified via from configuration.
I'll dig into the source a bit over the next few minutes and attempt a more complete answer.
Updates
You were very close to a solution! From what I spotted the #handle element was missing an animation property/value, and the #keyframes handle_rotate needed defined.
Here's a bit of code that should get ya a little closer to a full solution...
:root {
--time: 5s;
--handle-translate-x: 291.59718px;
--handle-translate-y: 210.10511px;
--handle-rotate-start: 20deg;
--handle-transform-origin-x: 4.742px;
--handle-transform-origin-y: 15.799px;
}
/* ... */
#handle {
animation: handle_rotate var(--time) linear infinite;
transform-origin: var(--handle-transform-origin-x) var(--handle-transform-origin-y);
transform:
translate(var(--handle-translate-x), var(--handle-translate-y))
rotate(var(--handle-rotate-start));
}
/* ... */
#keyframes handle_rotate {
to {
transform:
translate(var(--handle-translate-x), var(--handle-translate-y))
rotate(calc(var(--handle-rotate-start) + 360deg));
}
}
... Though be aware values still need a bit of fiddling with to get all the parts to sync-up with one another. In this case I'd suggest leveraging CSS Custom Properties (variables)
The problem is solved when adding angles individually and then using ttheta(without calling a function to add angles and than using ttheta), but can anyone tell about why using function here is wrong or what problem is this function causing
The issue is solved by using this:
dtransform = window.getComputedStyle(leg1, null).getPropertyValue("transform");
values = dtransform.split('(')[1].split(')')[0].split(',');
dtheta = Math.round(Math.atan2(values[1], values[0]) * (180 / Math.PI));
dtransform1 = window.getComputedStyle(leg2, null).getPropertyValue("transform");
values1 = dtransform1.split('(')[1].split(')')[0].split(',');
dtheta1 = Math.round(Math.atan2(values1[1], values1[0]) * (180 / Math.PI));
ttheta = dtheta + dtheta1;
Instead of using function.
What I am trying to achieve is to get value of end points of an element when it is rotated from left and top of browser.
X & Y values are max-distance of end points of shoe
I get right values at some points and wrong at some. I tried to add angle from the parent element but that also don't solve the problem.
This is the related answer from which I had taken help
To check values are right or wrong I added an event to get clientX of mouse click. And values of element positions are taken when Try button is clicked.
Am I doing something wrong, any insights will be really helpful
let leg1 = document.querySelector(".Leg1Shoe")
let leg2 = document.querySelector(".Leg1Part")
let animeAll = document.querySelectorAll(".allClass")
let animePause = false
let ttheta = 0;
function getPos() {
if (!animePause) {
animeAll.forEach(e => {
e.classList.add("AnimatePaused");
})
animePause = true;
} else {
animeAll.forEach(e => {
e.classList.remove("AnimatePaused");
})
animePause = false;
}
let h, w, x, dx, tx, y, dy, ty = "";
leg1.style.outline = "1px solid red"
h = leg1.offsetHeight;
w = leg1.offsetWidth;
x = leg1.getBoundingClientRect().left;
y = leg1.getBoundingClientRect().top;
func2(leg2);
func2(leg1);
dx = (Number(h * (Math.sin(ttheta * (Math.PI / 180)))) + Number(w * (Math.cos(ttheta * (Math.PI / 180))))).toFixed(2);
dy = (Number(w * (Math.sin(ttheta * (Math.PI / 180)))) + Number(h * (Math.cos(ttheta * (Math.PI / 180))))).toFixed(2);
tx = (Number(x) + Number(Math.abs(dx))).toFixed(2);
ty = (Number(y) + Number(Math.abs(dy))).toFixed(2);
console.log("X:" + tx, "Y:" + ty);
}
function func2(e) {
let dtransform, dtheta, values = "";
dtransform = window.getComputedStyle(e, null).getPropertyValue("transform");
if (dtransform != "none") {
values = dtransform.split('(')[1].split(')')[0].split(',');
dtheta = Math.round(Math.atan2(values[1], values[0]) * (180 / Math.PI));
} else {
dtheta = 0;
};
ttheta = Number(ttheta) + Number(dtheta);
}
leg1.addEventListener('click', mousePos);
function mousePos(e) {
console.log("X:" + e.clientX, "Y:" + e.clientY)
}
.Leg1Part {
position: relative;
left: 100px;
top: 43px;
width: 20px;
height: 75px;
background-color: green;
transform-origin: top center;
animation: animateLeg1Part 5.0s linear infinite alternate;
}
#keyframes animateLeg1Part {
0% {
transform: rotate(40deg);
}
25% {
transform: rotate(25deg);
}
50% {
transform: rotate(10deg);
}
75% {
transform: rotate(30deg);
}
100% {
transform: rotate(60deg);
}
}
.Leg1Shoe {
position: absolute;
left: 0px;
top: 73px;
width: 40px;
height: 20px;
background-color: blue;
transform-origin: center left;
animation: animateLeg1Shoe 5.0s linear infinite alternate;
}
#keyframes animateLeg1Shoe {
0% {
transform: rotate(15deg);
}
50% {
transform: rotate(-20deg);
}
100% {
transform: rotate(30deg);
}
}
.AnimatePaused {
animation-play-state: paused;
}
<div class="Leg1Part allClass">
<div class="Leg1Shoe allClass"></div>
</div>
<button onclick="getPos()">Try</button>
Thanks for help in advance
This is not a simple answer that can give you a complete solution, but just an outline of the process you can follow.
You get the transformation matrices for the "leg" and the "shoe", as you already do, by calling getPropertyValue("transform") This gives you a string like this: matrix(-0.568718, 0.822533, -0.822533, -0.568718, 0, 0), this is a shortened form of a 3x3 transformation matrix:
| cos(theta) -sin(theta) 0 |
| sin(theta) cos(theta) 0 |
| 0 0 1 |
Parse the string and create a 2d array for this matrix. Note: since you don't have any translation (two zeros in the last column) you can operate on 2x2 matrices.
Multiple the transformation matrices for the "leg" and the "shoe". It's a tedious process, and you can use the mathjs library to help.
Multiply the resulting transformation matrix by the vector of each point's original coordinates. This will give you the coordinates of the point with all rotations applied.
Here are some references:
https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/matrix()
https://en.wikipedia.org/wiki/Affine_transformation
http://web.cse.ohio-state.edu/~shen.94/681/Site/Slides_files/transformation_review.pdf
https://en.wikipedia.org/wiki/Matrix_multiplication_algorithm
I have a simple JS script which listens to keyboard input and displays, at a random position, a short animation of every typed letter fading out and getting smaller.
'use strict'
const body = document.querySelector('body')
const ignoreKeys = [
'Alt', 'Shift', 'Control', 'CapsLock', 'Tab', 'Backspace', 'Escape', 'Meta',
'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'
]
document.addEventListener('keydown', function(e) {
if (!ignoreKeys.includes(e.key)) {
// Values 400 & 200 keep the div completely inside the window
const maxHeight = window.innerHeight - 400
const maxWidth = window.innerWidth - 200
const div = document.createElement('div')
div.className = 'anim'
div.textContent = e.key
div.style.top = getRandomInt(0, maxHeight) + 'px'
div.style.left = getRandomInt(0, maxWidth) + 'px'
body.append(div)
setTimeout(function() { div.remove() }, 3000)
}
})
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min) + min)
}
.anim {
position: fixed;
text-align: center;
animation-name: fade;
animation-duration: 2s;
animation-timing-function: ease-in-out;
opacity: 0%;
}
#keyframes fade {
0% { opacity: 100%; font-size: 300px;}
100% { opacity: 0%; font-size: 100px;}
}
By animating the font-size property, each letter gets smaller. However, since its "anchor point" is the top of the div, the visible effect is a letter getting smaller and moving slightly upwards. I would like each letter to shrink towards the vertical center of the div instead.
I can calculate the center the div easily and add the proper top coordinate to the #keyframe property, but I don't know how to modify that property in JS, individually for each div. Is this possible at all via CSS? Or should I rewrite the whole thing in pure JS?
You don't need to adjust the div's top value at all. As there is no border or anything else displayed for the DIV tag itself - just the letter within it - you can adjust either the margin, the border and/or the padding to achieve the same effect as increasing the top value for the DIV. As each of these can be handled within the css transition, you could do something like:
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min) + min)
}
document.addEventListener('keydown', function(e) {
const body = document.querySelector('body')
const ignoreKeys = [
'Alt', 'Shift', 'Control', 'CapsLock', 'Tab', 'Backspace', 'Escape', 'Meta',
'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'
]
if (!ignoreKeys.includes(e.key)) {
// Values 400 & 200 keep the div completely inside the window
const maxHeight = window.innerHeight - 400
const maxWidth = window.innerWidth - 200
const div = document.createElement('div')
div.className = 'anim'
div.textContent = e.key;
div.style.top = getRandomInt(0, maxHeight) + 'px'
div.style.left = getRandomInt(0, maxWidth) + 'px'
body.append(div)
setTimeout(function() { div.remove() }, 3000)
}
})
.anim {
display:block;
position: fixed;
text-align: center;
width:200px;
animation-name: fade;
animation-duration: 2s;
animation-timing-function: ease-in-out;
opacity: 0;
margin:0px;
}
#keyframes fade {
0% { opacity:1; font-size: 300px;}
100% { opacity:0; font-size: 100px; margin-top:100px;}
}
The initial state of the DIV is with margin:0px. Adding a margin-top setting to the keyframes css, increases this from 0 to 100 during the transition. The effect of that is to push the DIV down - and, as noted above, as nothing is being displayed for the DIV itself, the user will not see it move. Note that I have fixed the width of the DIV at 200px so ensure that everything is always centered horizontally - otherwise the DIV width is based on the width of the character, so would change during transition and the character would move to the left as the centre line changes. I've moved some of the code around to make it easier to test - but the only actual change is in the CSS styling. Also note that opacity is a value from 0 to 1, so should not be shown as a percentage.
UPDATE
Have a look at the following snippet. I think that it may be possible to have random font sizes AND random positions using transform rather than animate.
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min) + min)
}
const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".split("");
function zoomOUT(){
let d = document.getElementById("test");
d.classList.remove("zoomIN");
d.classList.add("zoomOUT");
}
function zoomIN(){
let d = document.getElementById("test");
let dletter = document.getElementById("testletter");
let t = getRandomInt(20, 60) * 10;
let l = getRandomInt(20, 100) * 10;
let fs = getRandomInt(10, 20) * 10;
dletter.innerHTML = letters[getRandomInt(0, 61)];
d.style.top = t + "px";
d.style.left = l + "px";
d.style.fontSize = fs + "%";
d.classList.remove("zoomOUT");
d.classList.add("zoomIN");
}
#test {
position:absolute;
padding: 50px;
margin: 0 auto;
text-align:center;
vertical-align:middle;
}
.zoomIN {
opacity:1;
transform: scale(3);
transition: transform 2s;
}
.zoomOUT {
opacity:0.5;
transform: scale(0.1);
transition: transform 3s;
}
<button onclick="zoomOUT();" z-index=1>Play</button><button onclick="zoomIN();" z-index=1>Restart</button>
<div id="test" class="zoomIN" style="top:300px; left:300px;" z-index=0><div id="testletter" style="font-size:600%; width:100%; height:100%">A</div></div>
Transform seems to keep things in the same place, so there is no need to adjust any top/margin/border/padding settings at all. In fact, the only things that change are the font-size (using scale(..)) and opacity. The size of the font is determined by the code. Note that this requires the character to be in a div within a div. This is just a test, but should give you enough to convert things into your code requirements.
I am currently trying to figure out a way to rotate an object along axis relative to the user and not relative to the object. I'm doing this with Javascript (jQuery) and CSS transform rotate.
I have built a codepen for to test it out.
var bX = 0; //bufferX (user scroll input)
var bY = 0; //bufferY (user scroll input)
var cX = 0; //currentX (effective X rotation)
var cY = 0; //currentY (effective Y rotation)
var bBX = 0; // same second rotation properties
var bBY = 0;
var cBX = 0;
var cBY = 0;
var cBZ = 0; // currentZ (effective Z rotation)
var pi = 3.1416
// Fidget animation
function fidget() {
if (!(bX + bY == 0)) {
var dX = bX * 0.1; // how much we rotate this frame
var dY = bY * 0.1;
cX = (cX + dX) % (2 * pi); // set rotation value
cY = (cY + dY) % (2 * pi);
bX -= dX; // update buffer
bY -= dY;
}
$('#one').css('transform', 'rotateX(' + cX + 'rad) rotateY(' + cY + 'rad)');
window.requestAnimationFrame(fidget);
}
function fidgetB() {
if (!(bBX + bBY == 0)) {
var dBX = bBX * 0.1;
cBX = (cBX + dBX) % (2 * pi);
var rBY = Math.cos(cBX); //Y ratio
var rBZ = Math.sin(cBX); //Z ratio
var dBY = bBY * 0.1; // deltaY
var dBZ = bBY * 0.1;
cBY = (cBY + rBY * dBY) % (2 * pi); // current
cBZ = (cBZ - rBZ * dBZ) % (2 * pi);
bBX -= dBX; // buffer
bBY -= (dBY);
}
$('#two').css('transform', 'rotateX(' + cBX + 'rad) rotateY(' + cBY + 'rad) rotateZ(' + cBZ + 'rad)');
window.requestAnimationFrame(fidgetB);
}
var init = function () {
// Fidget
requestAnimationFrame(fidget);
requestAnimationFrame(fidgetB);
// scroll detection script
!function(window,document){var prefix="",_addEventListener,support;function _addWheelListener(elem,eventName,callback,useCapture){elem[_addEventListener](prefix+eventName,"wheel"==support?callback:function(originalEvent){!originalEvent&&(originalEvent=window.event);var event={originalEvent:originalEvent,target:originalEvent.target||originalEvent.srcElement,type:"wheel",deltaMode:"MozMousePixelScroll"==originalEvent.type?0:1,deltaX:0,deltaY:0,deltaZ:0,preventDefault:function(){originalEvent.preventDefault?originalEvent.preventDefault():originalEvent.returnValue=!1}};return"mousewheel"==support?(event.deltaY=-.025*originalEvent.wheelDelta,originalEvent.wheelDeltaX&&(event.deltaX=-.025*originalEvent.wheelDeltaX)):event.deltaY=originalEvent.deltaY||originalEvent.detail,callback(event)},useCapture||!1)}window.addEventListener?_addEventListener="addEventListener":(_addEventListener="attachEvent",prefix="on"),support="onwheel"in document.createElement("div")?"wheel":void 0!==document.onmousewheel?"mousewheel":"DOMMouseScroll",window.addWheelListener=function(elem,callback,useCapture){_addWheelListener(elem,support,callback,useCapture),"DOMMouseScroll"==support&&_addWheelListener(elem,"MozMousePixelScroll",callback,useCapture)}}(window,document);
window.addWheelListener(window, function (e) {
e.preventDefault();
bY -= e.deltaX / window.innerWidth;
bX += e.deltaY / window.innerWidth;
bBY -= e.deltaX / window.innerWidth;
bBX += e.deltaY / window.innerWidth;
});
};
jQuery(document).ready(function ($) {
init();
});
html, html * {
margin: 0;
padding: 0;
-webkit-box-sizing: border-box;
box-sizing: border-box;
color:white;
text-align: center;
font-size: 6vmin;
font-family: sans-serif;
}
#intro {
position:absolute;
width: 100%;
height: 100%;
-webkit-perspective: 200vmax;
perspective: 200vmax;
-webkit-perspective-origin: 50% 50%;
perspective-origin: 50% 50%;
transform-style: preserve-3d;
}
#one {
left: 33%;
}
#two {
left: 66%;
}
.square {
background-color: black;
width: 50vmin;
height: 50vmin;
line-height: 50vmin;
top: 50%;
margin-top: -25vmin;
margin-left: -25vmin;
transform-style: preserve-3d;
position: absolute;
-webkit-backface-visibility: visible;
backface-visibility: visible;
-webkit-transform-origin: 50% 37%;
transform-origin: 50% 37%;
/* -webkit-animation: rotate 25s linear infinite;
animation: rotate 25s linear infinite; */
pointer-events: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="intro">
<div id="one" class="square">Object</div>
<div id="two" class="square">User</div>
</div>
[https://codepen.io/rsepierre/pen/XqVJKR][1]
To control the rotation of any of those square you scroll with the mousewheel/touchpad (shift+wheel to scroll Y axis. Would add touchsupport when sorted out)
The left square's rotation is relative to the object:
if you only scroll left<->right, no problem, it works. Same for up<->down.
Now if you lay the item flat (scroll 90° down<->up ) and then you try to rotate on the other axis ( left<->right ), it will rotate on itself. Which mean that to the user it will appear like a Z-axis rotation (like a clock).
The right square's rotation is my attempt to make the rotation
relative to the user :
if you scroll left<->right, no problem, it works. Same for up<->down.
if you lay the item flat again (scroll 90° down<->up ) and then you try to rotate on the other axis ( scroll left<->right ), it will rotate relative to the user.
The item rotates on the "Z" axis, but will appears like a Y axis rotation from the users perspective.
And it is actualy working :
IF you only make scroll up<->down by 90° steps and never get in between.
Even if you get a 89° angle instead of a 90° angle when you scroll up<->down, it will slowly become a mess.
My best bet for a solution :
I came to understand that 3D rotations are not commutative (the order in which you apply rotations will impact the final result, this is why only the Y-axis rotation gets screwed up, and the X-axis rotation never does).
One easy fix would be to just add each new user input into a new rotation (rotateX(100) rotateY(5) rotateY(20) rotateX(105)... indefinitly.
But obviously i'm doing this 60times per second, it would very quickly become the worste CPU/GPU leek you've seen in a while.
Because of this I believe that somehow I should do the all the math behind the scene in JS and apply only one rotate3D(X,Y,Z,angle) css rotation.
Thing is I don't know anything about the math behind rotation matrices.
I found this converter to translate 3D rotation things into others 3D rotation things, but I think I would need to understand what is what to begin to do any math.
Any help much apreciated.
Found this stackoverflow post. It's not exactly the same situation but really the same problem.
Will post updated code when I get there.
meanwhile :
Rotating CSS cube on fixed axes
I used Javascript to translate via transform a circle from the center of the canvas to the upper left. What I want to do next is call a function that picks random coordinates within the canvas and sends them to translate, so its position can be shifted. Unfortunately this is not working.
Can you only call translate once on an element within CSS? This is the conclusion I'm coming to but I haven't been able to find information in the docs say this type of behavior isn't allowed.
The heart of the matter:
function change_level() {
var level = document.getElementById("level");
level.parentNode.removeChild(level);
var ball = document.getElementById("init_pos");
ball.style.backgroundColor = "orange";
ball.style.borderRadius = "25px";
ball.style.transform = "translate(-600%, -647%)";
setTimeout(ball_movement(ball), 3000);
ball.style.transition = "background-color 2s ease-in, transform 3s ease";
}
function ball_movement(ball) {
var movements = 5;
var x;
var y;
for (var i = 0; i < movements; i++) {
x = getRandomArbitrary(-800, 800);
y = getRandomArbitrary(-800, 800);
ball.style.transform = "translate("+x+", "+y+")";
ball.style.transition = "transform 3s ease";
console.log(x);
}
}
Posted my code on jsfiddle, though my calculations are bigger than the campus in jsfiddle and so don't work properly.
https://jsfiddle.net/2c5gwbcd/
There are a couple of corrections needed to your code:
When setting the transform value within ball_movement, the x and y variables have merely numbers as value but the translate function needs a value with units (percentage, pixels etc). So, add it by appending px or % to the string as appropriate.
In the timeout function call, when you give the first param as ball_movement(ball) the function gets called immediately. You should wrap it within an anonymous function.
Note: In the below snippet, I had reduced the initial value of the translate function and the input for the random number calculation to keep the ball movement within boundaries.
window.onload = function() {
var
html_display = {
0: "Level One",
1: "Level Two",
2: "Level Three",
3: "Level Four",
4: "Level Five"
};
html_key = 0;
//need to take level offscreen, add ball
function change_level() {
var level = document.getElementById("level");
level.parentNode.removeChild(level);
var ball = document.getElementById("init_pos");
ball.style.backgroundColor = "orange";
ball.style.borderRadius = "25px";
ball.style.transform = "translate(-150%, -150%)";
ball.style.transition = "background-color 2s ease-in, transform 3s ease";
setTimeout(function() {
ball_movement(ball);
}, 3000);
}
function ball_movement(ball) {
var movements = 5;
var x;
var y;
for (var i = 0; i < movements; i++) {
x = getRandomArbitrary(-100, 100);
y = getRandomArbitrary(-100, 100);
ball.style.transform = "translate(" + x + "px, " + y + "px)";
ball.style.transition = "transform 3s ease";
}
}
function getRandomArbitrary(min, max) {
return Math.random() * (max - min) + min;
}
function intro_html() {
document.getElementById("level").innerHTML = html_display[html_key];
setTimeout(change_level, 1000);
}
intro_html();
}
body {
position: absolute;
top: 45%;
left: 50%;
-moz-transform: translateX(-50%) translateY(-50%);
-webkit-transform: translateX(-50%) translateY(-50%);
transform: translateX(-50%) translateY(-50%);
}
#level {
font-family: helvetica;
font-size: 29px;
position: absolute;
top: 45%;
left: 50%;
-moz-transform: translateX(-50%) translateY(-50%);
-webkit-transform: translateX(-50%) translateY(-50%);
transform: translateX(-50%) translateY(-50%);
}
#init_pos {
position: absolute;
top: 44%;
left: 48.17%;
height: 50px;
width: 50px;
}
.container {
height: 700px;
width: 1100px;
top: 45%;
left: 50%;
border: 4px solid black;
overflow: hidden;
}
<div class="container">
<p id="level"></p>
<p id="init_pos"></p>
</div>