@mapagella came to me with a question. A friend of his was trying to do ray tracing with CSS3. He wanted to rotate in 3D a <div>
element shaped like a line so that it started and ended in specific points in space.
It turns out that CSS3 defines a rotate3d()
transform function that allows you to rotate an HTML element a certain angle around a certain direction in 3D space. The function looks like rotate3d(x, y, z, angle)
where x
, y
and z
define the direction around which the element will be rotated. The question then is what direction and which angle to use.
Let's start by defining what our line will be. We'll use a div
element with a line
class: <div class="line"></div>
. And we'll define line
as:
.line {
background: green;
height: 1px;
}
Afterwards we'll set the width of the line to the length of a vector. That means that the line as a vector will initially have the coordinates (length,0,0)
.
But before jumping to the math we must set one extra CSS rule. CSS transformations act on a "transform origin" because HTML elements are all boxes. Since we want this box to look and act like a line, we have to first tell CSS to set the origin at the left of the box:
.line {
background: green;
height: 1px;
transform-origin: left center;
}
Now that our div
behaves like a vector we can start figuring out how to rotate it. Let's say we want to position it so that the line starts at v1 = (x1,y1,z1)
and ends at v2 = (x2,y2,z2)
.
The vector that sets the desired direction of the line will be dv = v2-v1 = (dx,dy,dz)
. We want to rotate the original line so that it matches this vector. That means that we want to rotate in the plane defined by the line and dv
. This also means that the axis around which we'll be rotating the line is perpendicular to the plane. We can obtain the axis by calculating the cross product between the line and dv
:
normal = line x dv
= (length,0,0) x (dx,dy,dz)
= (0,-length*dz,length*dy)
We already know that length
is the norm of dv
: length = sqrt(dx^2+dy^2+dz^2)
. So the only thing left is the angle between the line and dv
. We can calculate it using a property of the dot product:
A . B = |A| |B| cos u => u = acos( A . B / (|A| |B|) )
In this case |line| = |dv| = length
. And so we calculate the dot product and the angle like this:
line . dv = (length,0,0) . (dx,dy,dz)
= length*dx
=> u = acos( length*dx / length^2 )
= acos( dx / length )
We now know everything we need to know and we can write a little JavaScript function that will move the line to the corresponding points:
function ray(line, v1, v2) {
var dx = v2[0] - v1[0],
dy = v2[1] - v1[1],
dz = v2[2] - v1[2],
length = Math.sqrt( dx*dx + dy*dy + dz*dz ),
angle = Math.acos( dx / length );
line.style.transform = 'translate3d(' + v1[0] + 'px,' + v1[1] + 'px,' + v1[2] + 'px) ' +
'rotate3d(0,' + (-length*dz) + ',' + (length*dy) + ',' + angle + 'rad)';
}