## Inspirational Website of the Week: Aristide Benoist

The website of Aristide Benoist is an interaction gem that shines with novel details and unique touches. Our pick this week.

The post Collective #665 appeared first on Codrops.

]]>The website of Aristide Benoist is an interaction gem that shines with novel details and unique touches. Our pick this week.

Our Sponsor

BeTheme’s new Muffin Builder is like upgrading from a Ford to a Ferrari. You’ll be shocked at how quickly you get your website (or one of Be’s 600+ pre-built websites) to the finish line.

A look at the systematic errors in thinking that affects decisions and judgment during the design process. By Jon Yablonski.

An interactive sandbox that shows the 3 basic matrix transformations that are essential for understanding vertex shaders in Three.js.

Learn how to optimize web fonts for Core Web Vitals in this article by Katie Hempenius.

Justin Searls’ screencast on how to go about identifying and removing unused styles.

A web component to represent a graph data structure in a 3-dimensional space using a force-directed iterative layout. It uses Three.js for 3D rendering.

Adam Argyle explains the new pseudo class selectors :is() and :where() are going to have a big impact.

Temani Afif shows how to create a fully responsive hexagon grid made without media queries.

Take a deep dive into what webpack is and how to use it in your development workflow. By Nwani Victory.

Sophie Koonin explains why it’s paramount to use semantic HTML.

Fig adds VSCode-style autocomplete to your existing terminal.

A “post-modern” modal text editor where the whole design is based around multiple selections as an editing primitive.

Learn how to disable a hyperlink in this article by Scott O’Hara.

Learn how to fix the insufficient text contrast issue for better accessibility using a browser extension.

The game is pretty simple: look at some code & decide if it would trigger an HTTP request in the latest stable release of particular browsers.

A free and open source Airtable alternative that can turn any SQL database into a smart spreadsheet. Supports MySQL, Postgres, SQL server, MariaDB & SQLite.

Sam Redmond shares the largest issues he faced when dealing with a variety of React projects and explains how Angular solves most of them.

Matej Latin shows how you only need 5 fonts for good web typography.

Kalker is a calculator that supports user-defined variables, functions, ambiguous syntax, derivation and integration. It runs on Windows, macOS, Linux, Android, and in web browsers (with WebAssembly).

From Our Blog

Get updated on the latest trends in web design with this new collection of super creative websites.

From Our Blog

In part three of our series we’ll look at how to create more interesting shapes with trigonometry, and how to draw them with the Canvas API.

From Our Blog

A simple thumbnail hover effect with a caption slide out animation and an SVG filter distortion on the image.

The post Collective #665 appeared first on Codrops.

]]>The post Thumbnail Hover Effect with SVG Filters appeared first on Codrops.

]]>So the idea is to hover a small image and apply an SVG filter to it while sliding in another element, a caption that covers the image.

This kind of animation adds that little special extra to a design component like this. I really hope you like it and that it inspires you for new ideas.

I’ve used a different filter on each one of the images, so you can get an idea on the different possibilities here.

**Please download it and use it as you wish, and thanks for stopping by! **

Let me know what you think of it @crnacura or @codrops.

The post Thumbnail Hover Effect with SVG Filters appeared first on Codrops.

]]>The post Trigonometry in CSS and JavaScript: Beyond Triangles appeared first on Codrops.

]]>This article is the 3rd part in a series on Trigonometry in CSS and JavaScript:

- Introduction to Trigonometry
- Getting Creative with Trigonometric Functions
**Beyond Triangles**(this article)

A regular polygon is a polygon with all equal sides and all equal angles. An equilateral triangle is one, so too is a pentagon, hexagon, decagon, and any number of others that meet the criteria. We can use trigonometry to plot the points of a regular polygon by visualizing each set of coordinates as points of a triangle.

If we visualize a circle on an *x/y* axis, draw a line from the center to any point on the outer edge, then connect that point to the horizontal axis, we get a triangle.

If we repeatedly rotated the line at equal intervals six times around the circle, we could plot the points of a hexagon.

But how do we get the *x* and *y* coordinates for each point? These are known as cartesian coordinates, whereas polar coordinates tell us the distance and angle from a particular point. Essentially, the radius of the circle and the angle of the line. Drawing a line from the center to the edge gives us a triangle where *hypotenuse* is equal to the circle’s radius.

We can get the angle in degrees by diving 360 by the number of vertices our polygon has, or in radians by diving 2pi radians. For a hexagon with a radius of 100, the polar coordinates of the uppermost point of the triangle in the diagram would be written (100, 1.0472rad) *(r, θ)*.

An infinite number of points would enable us to plot a circle.

We need to plot the points of our polygon as cartesian coordinates – their position on the *x* and *y* axis.

As we know the radius and the angle, we need to calculate the *adjacent* side length for the *x* position, and the *opposite* side length for the *y* position.

Therefore we need *Cosine* for the former and *Sine* for the latter:

```
adjacent = cos(angle) * hypotenuse
opposite = sin(angle) * hypotenuse
```

We can write a JS function that returns an array of coordinates:

```
const plotPoints = (radius, numberOfPoints) => {
/* step used to place each point at equal distances */
const angleStep = (Math.PI * 2) / numberOfPoints
const points = []
for (let i = 1; i <= numberOfPoints; i++) {
/* x & y coordinates of the current point */
const x = Math.cos(i * angleStep) * radius
const y = Math.sin(i * angleStep) * radius
/* push the point to the points array */
points.push({ x, y })
}
return points
}
```

We could then convert each array item into a string with the *x* and *y* coordinates in pixels, then use the `join()`

method to join them into a string for use in a clip path:

```
const polygonCoordinates = plotPoints(100, 6).map(({ x, y }) => {
return `${x}px ${y}px`
}).join(',')
shape.style.clipPath = `polygon(${polygonCoordinates})`
```

See the Pen Clip-path polygon by Michelle Barker (@michellebarker) on CodePen.dark

This clips a polygon, but you’ll notice we can only see one quarter of it. The clip path is positioned in the top left corner, with the center of the polygon in the corner. This is because at some points, calculating the cartesian coordinates from the polar coordinates is going to result in negative values. The area we’re clipping is outside of the element’s bounding box.

To position the clip path centrally, we need to add half of the width and height respectively to our calculations:

```
const xPosition = shape.clientWidth / 2
const yPosition = shape.clientHeight / 2
const x = xPosition + Math.cos(i * angleStep) * radius
const y = yPosition + Math.sin(i * angleStep) * radius
```

Let’s modify our function:

```
const plotPoints = (radius, numberOfPoints) => {
const xPosition = shape.clientWidth / 2
const yPosition = shape.clientHeight / 2
const angleStep = (Math.PI * 2) / numberOfPoints
const points = []
for (let i = 1; i <= numberOfPoints; i++) {
const x = xPosition + Math.cos(i * angleStep) * radius
const y = yPosition + Math.sin(i * angleStep) * radius
points.push({ x, y })
}
return points
}
```

Our clip path is now positioned in the center.

See the Pen Clip-path polygon by Michelle Barker (@michellebarker) on CodePen.dark

The types of polygons we’ve plotted so far are known as *convex* polygons. We can also plot *star* polygons by modifying our code in the `plotPoints()`

function ever so slightly. For every other point, we could change the radius value to be 50% of the original value:

```
/* Set every other point’s radius to be 50% */
const radiusAtPoint = i % 2 === 0 ? radius * 0.5 : radius
/* x & y coordinates of the current point */
const x = xPosition + Math.cos(i * angleStep) * radiusAtPoint
const y = yPosition + Math.sin(i * angleStep) * radiusAtPoint
```

See the Pen Clip-path star polygon by Michelle Barker (@michellebarker) on CodePen.dark

Here’s an interactive example. Try adjusting the values for the number of points and the inner radius to see the different shapes that can be made.

See the Pen Clip-path adjustable polygon by Michelle Barker (@michellebarker) on CodePen.dark

So far we’ve plotted values to use in CSS, but trigonometry has plenty of applications beyond that. For instance, we can plot points in exactly the same way to draw on a `<canvas>`

with Javascript. In this function, we’re using the same function as before (`plotPoints()`

) to create an array of polygon points, then we draw a line from one point to the next:

```
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
const draw = () => {
/* Create the array of points */
const points = plotPoints()
/* Move to starting position and plot the path */
ctx.beginPath()
ctx.moveTo(points[0].x, points[0].y)
points.forEach(({ x, y }) => {
ctx.lineTo(x, y)
})
ctx.closePath()
/* Draw the line */
ctx.stroke()
}
```

See the Pen Canvas polygon (simple) by Michelle Barker (@michellebarker) on CodePen.dark

We don’t even have to stick with polygons. With some small tweaks to our code, we can even create spiral patterns. We need to change two things here: First of all, a spiral requires multiple rotations around the point, not just one. To get the angle for each step, we can multiply *pi* by 10 (for example), instead of two, and divide that by the number of points. That will result in five rotations of the spiral (as 10*pi* divided by two is five).

`const angleStep = (Math.PI * 10) / numberOfPoints`

Secondly, instead of an equal radius for every point, we’ll need to increase this with every step. We can multiply it by a number of our choosing to determine how far apart the lines of our spiral are rendered:

```
const multiplier = 2
const radius = i * multiplier
const x = xPosition + Math.cos(i * angleStep) * radius
const y = yPosition + Math.sin(i * angleStep) * radius
```

Putting it all together, our adjusted function to plot the points is as follows:

```
const plotPoints = (numberOfPoints) => {
const angleStep = (Math.PI * 10) / numberOfPoints
const xPosition = canvas.width / 2
const yPosition = canvas.height / 2
const points = []
for (let i = 1; i <= numberOfPoints; i++) {
const radius = i * 2 // multiply the radius to get the spiral
const x = xPosition + Math.cos(i * angleStep) * radius
const y = yPosition + Math.sin(i * angleStep) * radius
points.push({ x, y })
}
return points
}
```

See the Pen Canvas spiral – simple by Michelle Barker (@michellebarker) on CodePen.dark

At the moment the lines of our spiral are at equal distance from each other, but we could increase the radius exponentially to get a more pleasing spiral. By using the `Math.pow()`

function, we can increase the radius by a larger number for each iteration. By the golden ratio, for example:

```
const radius = Math.pow(i, 1.618)
const x = xPosition + Math.cos(i * angleStep) * radius
const y = yPosition + Math.sin(i * angleStep) * radius
```

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

We could also rotate the spiral, using (using requestAnimationFrame). We’ll set a `rotation`

variable to *0*, then on every frame increment or decrement it by a small amount. In this case I’m decrementing the rotation, to rotate the spiral anti-clockwise

```
let rotation = 0
const draw = () => {
const { width, height } = canvas
/* Create points */
const points = plotPoints(400, rotation)
/* Clear canvas and redraw */
ctx.clearRect(0, 0, width, height)
ctx.fillStyle = '#ffffff'
ctx.fillRect(0, 0, width, height)
/* Move to beginning position */
ctx.beginPath()
ctx.moveTo(points[0].x, points[0].y)
/* Plot lines */
points.forEach((point, i) => {
ctx.lineTo(point.x, point.y)
})
/* Draw the stroke */
ctx.strokeStyle = '#000000'
ctx.stroke()
/* Decrement the rotation */
rotation -= 0.01
window.requestAnimationFrame(draw)
}
draw()
```

We’ll also need to modify our `plotPoints()`

function to take the `rotation`

value as an argument. We’ll use this to increment the *x* and *y* position of each point on every frame:

```
const x = xPosition + Math.cos(i * angleStep + rotation) * radius
const y = yPosition + Math.sin(i * angleStep + rotation) * radius
```

This is how our `plotPoints()`

function looks now:

```
const plotPoints = (numberOfPoints, rotation) => {
/* 6 rotations of the spiral divided by number of points */
const angleStep = (Math.PI * 12) / numberOfPoints
/* Center the spiral */
const xPosition = canvas.width / 2
const yPosition = canvas.height / 2
const points = []
for (let i = 1; i <= numberOfPoints; i++) {
const r = Math.pow(i, 1.3)
const x = xPosition + Math.cos(i * angleStep + rotation) * r
const y = yPosition + Math.sin(i * angleStep + rotation) * r
points.push({ x, y, r })
}
return points
}
```

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

I hope this series of articles has given you a few ideas for how to get creative with trigonometry and code. I’ll leave you with one more creative example to delve into, using the spiral method detailed above. Instead of plotting points from an array, I’m drawing circles at a new position on each iteration (using requestAnimationFrame).

See the Pen Canvas spiral IIII by Michelle Barker (@michellebarker) on CodePen.dark

Special thanks to George Francis and Liam Egan, whose wonderful creative work inspired me to delve deeper into this topic!

The post Trigonometry in CSS and JavaScript: Beyond Triangles appeared first on Codrops.

]]>The post Collective #664 appeared first on Codrops.

]]>A super sharp design with great typography and vibrant details. Our pick this week.

Learn about CSS-related techniques for optimizing Web Vitals in this article by Katie Hempenius and Una Kravets.

A great CSS Grid & Flexbox generator for creating layout components.

A massive resource and guide to JavaScript.

There’s more to a link than just a clickable word or image. So, how do you create the perfect link? Rian Rietveld takes you on an informative journey.

Learn how to add a vector-based, fluid hover effect to any simple SVG path. By George Francis.

A great freebie by BONT that contains 3 hero designs with different art directions. Made for Figma.

Barry Pollard looks at ways to tackle issues regarding the Cumulative Layout Shift metric.

Learn about design specs, how to create them, and how they smooth out the hand-off process between design and engineering. By Mahdi Farra.

Boring avatars is a tiny JavaScript React library that generates custom, SVG-based, round avatars from any username and color palette.

Zero-runtime stylesheets-in-TypeScript: Use TypeScript as your preprocessor. Write type‑safe, locally scoped classes, variables and themes, then generate static CSS files at build time.

Readsom hosts newsletters that are handpicked and filtered through topics to help you find your niche of interest. Explore and discover your next favorite newsletter.

Evgeny Klimenchenko covers the most popular and widely used types of front-end tests and explains why testing is for everyone.

A real-time strategy game where you control your units by writing JavaScript code.

Ethan Marcotte shares his excitement about container queries.

Khroma uses AI to learn which colors you like and creates limitless palettes for you to discover, search, and save.

Supercool CSS only text morphing by Amit Sheen. If you like this effect, you might also find this demo interesting.

A great collection of useful UI design tips by Victor Ponamariov.

Finally a Captcha that respects your privacy and that is everything but boring!

Mikael Ainalem explains how to create a slick input with an animating placeholder.

Jay Freestone argues that we should spend less time trying to fix things, and more time trying to understand them.

macOCR is a command line app that enables you to turn any text on your screen into text on your clipboard.

From Our Blog

Learn how to code the particle cloud seen on the website of Visualdata using Three.js.

From Our Blog

In this series of articles we’ll get an overview of trigonometry, understand how it can be useful, and delve into some creative applications in CSS and JavaScript.

From Our Blog

In the second part of this series on trigonometry, we’ll explore JavaScript trigonometric functions and learn how we can apply them to our CSS code.

The post Collective #664 appeared first on Codrops.

]]>The post Inspirational Websites Roundup #26 appeared first on Codrops.

]]>Please enjoy and thanks for checking by!

The post Inspirational Websites Roundup #26 appeared first on Codrops.

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

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

- Introduction to Trigonometry
**Getting Creative with Trigonometric Functions**(this article)- 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;
}
```

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`

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

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)
```

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.

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.

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.

]]>The post Trigonometry in CSS and JavaScript: Introduction to Trigonometry appeared first on Codrops.

]]>**Introduction to Trigonometry**(this article)- Getting Creative with Trigonometric Functions
- Beyond Triangles

If, like me, you’ve rarely used trigonometry outside of the classroom, let’s take a trip back to school and get ourselves reacquainted.

Trigonometric functions allow us to calculate unknown values of a right-angled triangle from known parameters. Imagine you’re standing on the ground, looking up at a tall tree. It would be very difficult to measure the height of the tree from the ground. But if we know the angle at which we look up at the top of the tree, and we know the distance from ourselves to the tree, we can infer the height of the tree itself.

If we imagine this scene as a triangle, the known length (from us to the tree) is known as the *adjacent* side, the tree is the *opposite* side (it’s *opposite* the angle), and the longest side – from us to the top of the tree – is called the *hypotenuse*.

There are three main functions to remember in trigonometry: *Sine*, *Cosine* and *Tangent* (abbreviated to *sin*, *cos* and *tan*). They are expressed as the following formulae:

```
sin(angle) = opposite / hypotenuse
cos(angle) = adjacent / hypotenuse
tan(angle) = opposite / adjacent
```

The angle is usually written as the Greek *theta* (*θ*) symbol.

We can use these equations to calculate the unknown values of our triangle from the known ones. To measure the height of the tree in the example, we know the angle (*θ*) and the *adjacent* side.

To calculate the *opposite* side we would need the *tangent* function. We would need to switch around the formula:

`opposite = tan(angle) * adjacent`

How do we get *tan( θ)*? We could use a scientific calculator (type

If we’re working with predetermined values, we could use the trigonometric functions built into Sass (the CSS preprocessor).

To include the Math module we need the following line in our Sass file:

`@use "sass:math";`

We can use variables to calculate the *opposite* side from the angle and *adjacent* side values.

```
$angle: 45deg;
$adjacent: 100%;
$opposite: math.tan($angle) * $adjacent;
```

The *tan* function in Sass can use radians or degrees — if using degrees, the units must be specified. Without units, radians will be used by default (more on these later).

In the following demo we’re using these in the `clip-path`

property to determine the coordinates of the polygon points, similar to calculating the height of a tree.

See the Pen Using Sass trigonometry for clip-path values by Michelle Barker (@michellebarker) on CodePen.dark

We need to subtract the `$opposite`

variable from the height of the element in order to get the *y* coordinate — as clip-path coordinates are plotted along the *y* axis increasing from top to bottom.

```
.element {
clip-path: polygon(0 100%, $adjacent (100% - $opposite), $adjacent 100%);
}
```

A right-angled triangle is the simplest use of trigonometry. But we can work out the coordinates of more complex shapes by splitting them up into right-angled triangles.

An equilateral triangle is a triangle with three sides of the same length. Perhaps you remember from school that the angles in a triangle add up to 180º? That means each angle in an equilateral triangle is 60º.

If we draw a line down the middle of an equilateral triangle, we split it into (you guessed it) two right-angled triangles. So, for a triangle with sides of a given length, we know the angle (60º), the length of the *hypotenuse*, and the length of the *adjacent* side (half the length of the *hypotenuse*).

What we *don’t* know is the height of the triangle — once again, the *opposite* side of the right-angled triangle. To plot the clip-path coordinates, this is what we need to work out. This time, as we know the angle and the length of the *hypotenuse*, we can use the *sine* function:

```
$hypotenuse: 60%; // side length
$angle: 60deg;
$opposite: math.sin($angle) * $hypotenuse;
```

(It would also be possible for us to use the *tangent* function instead, as we know that the length of the *adjacent* side is half of the *hypotenuse*.) Then we can use those values for our clip-path polygon points:

```
.element {
clip-path: polygon(
0 $opposite,
($hypotenuse / 2) 0,
$hypotenuse $opposite
);
}
```

See the Pen Clip-path simple equilateral triangles with Sass by Michelle Barker (@michellebarker) on CodePen.dark

As you can see in the demo, the element is clipped from the top left corner. This might not be completely satisfactory: it’s more likely we’d want to clip from the center, especially if we’re clipping an image. We can adjust our clip-path coordinates accordingly. To make this more readable, we can assign some additional variables for the *adjacent* side length (half the hypotenuse), and the start and end position of the triangle:

```
$hypotenuse: 60%; //side length
$angle: 60deg;
$opposite: math.sin($angle) * $hypotenuse;
$adjacent: $hypotenuse / 2;
$startPosX: (50% - $adjacent);
$startPosY: (50% - $opposite / 2);
$endPosX: (50% + $adjacent);
$endPosY: (50% + $opposite / 2);
.element {
clip-path: polygon(
$startPosX $endPosY,
50% $startPosY,
$endPosX $endPosY
);
}
```

This is quite a bit of complex code to write for a single triangle. Let’s create a Sass mixin, allowing us to clip a triangle of any size on any element we like. As `clip-path`

still needs a prefix in some browsers, our mixin covers that too:

```
@mixin triangle($sideLength) {
$hypotenuse: $sideLength;
$angle: 60deg;
$opposite: math.sin($angle) * $hypotenuse;
$adjacent: $hypotenuse / 2;
$startPosX: (50% - $adjacent);
$startPosY: (50% - $opposite / 2);
$endPosX: (50% + $adjacent);
$endPosY: (50% + $opposite / 2);
$clip: polygon(
$startPosX $endPosY,
50% $startPosY,
$endPosX $endPosY
);
-webkit-clip-path: $clip;
clip-path: $clip;
}
```

To clip a centred equilateral triangle from any element, we can simply include the mixin, passing in the length of the triangle’s sides:

```
.triangle {
@include triangle(60%);
}
```

See the Pen Clip-path equilateral triangles with Sass trigonometric functions by Michelle Barker (@michellebarker) on CodePen.dark

Our use of Sass functions has some limitations:

- It assumes the
`$sideLength`

variable is known at compile time, and doesn’t allow for dynamic values. - Sass doesn’t handle mixing units all that well for our needs. In the last demo, if you switch out the percentage-based side length to a fixed length (such as rems or pixels), the code breaks.

The latter is because our calculations for the `$startPos`

and `$endPos`

variables (to position the clip-path centrally) depend on subtracting the side length from a percentage. Unlike in regular CSS (using *calc()*), Sass doesn’t allow for that. In the final demo, I’ve adjusted the mixin so that it works for any valid length unit, by passing in the size of the clipped element as a parameter. We’d just need to ensure that the values for the two parameters passed in have identical units.

See the Pen Clip-path equilateral triangles with Sass trigonometric functions by Michelle Barker (@michellebarker) on CodePen.dark

CSS has a proposal for trigonometric functions as part of the CSS Values and Units Module Level 4 (currently in working draft). These could be extremely useful, especially when used alongside custom properties. Here’s how we could rewrite our CSS to use native CSS trigonometric functions. Changing the size of the clip path is as simple as updating a single custom property:

```
.triangle {
--hypotenuse: 8rem;
--opposite: calc(sin(60deg) * var(--hypotenuse));
--adjacent: calc(var(--hypotenuse) / 2);
--startPosX: calc(var(--size) / 2 - var(--adjacent));
--startPosY: calc(var(--size) / 2 - var(--opposite) / 2);
--endPosX: calc(var(--size) / 2 + var(--adjacent));
--endPosY: calc(var(--size) / 2 + var(--opposite) / 2);
--clip: polygon(
var(--startPosX) var(--endPosX),
50% var(--startPosY),
var(--endPosX) var(--endPosY)
);
-webkit-clip-path: var(--clip);
clip-path: var(--clip);
}
.triangle:nth-child(2) {
--hypotenuse: 3rem;
}
.triangle:nth-child(2) {
--hypotenuse: 50%;
}
```

Custom properties can be dynamic too. We can change them with JS and the values dependant on them will be automatically recalculated.

`triangle.style.setProperty('--hypotenuse', '5rem')`

CSS trigonometric functions have a lot of potential when they finally land, but sadly they’re not yet supported in any browsers. To use trigonometry with dynamic variables right now, we need JavaScript.

We’ll take a look at some of the possibilities in the next article.

The post Trigonometry in CSS and JavaScript: Introduction to Trigonometry appeared first on Codrops.

]]>The post FBO Particles with Three.js appeared first on Codrops.

]]>In this ALL YOUR HTML coding session you’ll learn how to create an FBO particle system to replicate the beautiful particle cloud from Visualdata with Three.js.

This coding session was streamed live on May 30, 2021.

Support: https://www.patreon.com/allyourhtml

Setup: https://gist.github.com/akella/a19954…

The post FBO Particles with Three.js appeared first on Codrops.

]]>The post Collective #663 appeared first on Codrops.

]]>Kazuki Noda’s portfolio is a creative gem with many unique details and interactive magic. Our pick this week.

Our Sponsor

With the Divi Layout Packs you’ll get world-class designs ready to be used for your client projects.

Eric Meyer remebers how CSS kicked off 25 years ago this month in a conference room in Paris, May 7th, 1996.

With *Material You* Google wants to transform design for Android, for Google, and for the entire tech industry.

Ahmad Shadeed explains CSS Container queries and shows how it will change your workflow as a designer.

Flat explores how to make it easy to work with data in git and GitHub.

Easily build blogs, landing pages, portfolios or documentation sites with ready-to-use templates.

Amazing Three.js magic by Ryohei Ukon.

In this article, Adrian Bece covers CSS container query basics and shows how we can use them today with progressive enhancement or polyfills.

Lea Verou explains some tricky CSS and shows how “revert” works.

Design structured diagrams and link directly to your repos, folders and files.

Cassidy Williams writes about Incremental Static Regeneration or “hybrid web development” and how it can improve and scale sites “beyond the Jamstack”.

Handy makes defining and recognizing custom hand poses in WebXR a snap. It recognizes 100+ hand poses, including ASL finger spelling. Built on Three.js.

Barry Pollard explores some upcoming font options that might make it easier to align fallback fonts to final fonts.

A candid and practical handbook for designers by Fabricio Teixeira.

Shubham Jain shows how to include external SVGs while retaining the format’s customization powers.

Sarah Dayan writes about utility-first CSS and ends with the overused cliché that utility classes are just inline styles.

A browser extension that helps you navigate back in time without leaving your current tab.

A lightweight, modular, JavaScript image and video lightbox gallery plugin. Available for React.js, Vue.js, Angular, and TypeScript.

A mini-game about pop ups, and the deviousness of websites and apps.

Some 2D optics magic in JavaScript made by Philip Zucker.

Read how Katsuomi Kobayashi’s used the new WebGL version of the Google Maps JavaScript API for his car simulator.

Learn how to build a camera shutter/iris simulation in plain JavaScript.

Read how from the release of the page experience algorithm, there is no longer any preferential treatment for AMP in Google’s search results, Top Stories carousel and the Google News.

Dmitri Pavlutin explains how to correctly cleanup async side-effects in React when the component unmounts or updates.

From Our Blog

A quick tutorial on how to craft a fullscreen SVG crosshair mouse cursor with a special distortion effect on hover.

The post Collective #663 appeared first on Codrops.

]]>The post How to Code a Crosshair Mouse Cursor with Distortion Hover Effect appeared first on Codrops.

]]>Let’s have a look how to create a fullscreen crosshair cursor in SVG and how to distort the cursors’s lines with an SVG filter when hovering over links. We’ll also add a nice hover animation for some menu items using Splitting.js.

For the SVG cursor we want a singe SVG for each line that has enough of space to get the distortion effect applied to it:

```
<div class="cursor">
<svg class="cursor__line cursor__line--horizontal" viewBox="0 0 200 20" preserveAspectRatio="none">
<line class="cursor__line-element" x1="0" y1="10" x2="200" y2="10" shape-rendering="crispEdges" vector-effect="non-scaling-stroke" />
</svg>
<svg class="cursor__line cursor__line--vertical" viewBox="0 0 20 200" preserveAspectRatio="none">
<line class="cursor__line-element" x1="10" y1="0" x2="10" y2="200" shape-rendering="crispEdges" vector-effect="non-scaling-stroke" />
</svg>
</div>
```

We’ll add the filters like this:

```
<div class="cursor">
<svg class="cursor__line cursor__line--horizontal" viewBox="0 0 200 20" preserveAspectRatio="none">
<defs>
<filter id="filter-noise-x" x="-50%" y="-50%" width="200%" height="200%"
filterUnits="objectBoundingBox">
<feTurbulence type="fractalNoise" baseFrequency="0" numOctaves="1" result="warp" />
<feOffset dx="-30" result="warpOffset" />
<feDisplacementMap xChannelSelector="R" yChannelSelector="G" scale="30" in="SourceGraphic" in2="warpOffset" />
</filter>
</defs>
<line class="cursor__line-element" x1="0" y1="10" x2="200" y2="10" shape-rendering="crispEdges" vector-effect="non-scaling-stroke" />
</svg>
<svg class="cursor__line cursor__line--vertical" viewBox="0 0 20 200" preserveAspectRatio="none">
<defs>
<filter id="filter-noise-y" x="-50%" y="-50%" width="200%" height="200%"
filterUnits="objectBoundingBox">
<feTurbulence type="fractalNoise" baseFrequency="0" numOctaves="1" result="warp" />
<feOffset dy="-30" result="warpOffset" />
<feDisplacementMap xChannelSelector="R" yChannelSelector="G" scale="30" in="SourceGraphic" in2="warpOffset" />
</filter>
</defs>
<line class="cursor__line-element" x1="10" y1="0" x2="10" y2="200" shape-rendering="crispEdges" vector-effect="non-scaling-stroke" />
</svg>
</div>
```

Let’s set up our menu with some data-splitting attributes:

```
<nav class="menu">
<a href="#content-1" class="menu__item">
<span data-splitting class="menu__item-title">Maputo</span>
<span data-splitting class="menu__item-sub">Nights</span>
</a>
<a href="#content-1" class="menu__item">
<span data-splitting class="menu__item-title">Gaborone</span>
<span data-splitting class="menu__item-sub">Vibes</span>
</a>
<a href="#content-1" class="menu__item">
<span data-splitting class="menu__item-title">Kinshasa</span>
<span data-splitting class="menu__item-sub">Walks</span>
</a>
</nav>
```

First, we need to hide our cursor by default, and we just want to show it if the user has a pointing device, like a mouse. So we add a media query with the any-pointer media feature:

```
.cursor {
display: block;
}
@media (any-pointer:fine) {
.cursor {
position: fixed;
top: 0;
left: 0;
display: block;
pointer-events: none;
z-index: 1001;
}
.no-js .cursor {
display: none;
}
.cursor__line {
position: fixed;
display: block;
will-change: transform, opacity;
}
.cursor__line--horizontal {
top: -10px;
left: -10%;
width: 120%;
height: 20px;
}
.cursor__line--vertical {
left: -10px;
top: -10%;
height: 120%;
width: 20px;
}
.cursor__line-element {
fill: none;
stroke: var(--cursor-stroke);
stroke-width: var(--cursor-stroke-width);
}
}
```

The variables are define in the body.

For the menu, we’ll use some flexbox magic to lay out the menu items beneath each other:

```
.menu {
display: flex;
flex-direction: column;
width: 100vw;
height: calc(100vh - 13rem);
position: relative;
justify-content: flex-start;
align-items: center;
}
.menu {
text-align: center;
padding-top: 10vh;
}
.menu__item {
display: inline-block;
margin-bottom: 3rem;
text-decoration: none;
color: var(--color-menu);
font-family: vortice-concept, sans-serif;
}
.menu__item-title {
line-height: 1;
font-size: 7.5vw;
}
.menu__item-sub {
font-size: 1.5vw;
display: block;
}
@media screen and (min-width: 53em) {
.menu {
height: 100vh;
justify-content: center;
}
}
```

Let’s create our custom cursor. So we have two SVGs, one for each line. As we saw in the markup earlier, each one of the SVGs will include a filter that we’ll apply to the respective line when hovering over a menu link.

Let’s start coding the entry JavaScript file (index.js):

```
import { Cursor } from './cursor';
// initialize custom cursor
const cursor = new Cursor(document.querySelector('.cursor'));
// mouse effects on all links
[...document.querySelectorAll('a')].forEach(link => {
link.addEventListener('mouseenter', () => cursor.enter());
link.addEventListener('mouseleave', () => cursor.leave());
});
```

Now, let’s create a class for the **cursor** (in cursor.js):

```
import { gsap } from 'gsap';
import { getMousePos } from './utils';
// Track the mouse position and update it on mouse move
let mouse = {x: 0, y: 0};
window.addEventListener('mousemove', ev => mouse = getMousePos(ev));
export class Cursor {
constructor(el) {
}
// hovering over a link
enter() {
}
// hovering out a link
leave() {
}
// create the turbulence animation timeline on the cursor line elements
createNoiseTimeline() {
}
// render styles and loop
render() {
// ...
requestAnimationFrame(() => this.render());
}
}
```

What we do here is to update the mouse position as we move the mouse around. For that, we use the getMousePos function (in utils.js).

Let’s move on to the next interesting part:

```
...
constructor(el) {
// main DOM element which includes the 2 svgs, each for each line
this.DOM = {el: el};
// both horizontal and vertical lines
this.DOM.lines = this.DOM.el.children;
[this.DOM.lineHorizontal, this.DOM.lineVertical] = this.DOM.lines;
// hide initially
gsap.set(this.DOM.lines, {opacity: 0});
...
}
...
```

We initialize the line DOM elements and hide them initially.

We want to update the lines transform (translation values) as we move the mouse. For that, let’s create an object that stores the translation state:

```
...
constructor(el) {
...
// style properties that will change as we move the mouse (translation)
this.renderedStyles = {
tx: {previous: 0, current: 0, amt: 0.15},
ty: {previous: 0, current: 0, amt: 0.15}
};
...
}
...
```

With interpolation, we can achieve a smooth animation effect when moving the mouse. The “previous” and “current” values are the values we’ll be interpolating. The current value of one of these “animatable” properties will be one between these two values at a specific increment. The value of “amt” is the amount to interpolate. As an example, the following formula calculates our current *translationX* value:

`this.renderedStyles.tx.previous = lerp(this.renderedStyles.tx.previous, this.renderedStyles.tx.current, this.renderedStyles.tx.amt);`

```
...
constructor(el) {
...
// svg filters (ids)
this.filterId = {
x: '#filter-noise-x',
y: '#filter-noise-y'
};
// the feTurbulence elements per filter
this.DOM.feTurbulence = {
x: document.querySelector(`${this.filterId.x} > feTurbulence`),
y: document.querySelector(`${this.filterId.y} > feTurbulence`)
}
// turbulence current value
this.primitiveValues = {turbulence: 0};
// create the gsap timeline that will animate the turbulence value
this.createNoiseTimeline();
}
...
```

Next, we initialize the filter ids, the *feTurbulence* elements for each SVG filter (one per line) and also the current turbulence value. Then we create the GSAP timeline that will take care of updating the *baseFrequency* of each filter with the current turbulence value. Here’s how that timeline and the methods that start/stop it look like:

```
...
createNoiseTimeline() {
// turbulence value animation timeline:
this.tl = gsap.timeline({
paused: true,
onStart: () => {
// apply the filters for each line element
this.DOM.lineHorizontal.style.filter = `url(${this.filterId.x}`;
this.DOM.lineVertical.style.filter = `url(${this.filterId.y}`;
},
onUpdate: () => {
// set the baseFrequency attribute for each line with the current turbulence value
this.DOM.feTurbulence.x.setAttribute('baseFrequency', this.primitiveValues.turbulence);
this.DOM.feTurbulence.y.setAttribute('baseFrequency', this.primitiveValues.turbulence);
},
onComplete: () => {
// remove the filters once the animation completes
this.DOM.lineHorizontal.style.filter = this.DOM.lineVertical.style.filter = 'none';
}
})
.to(this.primitiveValues, {
duration: 0.5,
ease: 'power1',
// turbulence start value
startAt: {turbulence: 1},
// animate to 0
turbulence: 0
});
}
enter() {
// start the turbulence timeline
this.tl.restart();
}
leave() {
// stop the turbulence timeline
this.tl.progress(1).kill();
}
...
```

The animation will change the turbulence value (which starts at 1 and ends at 0) and apply it to each feTurbulence’s *baseFrequency* attribute. The filters get applied to the lines in the beginning and removed once the animation is completed.

To finalize the constructor method we fade in the lines and start updating the translation values the first time we move the mouse:

```
...
constructor(el) {
...
import { lerp, getMousePos } from './utils';
...
// on first mousemove fade in the lines and start the requestAnimationFrame rendering function
this.onMouseMoveEv = () => {
this.renderedStyles.tx.previous = this.renderedStyles.tx.current = mouse.x;
this.renderedStyles.ty.previous = this.renderedStyles.ty.previous = mouse.y;
gsap.to(this.DOM.lines, {duration: 0.9, ease: 'Power3.easeOut', opacity: 1});
requestAnimationFrame(() => this.render());
window.removeEventListener('mousemove', this.onMouseMoveEv);
};
window.addEventListener('mousemove', this.onMouseMoveEv);
}
...
```

Now we’re only missing the actual method that updates the lines’ translation values as we move the mouse:

```
...
render() {
// update the current translation values
this.renderedStyles['tx'].current = mouse.x;
this.renderedStyles['ty'].current = mouse.y;
// use linear interpolation to delay the translation animation
for (const key in this.renderedStyles ) {
this.renderedStyles[key].previous = lerp(this.renderedStyles[key].previous, this.renderedStyles[key].current, this.renderedStyles[key].amt);
}
// set the new values
gsap.set(this.DOM.lineVertical, {x: this.renderedStyles['tx'].previous});
gsap.set(this.DOM.lineHorizontal, {y: this.renderedStyles['ty'].previous});
// loop this until the end of time
requestAnimationFrame(() => this.render());
}
```

As an extra, let’s add a little glitch effect to the menu items texts when we hover over them. We’ll use the Splitting library to split the menu texts into spans/chars so we can animate each one individually. We want to change the translation values of each character and also it’s color. Let’s update our index.js file:

```
import { Cursor } from './cursor';
import { MenuItem } from './menuItem';
// Splitting (used to split the menu item texts to spans/characters)
import 'splitting/dist/splitting.css';
import 'splitting/dist/splitting-cells.css';
import Splitting from 'splitting';
// initialize Splitting
const splitting = Splitting();
// initialize custom cursor
const cursor = new Cursor(document.querySelector('.cursor'));
// Menu Items
[...document.querySelectorAll('.menu > a')].forEach(el => new MenuItem(el));
// mouse effects on all links
[...document.querySelectorAll('a')].forEach(link => {
link.addEventListener('mouseenter', () => cursor.enter());
link.addEventListener('mouseleave', () => cursor.leave());
});
```

Assuming we have data-splitting set in all elements we want to split into chars, then we only need to call Splitting() and we then have each text split into a bunch of spans, for every letter of the text.

Let’s now have a look at our MenuItem class:

```
import { gsap } from 'gsap';
export class MenuItem {
constructor(el) {
this.DOM = {el};
// all text chars (Splittingjs)
this.DOM.titleChars = this.DOM.el.querySelectorAll('span.char');
// initial and final colors for each span char (before and after hovering)
const bodyComputedStyle = getComputedStyle(document.body);
this.colors = {
initial: bodyComputedStyle.getPropertyValue('--color-menu'),
final: bodyComputedStyle.getPropertyValue('--color-link')
};
this.initEvents();
}
...
}
```

We get a reference to all the characters of the menu item text and also it’s initial and final colors for the hover animation.

```
...
initEvents() {
this.onMouseEnterEv = () => this.onMouseEnter();
this.DOM.el.addEventListener('mouseenter', this.onMouseEnterEv);
this.onMouseLeaveEv = () => this.onMouseLeave();
this.DOM.el.addEventListener('mouseleave', this.onMouseLeaveEv);
}
...
```

We initialize the mouseenter/mouseleave events which will triggger the animation on the characters.

When hovering over a menu item we will randomly change its characters position and color, and when hovering out we reset the original color:

```
onMouseEnter() {
if ( this.leaveTimeline ) {
this.leaveTimeline.kill();
}
// let's try to do an animation that resembles a glitch effect on the characters
// we randomly set new positions for the translation and rotation values of each char and also set a new color
// and repeat this for 3 times
this.enterTimeline = gsap.timeline({
defaults: {
duration: 0.05,
ease: 'power3',
x: () => gsap.utils.random(-15, 15),
y: () => gsap.utils.random(-20, 10),
rotation: () => gsap.utils.random(-5, 5),
color: () => gsap.utils.random(0, 3) < 0.5 ? this.colors.final : this.colors.initial
}
})
// repeat 3 times (repeatRefresh option will make sure the translation/rotation values will be different for each iteration)
.to(this.DOM.titleChars, {
repeat: 3,
repeatRefresh: true
}, 0)
// reset translation/rotation and set final color
.to(this.DOM.titleChars, {
x: 0,
y: 0,
rotation: 0,
color: this.colors.final
}, '+=0.05');
}
onMouseLeave() {
// set back the initial color for each char
this.leaveTimeline = gsap.timeline()
.to(this.DOM.titleChars, {
duration: 0.4,
ease: 'power3',
color: this.colors.initial
});
}
```

And that is all!

I hope this has not been too difficult to follow and that you have gained some insight into constructing this fancy effect.

Please let me know if you have any question @codrops or @crnacura.

**Thank you for reading!**

The post How to Code a Crosshair Mouse Cursor with Distortion Hover Effect appeared first on Codrops.

]]>