Trigonometry in CSS and JavaScript: Getting Creative with Trigonometric Functions

In part 1 we got an overview of trigonometry and learnt how we can use trigonometric functions in Sass. But for dynamic variables, we would be wise to move our calculations into JavaScript. Let’s take a look at an example that’s slightly more complex than clipping a simple triangle.

This article is the 2nd part in a series on “Trigonometry in CSS and JavaScript”:

  1. Introduction to Trigonometry
  2. Getting Creative with Trigonometric Functions (this article)
  3. Beyond Triangles

In the following demo we have a square-based pyramid, built with CSS 3D transforms. Using the slider, we can change the length of the sides of the pyramid, which results in changes to the overall height, and the angle of the sloping sides.

See the Pen Pyramids by Michelle Barker (@michellebarker) on CodePen.dark

To recalculate the angle at which the sides are rotated every time the input value changes, we need trigonometry. In order to do that, we can take a cross-section of the pyramid from the side, and visualize it as a triangle.

We can see that, just like our equilateral triangle in the previous article, the cross-section of our pyramid can be broken up into two right-angled triangles. (This time the shape of the cross-section is an isosceles triangle — a triangle that has two sides of equal length.)

To create the shapes for the base and sides of the pyramid, we can set the width and initial height, and use clip-path to clip the triangular shape of the sides.

.shape__base {
	--w: 10rem;
	
	width: var(--w);
	height: var(--w);
}

.shape__side {
	width: var(--side);
	height: var(--h, 20rem);
	clip-path: polygon(50% 0, 100% 100%, 0 100%);
}

I’m using custom properties here because they allow us to easily reuse identical values. I’m setting a default value for the --h custom property for the height value of the shape side, as we’ll change this value later on with JavaScript. (This is the value we’ll get from the slider.)

Going back to our cross-section diagram, we can see that our known values are the opposite side (which will be half of our --w variable) and the hypotenuse (the --h variable). What is unknown is the angle at which we need to rotate the sides so that they meet in the middle.

If we imagine the side of the pyramid originates from a starting position in the center, the angle we need to calculate is the one at the top of the triangle. We can think of it as being a bit like leaning a ladder against a wall. The angle between the ladder and the wall is the one we need to calculate.

Again, we can use custom properties in our CSS to set some transform values. Each side will have the same rotateX() value (the angle we’re going to calculate), but different rotateY() values, as they’ll be rotated around the pyramid (represented by the --ry custom property here):

.shape__side {
	transform-origin: top center;
	transform: 	
		rotateY(var(--ry, 0)) 
		rotateX(var(--angle, 15deg));	
}

.shape__side:nth-child(2) {
	--ry: 90deg;
}
	
.shape__side:nth-child(3) {
	--ry: -90deg;
}

.shape__side:nth-child(4) {
	--ry: 180deg;
}

Calculating angles

In the previous article we saw how we can calculate the length of any side of a right-angled triangle if we know the angle, but how about calculating the angle itself? For that, we need to rearrange our equations.

We know the opposite side and the hypotenuse, which indicates we need to use the Sine function. Dividing the opposite by the hypotenuse gives us sin(ϴ):

sin(angle) = o / h
Thee trigonometric equations, abbreviated to SOHCAHTOA

Therefore the angle is calculated by the inverse Sine (or Arcsine) of the opposite divided by the hypotenuse:

Math functions

We can use JavaScript math functions for this. Let’s create a function to call whenever the input changes, and update the --h (for the hypotenuse) and --angle custom properties. To get the Arcsine value we use Math.asin():

const shape = document.querySelector('.shape')
const input = document.querySelector('[data-slider]')

const setAngles = () => {
	const o = shape.clientWidth / 2
	const h = input.value
	const angle = Math.asin(o / h)
	
	shape.style.setProperty('--h', `${h}px`)
	shape.style.setProperty('--angle', `${angle}rad`)
}

input.addEventListener('input', setAngles)

Radians versus degrees

You might notice that we’re setting the --angle custom property value in radians, not degrees. Unless you’re a mathematician, there’s a good chance you usually think of angles in terms of degrees, rather than radians. A radian can be visualized as the length of the radius of a circle wrapped around the circumference. There are 2pi radians in a circle.

Diagram illustrating 2pi radians in a circle

The Math.asin() function gives us the angle in radians, and radians are perfectly legitimate units in CSS, so this will work just fine. But if you prefer to set the value in degrees, we can convert them with a simple function:

const radToDeg = (radians) => {
	return radians * (180 / Math.PI)
}

In the demo I’m also rounding the resulting value to two decimal places with toFixed():

const setAngles = () => {
	const o = shape.clientWidth / 2
	const h = input.value
	const radians = Math.asin(o / h)
	const angle = radToDeg(radians).toFixed(2)
	
	shape.style.setProperty('--h', `${h}px`)
	shape.style.setProperty('--angle', `${angle}deg`)
}

Now the angles of the sides of our pyramid will be recalculated every time we move the slider to change the length of the sides.

Get creative

Using the same method, we could even create a bunch of pyramids of random heights, by changing a single custom property:

See the Pen Pyramids by Michelle Barker (@michellebarker) on CodePen.dark

Here’s another creative example of trigonometry in action: A paper snowflake maker, where the user can drag the handles to clip out segments of a triangle to generate the snowflake pattern. The clip path coordinates were calculated using trigonometric functions.

See the Pen Snowflakes with clip-path trigonometry by Michelle Barker (@michellebarker) on CodePen.dark

In the next article we’ll see how trigonometry affords us even more creative possibilities when combined with JS, by enabling us to plot polygons and more complex shapes.

The post Trigonometry in CSS and JavaScript: Getting Creative with Trigonometric Functions appeared first on Codrops.

Experimental Triangle Image Transitions with WebGL

Do you love triangles? I do. And because its boring to like all triangles, I specifically target my love to equilateral ones, like this one:

equilateral triangle
Isn’t this beautiful? ?

Why

Nobody: …
Nobody at all: …
Me: lets make full screen image transitions with equilateral triangles!

And although that pretty much sums up the reasoning part, I also did this for the love. For the love of equilateral triangles.

So let’s get started! Just as I did in my previous animations, I will need some plane geometry in front of a viewer.

But if I use the three.js PlaneBufferGeometry,

Not epic. Boring triangles.

…those triangles are not what we are looking for. To create equilateral ones, i just created my geometry with math! The final result looks like this:

Woah, now this is pure beauty. Don’t judge me.

What’s the math behind that? Pretty easy actually: with equilateral triangles you know exactly all the coordinates and sizes:

equilateral triangle math
Math.

Knowing those sizes, I can easily calculate as many triangles as I need. Also, I can’t help sharing this amazing resource on all kinds of hexagonal and equilateral triangle grids. That’s an amazing read regardless of your geometric preferences.

Anyhow, I got this kind of grid:

equilateral triangle strip
I scaled this to fit the whole screen.

Animation the triangles

Now that we have a grid, we can use GLSL and a vertex shader to animate those triangles. I recommend you read more about shaders and GLSL in “The Book Of Shaders“.

The good news is that we can animate each triangle separately; we can even animate each of its vertices on their own!

An example animation.

In code that looks like this:

vec3 newPosition = move(position, progress);

Where the move function moves and rotates the default position with the change of the progress value. That could be a simple shift on the X-axis, for example:

vec3 newPosition = position + progress*vec3(1.,0.,0.);

Or anything you could imagine doing with numbers.

I had a lot of fun experimenting with those effects, hope you will like them. And the possibilities here are endless of course.

Tell me, or better show me, what geometry figures do you like to animate? 🙂

P.S.: I do love all kinds of triangles, I hope no triangle will get offended or mad after reading this article.

The post Experimental Triangle Image Transitions with WebGL appeared first on Codrops.

CSS Triangles, Multiple Ways

I like Adam Laki's Quick Tip: CSS Triangles because it covers that ubiquitous fact about front-end techniques: there are always many ways to do the same thing. In this case, drawing a triangle can be done:

  • with border and a collapsed element
  • with clip-path: polygon()
  • with transform: rotate() and overflow: hidden
  • with glyphs like ▼

I'd say that the way I've typically done triangles the most over the years is with the border trick, but I think my favorite way now is using clip-path. Code like this is fairly clear, understandable, and maintainable to me: clip-path: polygon(50% 0, 0 100%, 100% 100%); Brain: Middle top! Bottom right! Bottom left! Triangle!

My 2nd Place method goes to an option that didn't make Adam's list: inline <svg>! This kind of thing is nearly just as brain-friendly: <polygon points="0,0 100,0 50,100"/>.

Direct Link to ArticlePermalink

The post CSS Triangles, Multiple Ways appeared first on CSS-Tricks.