CSS3 animation on transform: rotate. Way to fetch current deg of the rotating element? - animation

i am working on a html5 interface wich uses drag and drop. While i am dragging an element, the target gets a css-class, which makes it bidirectionally rotate due to a -webkit-animation.
#-webkit-keyframes pulse {
0% { -webkit-transform: rotate(0deg); }
25% { -webkit-transform:rotate(-10deg); }
75% { -webkit-transform: rotate(10deg); }
100% { -webkit-transform: rotate(0deg); }
}
.drag
{
-webkit-animation-name: pulse;
-webkit-animation-duration: 1s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-timing-function: ease-in-out;
}
When I drop the target, I want it to adopt the current state of rotation.
My first thought was to check the css property with jquery and the .css('-webkit-transform') method. But this method just returns 'none'.
So my question: Is there a way to get the current degree value of an element which is rotated via animation?
Thanks so far
Hendrik

I recently had to write a function that does exactly what you want! Feel free to use it:
// Parameter element should be a DOM Element object.
// Returns the rotation of the element in degrees.
function getRotationDegrees(element) {
// get the computed style object for the element
var style = window.getComputedStyle(element);
// this string will be in the form 'matrix(a, b, c, d, tx, ty)'
var transformString = style['-webkit-transform']
|| style['-moz-transform']
|| style['transform'] ;
if (!transformString || transformString == 'none')
return 0;
var splits = transformString.split(',');
// parse the string to get a and b
var parenLoc = splits[0].indexOf('(');
var a = parseFloat(splits[0].substr(parenLoc+1));
var b = parseFloat(splits[1]);
// doing atan2 on b, a will give you the angle in radians
var rad = Math.atan2(b, a);
var deg = 180 * rad / Math.PI;
// instead of having values from -180 to 180, get 0 to 360
if (deg < 0) deg += 360;
return deg;
}
Hope this helps!
EDIT I updated the code to work with matrix3d strings, but it still only gives the 2d rotation degrees (ie. rotation around the Z axis).

window.getComputedStyle(element,null)['-webkit-transform'] will return a string representing current transformation matrix.
You can calculate its rotation degree from that string by first parsing it, then applying a few maths on it.

Related

Position circles around a circle given x circles with fixed diameter in SASS

A question of two parts:
End goal is something like this, ala graph DB visualisers - but in html / css/sass
Part 1: How to position x number of circles around a circle so the edges touch (or prefereably, with a little whitespace).
For example, this is what i'm going for given 3, 6 and 7 circles.
I'm trying to get it working using SASS, however if there's a library or something that does what I'm after i'd rather use that - i'm just struggling to formulate the search phrase.
I'm using the trig functions from here, and stole the circle arrangement from here.
CODEPEN of what I've got so far.
I'm bad at maths, but some friends gave me the fomula you'll find below that should work out the distance to the outer circle center. $distance: tan((180-($angle))/2) * $radius;. However its not doing as i expect - given 6 circles, with a diameter of 100 I'd expect an output of 100, but i'm getting 86.602...
Here's the sass - probably easier to look in the codepen though.
#function strip-unit($number) {
#if type-of($number) == 'number' and not unitless($number) {
#return $number / ($number * 0 + 1);
}
#return $number;
}
#mixin on-circle($item-count, $circle-size, $item-size, $break-at) {
position: relative;
height: $circle-size;
padding: 0;
border-radius: 50%;
list-style: none;
>* {
display: block;
position: absolute;
top: 50%;
left: 50%;
width: $item-size;
height: $item-size;
margin: -($item-size / 2);
$angle: (360 / $break-at);
$rot: 0;
$prevLayer: 0;
#for $i from 1 through $item-count {
$layer: ceil($i/ $break-at);
$layerMinusOne: $layer - 1;
// MoveX figured out by aligning stuff by eye
// item-count 3 4 5 6 7 8 9 10 ...12 13 14...20
// moveX (%) 57 70 85 100 115 130 145 160 192 207 225 315
$item-radius: strip-unit($item-size) / 2;
// !! This is where i'm having trouble
$distance: tan((180-($angle * 1deg))/2) * $item-radius;
#debug "tan((180-#{$angle})/2) * #{$item-radius} = #{$distance}";
$moveX: ( $distance / strip-unit($item-size)) * 100 * 1%;
#debug "moveX: #{$moveX}";
#if $layer != $prevLayer {
$prevLayer: $layer;
$rot: $rot + $angle/2;
}
&:nth-of-type(#{$i}) {
transform:
// !! This is where the 'percent of circle diameter' measurements come in, translateX uses the size of the element being transformed when % is used.
rotate($rot * 1deg) translateX($moveX * $layer) rotate($rot * -1deg);
}
$rot: $rot+$angle;
}
}
}
$numOfCircles: 3; // <- Change me in the codepen
.circle-container {
#include on-circle($item-count: 28, $circle-size: 200px, $item-size: 50px,
$break-at: $numOfCircles);
margin: 5em auto 5em;
border: solid 5px red;
.around-a-circle {
text-align: center;
border-radius: 50%;
border: solid 1px #118be1;
}
}
Part 2: The extra layers.
My end goal as seen above is to display x number of circular elements in rings, where the inner most ring is made up of y elements and bubbles out from there.
As i said i'd rather use a library, but i couldn't find anything that does what i want. I was going to use the inner ring as a starting point and each alternating layer, rotate an extra bit and place the element nestled between the previous rings elements, but again i'm struggling with the dimensions, and how much to offset by.
$layer: ceil($i/ $break-at);
...
#if $layer != $prevLayer {
$prevLayer: $layer;
$rot: $rot + $angle/2;
}
The above code does that, mostly, however the spacings aren't optimised and the whitespace is too much compared to my end goal photo.
The distance needs to be between center of circle 1 and center of circle 2 and half that is your radius for all the circles.
I don't know sass so here is a working JS version as proof that this works.
https://jsfiddle.net/hk5spy20/
If you change line 32-33 from
var x1 = pointXY[0].x;
var y1 = pointXY[0].y;
to
var x1 = xPos;
var y1 = yPos;
You will replicate what you are currently doing which results in overlapping circles.
***** Added *****
The way you have the 2nd, 3rd, 4th level bubbles working will become gappy as you expand out because of the radius of the circle increases in size.
It won't work this way.
I have something but require work but I have this so far... https://jsfiddle.net/hd7qp06b/2/
I think for each row you will need two different set of formulas to get this working perfectly, the second row works, the 3rd row doesn't which is where a new formula is required. Will come back to this.
This seems to work: https://jsfiddle.net/eo170zsu/
You have to keep a track of pair adjacent bubbles and attachBubble next to it. If you put the co-ordinate of bubble 9 and 15 on the stack, it'll place new bubble perfectly next to it. However, you can't put bubble 9 and 16 on stack as well because this would cause an overlap. There is a sequence of pairs that are safe and probably be consistent for certain levels, I suspect odd / even levels has different rules for pairing.
Actually, thinking about it, just pair 9,15 and 9,16 process each one and if there is an overlap between two circles on screen, throw it away and try next pair.

Can you "bend" a SVG around bezier path while animating?

Is there any way to "bend" an SVG object as its being animated along a bezier path? Ive been using mostly GSAP for animating things. The effect would look something like this: https://www.behance.net/gallery/49401667/Twisted-letters-2 (the one with the blue pencil). I have managed to get the red arrow to animate along the path but the shape stays the same the whole time. Id like for it to follow along the green path and bend as it goes around the curve so that at the end of the animation it has the shape of the purple arrow. Here is the codepen.
GSAP code:
var motionPath = MorphSVGPlugin.pathDataToBezier("#motionPath", {align:"#arrow1"});
var tl1 = new TimelineMax({paused:true, reversed:true});
tl1.set("#arrow1", {xPercent:-50, yPercent:-50});
tl1.to("#arrow1", 4, {bezier:{values:motionPath, type:"cubic"}});
$("#createAnimation").click(function(){
tl1.reversed() ? tl1.play() : tl1.reverse();
});
Is there a way to do this with just GSAP? Or will I need something like Pixi?
This is how I would do it:
First I need an array of points to draw the arrow and a track. I want to move the arrow on the track, and the arrow should bend following the track. In order to achieve this effect with every frame of the animation I'm calculating the new position of the points for the arrow.
Also: the track is twice as long as it seams to be.
Please read the comments in the code
let track = document.getElementById("track");
let trackLength = track.getTotalLength();
let t = 0.1;// the position on the path. Can take values from 0 to .5
// an array of points used to draw the arrow
let points = [
[0, 0],[6.207, -2.447],[10.84, -4.997],[16.076, -7.878],[20.023, -10.05],[21.096, -4.809],[25.681, -4.468],[31.033, -4.069],[36.068, -3.695],[40.81, -3.343],[45.971, -2.96],[51.04, -2.584],[56.075, -2.21],[60.838, -1.856],[65.715, -1.49],[71.077, -1.095],[75.956, -0.733],[80, 0],[75.956, 0.733],[71.077, 1.095],[65.715, 1.49],[60.838, 1.856],[56.075, 2.21],[51.04, 2.584],[45.971, 2.96],[40.81, 3.343],[36.068, 3.695],[31.033, 4.069],[25.681, 4.468],[21.096, 4.809],[20.023, 10.05],[16.076, 7.878],[10.84, 4.997],[6.207, 2.447],[0, 0]
];
function move() {
requestAnimationFrame(move);
if (t > 0) {
t -= 0.001;
} else {
t = 0.5;
}
let ry = newPoints(track, t);
drawArrow(ry);
}
move();
function newPoints(track, t) {
// a function to change the value of every point on the points array
let ry = [];
points.map(p => {
ry.push(getPos(track, t, p[0], p[1]));
});
return ry;
}
function getPos(track, t, d, r) {
// a function to get the position of every point of the arrow on the track
let distance = d + trackLength * t;
// a point on the track
let p = track.getPointAtLength(distance);
// a point near p used to calculate the angle of rotation
let _p = track.getPointAtLength((distance + 1) % trackLength);
// the angle of rotation on the path
let a = Math.atan2(p.y - _p.y, p.x - _p.x) + Math.PI / 2;
// returns an array of coordinates: the first is the x, the second is the y
return [p.x + r * Math.cos(a), p.y + r * Math.sin(a)];
}
function drawArrow(points) {
// a function to draw the arrow in base of the points array
let d = `M${points[0][0]},${points[0][1]}L`;
points.shift();
points.map(p => {
d += `${p[0]}, ${p[1]} `;
});
d += "Z";
arrow.setAttributeNS(null, "d", d);
}
svg {
display: block;
margin: 2em auto;
border: 1px solid;
overflow: visible;
width:140vh;
}
#track {
stroke: #d9d9d9;
vector-effect: non-scaling-stroke;
}
<svg viewBox="-20 -10 440 180">
<path id="track" fill="none"
d="M200,80
C-50,280 -50,-120 200,80
C450,280 450,-120 200,80
C-50,280 -50,-120 200,80
C450,280 450,-120 200,80Z" />
<path id="arrow" d="" />
</svg>

2d circle rect collision and reflection doesnt work

I have game with map built by rectangles, darker rectangles (named "closed") mean its place where balls should be able to move, ball should reflect from the lighter rectangles(named "open") border. In future I'll add more balls and they will reflect from each other.
The problem is with new Vector after collision.
I force function circleRectGetCollisionNormal() to return vector(-1,0) what i think its normal for this case (ball is moving in right direction).
Ball is starting with degrees and change it simply to vector, this reflection worked for 45 degrees but when I change angle to 10 degrees ball moved into lighter rectangles(named "open").
Here is how it looks like (Picture)
I'm doing like this:
1-check if ball collided with lighter rectangle,
2-if it collided, I want to change direction so I return vector, for example for right side of ball colliding with rectangle return [-1,0] (I think its normal of vertical line, and its pointing left direction).
3-calculate new ball move Vector from this equation: newMoveVector = oldMoveVector − (2 * dotProduct(oldMoveVector, normalVector) * normalVector)
Here is code for each step:
1.
circleRect(circlePos, circleSize, rectPos, rectSize) {
//its rectRect collision but it doesnt matter because reflection surface is always horizontal or vertical
let r1 = {
left: circlePos.x - circleSize.x/2,
right: circlePos.x + circleSize.x/2,
top: circlePos.y - circleSize.y/2,
bottom: circlePos.y + circleSize.y/2
};
let r2 = {
left: rectPos.x,
right: rectPos.x + rectSize.x,
top: rectPos.y,
bottom: rectPos.y + rectSize.y
};
return !(r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top);
}
isOnOpenTile(pos: Vector, size: Vector) {
let openTiles = this.getTiles('open');
let result = false;
openTiles.forEach(element => {
if( this.circleRect(pos,size,element.pos,element.size) ){
result = element;
return;
}
});
return result;
}
2.
circleRectGetCollisionNormal(c, r) {
if(c.pos.y <= r.pos.y - (r.size.y/2)) return new Vector(0,-1);
//Hit was from below the brick
if(c.pos.y >= r.pos.y + (r.size.y/2)) return new Vector(0,1);
//Hit was from above the brick
if(c.pos.x < r.pos.x) return new Vector(1,0);
//Hit was on left
if(c.pos.x > r.pos.x) return new Vector(-1,0);
//Hit was on right
return false;
}
3.
getNewMoveVector(moveVector, normalVector) {
normalVector = this.normalize(normalVector);
let dot = (moveVector.x * moveVector.y) + (normalVector.x * normalVector.y);
let dotProduct = new Vector(dot, dot);
return moveVector.sub(dotProduct.mult(normalVector).mult(new Vector(2,2)));
}
normalize(v) {
let length = Math.sqrt((v.x*v.x) + (v.y*v.y));
return new Vector(v.x/length,v.y/length);
}
And here is main function for this
getMoveVectorOnCollision(circle) {
let coll = this.isOnOpenTile( circle.pos, circle.size );
if( coll != false) {
let vector = this.circleRectGetCollisionNormal(circle, coll);
return this.getNewMoveVector(circle.moveVector, vector);
} else return false;
}
Object Vector always contain 2 values all of function (mult, sub, div, add) work like here.
sub(vector: Vector) {
return new Vector(this.x - vector.x, this.y - vector.y);
}
Please give me advice, actual solution or tell about different way to do this reflection. I wasted more than 3 days trying to solve this, I have to move on.
Yor dot product calculation is erroneous. Change these lines:
let dot = (moveVector.x * moveVector.y) + (normalVector.x * normalVector.y);
let dotProduct = new Vector(dot, dot);
by this one line:
let dotProduct = (moveVector.x * normalVector.x + moveVector.y * normalVector.y);
Note that dotProduct is scalar value, not vector, so you have to make vector for subtraction as
subvec.x = 2 * dotProduct * normalVector.x
subvec.y = 2 * dotProduct * normalVector.y
and
return moveVector.sub(subvec);

Processing Bullets interaction

Hi How do I make bullets to collide with the objects in Processing ?
Bullets are fired and being translated and rotated
but whenever i try to use function dist() it always gives me 0 as the position of the vector
How do i get the correct vector position if i want the bullet to collide with objects using distance and make the the other object disappear ?
Here's the code
void move(){
passed = passed + time;
if (passed > bulletLife) {
alive = false;
}
forward.x = sin(theta);
forward.y = -cos(theta);
float speed = 15.0f;
velocity = PVector.mult(forward, speed);
side.add(forward);
void display(){
pushMatrix();
translate(side.x, side.y);
rotate(theta);
stroke(255);
ellipse(side.x, side.y, 30, 30);
popMatrix();
Thanks
You're getting 0 from dist() because translate() moves the coordinate system! I think, more than your question, you need to reconsider your code overall. You translate to side.x, side.y (which will then be 0,0 until you call popMatrix()) but then you draw the ellipse at side.x, side.y which is offset from its actual position.
In other words: if the position is 100,200, you're actually drawing the object at 200,400!
If you skip the translate() part, you can use this to draw your object:
void display() {
stroke(255);
ellipse(side.x, side.y, 30,30);
}
And this to check collision:
if (dist(side.x, side.y, bullet.x, bullet.y) == 0) {
collision = true;
}
else {
collision = false;
}
You can also see my collision-detection functions for Processing, which have lots of examples that might help.

Accelerated Ball leaving screen (Processing 2.1)

It is supposed to be a balloon going up, stoping when the window ends. I cannot understand why my ball eventually disappears... Somebody help me.... When I try to tune the gravity parameter or the accelereation one, it either starts moving crazy fast (and dissapears), or real slow (eventually dissapears too). I have the checkEdges(), method defined so that this does not happen. Obviously something is not right
Mover mover;
void setup(){
size(300,600);
background(255);
mover = new Mover();
}
void draw(){
background(255,50);
mover.applyForceWind();
mover.applyForceGravity();
mover.update();
mover.display();
mover.checkEdges();
}
class Mover {
PVector location;
PVector velocity;
PVector acceleration;
PVector gravity;
PVector wind;
Mover(){
location = new PVector(width/2,height*0.9);
velocity = new PVector(0,0);
acceleration = new PVector(0,-1);
}
void applyForceWind(){
PVector wind = new PVector(0.001,0);
acceleration.add(wind);
}
void applyForceGravity(){
PVector gravity = new PVector(0,0.1);
acceleration.add(gravity);
}
void update(){
velocity.add(acceleration);
location.add(velocity);
acceleration.mult(0);
}
void display(){
stroke(0);
fill(155);
ellipse(location.x,location.y,30,30);
}
void checkEdges(){
if ( location.x > width || location.x < 0){ velocity.x *= (-1);}
if ( location.y > height || location.y < 0){ velocity.y *=(-1);}
}
}
You had an extra close curly brace in your code, just FYI. The problem with the ball eventually getting "stuck" on the floor and disappearing is due to your checkEdges() code, as you suspected. To shed some light on the issue, try this code:
void checkEdges(){
if ( location.x > width || location.x < 0){ velocity.x *= (-1);}
if ( location.y > height || location.y < 0){
velocity.y *=(-1);
println("velocity: " + velocity.y + "; position: " + location.y);
}
}
And it will give something like this:
...
velocity: -2.3999999; position: 602.1
velocity: -2.3; position: 602.1
velocity: -2.2; position: 602.1
velocity: -2.1000001; position: 602.1
velocity: 2.0000002; position: 600.1
velocity: -2.1000001; position: 602.19995
velocity: 2.0000002; position: 600.19995
velocity: -2.1000001; position: 602.2999
velocity: 2.0000002; position: 600.2999
...
As you can see, the y-velocity at each successive "bounce" is slightly lower than the one before it, meaning that your system is bleeding energy. In particular, when the velocity becomes too low to get it back above the bottom of the screen (it goes below 2.1, which how far it has to go to get back up), it starts behaving erratically. Imagine it goes for a while, and then the next time it has to bounce, it only has a y velocity of -2. It's at y=602.1 > 600, so multiply y velocity by negative one. On the next iteration, it moves up 2 units, so y is now 600.1, but that is still off the screen so it inverts the velocity again! Here's a (crude) picture to illustrate the idea:
The red lines represent the iteration after the velocity was reversed. The length of each red line decreases as time goes on (the ball doesn't bounce quite as high), and eventually it doesn't actually get back above the barrier! At this point, it's still below the bottom of the screen, and so the inversion test still passes, so it turns the positive velocity negative, while it's still below the bottom of the screen.
There are a few ways to fix this:
Prevent the ball from losing energy as time goes on. This can be tricky to do. Here's a clue: if you swap the order of these two lines in your update() function, the ball will gain energy as time goes on! An interesting result.
location.add(velocity);
velocity.add(acceleration);
It doesn't make sense to allow the ball to go below the edge of the window anyway, so if it is there, then force it back up to the edge, ie insert this into your checkEdges() method:
location.y = height;
When I use the second bullet, I get a constant bounce response and no loss of speed as time goes on:
velocity: -3.5999987; position: 600.0
velocity: -3.5999987; position: 600.0
velocity: -3.5999987; position: 600.0
velocity: -3.5999987; position: 600.0
velocity: -3.5999987; position: 600.0
velocity: -3.5999987; position: 600.0
Also, a side note: there is no need to create new wind and gravity PVectors each time you call the methods. In fact, you even declared them as class variables anyways! Set their values in the constructor and then you can use them in your other methods.
Dude, acceleration is constant, but you're adding to it every iteration....

Resources