The other day, I encountered a very interesting animation. It’s kind of a repetition effect on an image where the same gets scaled, layer by layer. This is Eva Habermann’s website where this element has that exact effect:
While this is a scroll based animation, there was also a hover effect somewhere, I just can’t recall where! If you’ve seen it, please let me know so that I can add it here.
There are some interesting parameters that we can play with in order to create different touches, all with a unique look and feel, so this is what I came up with, hope you enjoy it!
The way that we can define the parameters is as follows:
Then we animate the “image_element” divs according to the parameters set.
Here’s a short explanation of the parameters:
data-repetition
// this is so that we know we have to apply the effect
data-repetition-elems="4"
// number of inner elements/images
data-repetition-animate="scale"
// property to animate: scale, scaleX, scaleY
data-repetition-origin="50% 50%"
// transform origin
data-repetition-stagger="-0.1"
// GSAP animation stagger value between each inner image
data-repetition-initial-scale="2"
// this is the initial scale that is applied to the first inner child
data-repetition-duration="0.8"
// animation duration
data-repetition-ease="power2.inOut"
// animation ease
There’s lots of things to experiment with here, so I hope you can use this to have some fun with it!
Here’s our first example:
The main idea is to have repeated layers of the same image and do something to them, like scale them up and down, like in this case. There’s a little twist added here, which is that the first and last layer also have a zoom effect on the image. Changing the transform origin, can also add a nice touch to it:
I really hope you enjoy this and find it useful!
Thanks for checking by and if you want to support our work, please share and give us a follow @codrops!
Some months ago, I wrote about how to achieve a hover effect for a menu where an image would appear for each item. Then we saw how to apply this in Exploring Animations for Menu Hover Effects. When I discovered the fantastic site of One up Studio I wanted to combine their cool 3D-ish menu hover effect with that previous animation, so this is a little fusion between the two.
The 3D motion is slightly different and an image is shown in an angle, creating a playful look.
I really hope you like this little experiment and find it useful. Thanks for visiting!
Editor’s note: We want to share more of the web dev and design community directly here on Codrops, so we’re very happy to start featuring Yuriy’s newest live coding sessions!
In this live stream of ALL YOUR HTML, I’m replicating the particles animation from Mathis Biabiany‘s website. The effect consists of 2^18 particles forming images and a colorful tunnel that creates an interesting transition from one image to another.
This coding session was streamed live on Oct 25, 2020.
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.
I also wanted to add some smooth scrolling and on-scroll animations, so I’ve used Locomotive Scroll. The beautiful images are by DeMorris Byrd.
This is highly experimental and it turned out to be a complex process. But I hope it gives you some of sort idea and entry point of how to pull off these kind of animations without touching the width and height of an element.
The main idea behind this technique is to scale an element and then counter-scale the child. Paul Lewis and Stephen McGruer show how to do that on a menu using expand and collapse animations. Avoiding animating the width and height of an element helps keep performance in check.
So what we do is to initially set the scale of the content__intro wrapper to a value that will make it shrink to an exact size. Then we set a counter scale to the image. This will make the image maintain the same size as before. Then, we add another scale to the image, shrinking it also the to the target size.
Having the initial width and height of an element and also the target dimensions, we can calculate the scale values of the outer wrapper based on this:
1/introTransform.scaleX is the counter scale of the outer wrapper. The second value that we multiply makes sure that we scale the image down to our desired size, just like we did with the outer wrapper before.
And that’s the main idea behind the scaling magic.
I hope this gives you a starting point for these kind of tricky animations! Thank you for checking it out
Last week I showed you how we can create a fancy menu hover animation with images. Today I’d like to share some of the interesting effects we can achieve with that technique. Tuning some variables, timings and filters, the possibilities are really endless, so I invite you to explore some of the things we can come up with in the these couple of demos. Don’t forget to move your mouse along the link to see some jazzy motion and filters in action
At Codrops, we love experimenting with playful hover effects. Back in 2018, we explored a set of fun hover animations for links. We called that Image Reveal Hover Effects and it shows how to make images appear with a fancy animation when hovering items of a menu. After seeing the fantastic portfolio of Marvin Schwaibold, I wanted to try this effect again on a larger menu and add that beautiful swing effect when moving the mouse. Using some filters, this can also be made more dramatic.
If you are interested in other similar effect, have a look at these:
So, today we’ll have a look at how to create this juicy image hover reveal animation:
Some Markup and Styling
We’ll use a nested structure for each menu item because we’ll have several text elements that will appear on page load and hover.
But we’ll not go into the text animation on load or the hover effect so what we are interested in here is how we’ll make the image appear for each item. The first thing I do when I want to make a certain effect is to write up the structure that I need using no JavaScript. So let’s take a look at that:
In order to construct this markup for the image, we need to save the source somewhere. We’ll use a data attribute on the menu__item, e.g. data-img="img/1.jpg". We’ll go into more detail later on.
Any other styles that are specific to our effect (like the transforms) we’ll add dynamically.
Let’s take a look at the JavaScript.
The JavaScript
We’ll use GSAP and besides our hover animation, we’ll also use a custom cursor and smooth scrolling. For that we’ll use the smooth scroll library from the amazing folks of Locomotive, the Agency of the year. Since those are both optional and out of the scope of the menu effect we want to showcase, we’ll not be covering it here.
First things first: let’s preload all the images. For the purpose of this demo we are doing this on page load, but that’s optional.
Once that’s done, we can initialize the smooth scroll instance, the custom cursor and our Menu instance.
Here’s how the entry JavaScript file (index.js) looks like:
import Cursor from './cursor';
import {preloader} from './preloader';
import LocomotiveScroll from 'locomotive-scroll';
import Menu from './menu';
const menuEl = document.querySelector('.menu');
preloader('.menu__item').then(() => {
const scroll = new LocomotiveScroll({el: menuEl, smooth: true});
const cursor = new Cursor(document.querySelector('.cursor'));
new Menu(menuEl);
});
Now, let’s create a class for the Menu (in menu.js):
import {gsap} from 'gsap';
import MenuItem from './menuItem';
export default class Menu {
constructor(el) {
this.DOM = {el: el};
this.DOM.menuItems = this.DOM.el.querySelectorAll('.menu__item');
this.menuItems = [];
[...this.DOM.menuItems].forEach((item, pos) => this.menuItems.push(new MenuItem(item, pos, this.animatableProperties)));
...
}
...
}
So far we have a reference to the main element (the menu <nav> element) and the menu item elements. We’ll also create an array of our MenuItem instances. But let’s cover that bit in a moment.
What we’ll want to do now is to update the transform (both, X and Y translate) value as we move the mouse over the menu items. But we might as well want to update other properties. In our case we will additionally be updating the rotation and the CSS filter value (brightness). For that, let’s create an object that stores this configuration:
With interpolation, we can achieve the 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:
Finally, we can show the menu items, which are hidden by default. This was just a little extra, and totally optional, but it’s definitely a nice add-on to reveal each item with a delay on page load.
That’s it for the Menu class. What we’ll be looking into next is how to create the MenuItem class together with some helper variables and functions.
So, let’s start by importing the GSAP library (which we will use to show and hide the images), some helper functions and the images inside our images folder.
Next, we need to get access to the mouse position at any given time, since the image will follow along its movement. We can update this value on “mousemove” . We will also cache its position so we can calculate its speed and movement direction for both, the X and Y axis.
Hence, that’s what we’ll have so far in the menuItem.js file:
import {gsap} from 'gsap';
import { map, lerp, clamp, getMousePos } from './utils';
const images = Object.entries(require('../img/*.jpg'));
let mousepos = {x: 0, y: 0};
let mousePosCache = mousepos;
let direction = {x: mousePosCache.x-mousepos.x, y: mousePosCache.y-mousepos.y};
window.addEventListener('mousemove', ev => mousepos = getMousePos(ev));
export default class MenuItem {
constructor(el, inMenuPosition, animatableProperties) {
...
}
...
}
An item will be passed its position/index in the menu (inMenuPosition) and the animatableProperties object described before. The fact that the “animatable” property values are shared and updated among the different menu items will make the movement and rotation of the images continuous.
Now, in order to be possible to show and hide the menu item image in a fancy way, we need to create that specific markup we’ve shown in the beginning and append it to the item. Remember, our menu item is this by default:
The hover-reveal element will be the one moving as we move the mouse. The hover-reveal__inner element together with the hover-reveal__img (the one with the background image) will be the ones that we can animate together to create fancy animations like reveal/unreveal effects.
The last step is to initialize some events. We need to show the image when hovering the item and hide it when leaving the item.
Also, when hovering it we need to update the animatableProperties object properties, and make the image move, rotate and change its brightness as the mouse moves:
Let’s now code the showImage and hideImage functions.
We can create a GSAP timeline for this. Let’s start by setting the opacity to 1 for the reveal element (the top element of that structure we’ve just created). Also, in order to make the image appear on top of all other menu items, let’s set the item’s z-index to a high value.
Next, we can animate the appearance of the image. Let’s do it like this: the image gets revealed to the right or left, depending on the mouse x-axis movement direction (which we have in direction.x). For this to happen, the image element (revealImage) needs to animate its translationX value to the opposite side of its parent element (revealInner element). That’s basically it:
Now we just need to update the animatableProperties object properties so the image can move around, rotate and change its brightness smoothly. We do this inside a requestAnimationFrame loop. In every cycle we interpolate the previous and current values so things happen with an easing.
We want to rotate the image and change its brightness depending on the x-axis speed (or distance traveled from the previous cycle) of the mouse. Therefore we need to calculate that distance for every cycle which we can get by subtracting the mouse position from the cached mouse position.
We also want to know in which direction we move the mouse since the rotation will be dependent on it. When moving to the left the image rotates negatively, and when moving to the right, positively.
Next, we want to update the animatableProperties values. For the translationX and translationY, we want the center of the image to be positioned where the mouse is. Note that the original position of the image element is on the left side of the menu item.
The rotation can go from -60 to 60 degrees depending on the speed/distance of the mouse and its direction. Finally the brightness can go from 1 to 4, also depending on the speed/distance of the mouse.
In the end, we take these values together with the previous cycle values and use interpolation to set up a final value that will then give us that smooth feeling when animating the element.
Google Photos is the best service for backing up your digital photos to the cloud. They have no storage restrictions, you can upload images as well as videos, and the built-in visual search engine helps you find photos by faces or objects in the picture. There’s one feature though that’s still missing in Google Photos.
You can easily share your photos with anyone using a simple link but Google Photos offers no option for you to embed an existing image into a website. That is, if you have already uploaded an image onto Google Photos, you can’t directly embed it into your website through Google Photos.
Embed Google Photos is a new web app that, as the name suggests, makes it extremely easy for you to pick any image hosted on Google Photos and place it on a web page using simple HTML code.
Here’re the steps involved:
Go to photos.google.com and open any image that you wish to embed in your website.
Tap the Share Icon (video tutorial) and then click the Get Link button to generate a shareable link of that image.
Go to j.mp/EmbedGooglePhotos, paste that link and it will instantly generate the embed code for your selected picture.
That’s it. Open your website template, paste the generated code and save (see sample). The image will now serve directly from your Google Photos account. This technique can also be used for embedding images in HTML Mail without having to use an external image hosting service.
Embed Google Photos - How it works?
When you share any single photo in Google Photos, it creates an unlisted link that is accessible to anyone including those who are not logged into their Google Accounts. Internally, the embed app downloads the page behind this link and extracts the Open Graph tags to determine the direct link of the image and the underlying photo album.
Change Height and Width of the Image
All images hosted inside Google Photos have a URL in a particular format:
https://lh3.googleusercontent.com/xyz=w2400
The w2400 in the URL indicates that the maximum width of the image will be 2400 pixels. However, if you have a higher resolution image, you can change the width parameter to something like w8000 meaning 8000 px wide. The height will be adjusted automatically to preserve the original aspect ratio.
You can also specify the height and width values in the URL and Google Photos will fit the image to the specified size.
https://lh3.googleusercontent.com/xyz=w1415-h944
The embed app only works for single images and not albums. One more thing. I am not aware of any bandwidth limitations for images shared via Google Photos.
A while back, I came across this tweet by Twitter Marketing, which shows a video with a really nice intro animation. So I made a quick demo, trying to implement that effect. The idea is to animate some fullscreen images rapidly, like a sequence of covering layers. It’s a nice idea for an intro splash or even a page transition.
Basically, we have a parent wrap that is set to overflow hidden that we translate up (or down), while we translate its child in the opposite direction. It looks the same as if animating a clip-path that cuts off the image. (We could also do that, but clip-path is not a very performant thing to animate like transforms.)
When doing that animation on a set of layers, we get that really nice effect seen in the video.
For this demo, I created an initial (dummy) menu layout where you can click on the middle item to trigger the animation.
By setting a fitting animation duration and delay (I’m using GSAP here), the effect can be adjusted to look as smooth or as fast as needed. Have a look at the result:
We’ve covered in the past how we can read data from Audio, using the p5.sound library and how we can use that data, to draw things in the canvas, using p5.js.
Well, what if instead of drawing a sketch, we used audio to distort an image? Today we want to show you some demos that play around that idea.
We’ve created some experiments using the theme of movie trailers where the background image of the movie poster is being distorted using a sound sample. It kind of adds some drama to an otherwise static image in this case.
We analyze the sound and map the range of frequencies, to some uniforms we pass in our fragment shader. Then depending on the effect/distortion we have, we can tweak different parameters, using the audio frequencies which constantly change overtime.
In our first demo, we create a simple sinewave in our fragment shader, by using the bass frequencies of the audio track to control its frequency and the mid frequencies to control its amplitude. Then we add the distortion in both axes (x & y) of our uv and add that distortion to the initial texture coordinates.
It looks like this:
float wave = sin(uv.y * u_bass + u_time) * u_mid;
vec2 d = vec2(wave); // could be vec2(wave, 0.0) or vec2(0.0, wave) for distortion only in 1 axis.
vec4 image = texture2D(u_texture, uv + d);
gl_FragColor = image;
The possibilities are endless if you want to play around that idea, it’s just a matter of what effect you’re after. Make sure you’re mapping values to your uniforms, that are within a range that can distort your visual, and you can always use some generic uniforms like u_time, that can put some ‘overdrive’ to your distortion.
Head over to the demos and check out the variations we’ve made.
Hope you’ll have fun with this one and be sure to share any of your own versions!
Today we’d like to share a little set of playful dragging effects with you. The idea is to animate images as they are being dragged, and distort, scale or apply filters to them. For some examples we tie the intensity of the effect to the speed of the dragging motion.
The inspiration for these effects come from two Dribbble shots by Zhenya Rynzhuk:
Once you want to show more than one image, you can’t help making a transition between them. Or is it just me?
Jokes aside, image transitions are all over the web. They can be powered by CSS, SVG or WebGL. But of course, the most efficient way to work with graphics in the browser is using the Graphics Processor, or GPU. And the the best way to do this is with WebGL, specifically with shaders written in GLSL.
Today we want to show you some interesting image transition experiments that reveal the boundless possibilities of WebGL. These effects were inspired by the countless incredible design examples and effects seen on websites like The Avener and Oversize Studio.
Setup
I will be using the Three.js framework for my transitions. It doesn’t really matter what library you use, it could have also been the amazing Pixi.js library, or simply (but not so straightforward) native WebGL. I’ve used native WebGL in my previous experiment, so this time I’m going to use Three.js. It also seems most beginner friendly to me.
Three.js uses concepts like Camera, Scene and Objects. We will create a simple Plane object, add it to Scene and put it in front of the Camera, so that it is the only thing that you can see. There is a template for that kind of object, PlaneBufferGeometry:
To cover the whole screen with a plane you need a little bit of geometry. The Camera has a fov (field of view), and the plane has a size. So with some calculations you can get it to fill your whole screen:
Looks complicated, but it’s just getting the angle(fov), knowing all the distances here:
That is actually the end of the 3D part, everything else will be happening in 2D.
GLSL
In case you are not yet familiar with this language, I highly advise you to check out the wonderful Book Of Shaders.
So, we have a plane and we have a fragment shader attached to it that calculates each pixels color. How do we make a transition? The simplest one done with a shader looks like this:
Where progress is a number between 0 and 1, indicating the progress of the animation.
With that kind of code you will get a simple fade transition between images. But that’s not that cool, right?
Cool transitions
Usually all transitions are based on changing so called UVs, or the way texture is wrapped on the plane. So for instance, multiplying UV scales the image, adding a number just shifts the image on the plane.
UVs are nothing magical. Think of them as a coordinate system for pixels on a plane:
Let’s start with some basic code:
gl_FragColor = texture2D(texture,uv);
This just shows an image on the screen. Now let’s adjust that code a bit:
gl_FragColor = texture2D(texture,fract(uv + uv));
By taking the fractional part, we make sure that all the values stay between 0 and 1. And if UV was from 0 to 1, doubling it means it will be from 0 to 2, so we should see the fractional part changing from 0 to 1, and from 0 to 1 again!
And that’s what you get: a repeated image. Now let’s try something different: subtracting UV and using the progress for the animation:
First, we make sure that we are only changing one axis of UV, by multiplying it with vec2(1.,0). So, when the progress is 0, it should be the default image. Let’s see:
Now we can stretch the image! Let’s combine those two effects into one.
So basically, we do the stretching and repeat it 5 times. We could use any other number as well.
Much better! Next, if we add another image, we get the effect that you can see in demo 7.
Cool isn’t it? Just two simple arithmetic operations, and you get an interesting transition effect.
That’s just one way of changing UVs. Check out all the other demos, and try to guess what’s the math behind them! Try to come up with your own unique animation and share it with me!
We’ll briefly go over some main concepts so you can make full use of the configurator. If you’d like to understand the main idea behind the work, and why the animations behave the way they do in more depth, we highly recommend you to read the main tutorial Creating Grid-to-Fullscreen Animations with Three.js.
Basics of the configurator
The configurator allows you to modify all the details of the effect, making it possible to create unique animations. Even though you don’t have to be a programmer to create your own effect, understanding the options available will give you more insight into what you can achieve with it.
To see your personalized effect in action, either click on the image or drag the Progress bar. The Duration option sets the time of the whole animation.
Under Easings you can control the “rate of change” of your animation. For example:
Power1.easeOut: Start really fast but end slowly
Power1.easeInOut: Start and end slowly, but go really fast in the middle of the animation
Bounce: Bounce around like a basketball
The simplest easings to play around with are Power0-4 with ease-out. If you would like to know the difference between each easing, check out this ease visualizer.
Note that the configurator automatically saves your progress for later use. Feel free to close the page and come back to it later.
Timing, Activation and Transformation
Timing, Activation and Transformation are concepts that come from our previous tutorial. Each on of them has their own list of types, that also have their own set of options for you to explore.
You can explore them by changing the types, and expanding the respective options tab. When you swap one type for another, your previous set of options is saved in case you want to go back to it.
Timing
The timing function maps the activation into actual progress for each vertex. Without timing, the activation doesn’t get applied and all the vertices move at the same rate. Set timing type to none to see it in action.
SameEnd: The vertices have different start times, but they all end at the same time. Or vice versa.
sections: Move by sections, wait for the previous section to finish before starting.
The same activation with a different timing will result in a very different result.
Activation
The activation determines how the plane is going to move to full screen:
side: From left to right.
corners: From top-left to bottom-right
radial: From the position of the mouse
And others.
For a visual representation of the current activation, toggle debug activation and start the animation to see it in action.
Transformation
Transform the plane into a different shape or position over the course of the animation:
Flip: Flip the plane on the X axis
simplex: Move the vertices with noise over the while transitioning
wavy: Make the plane wavy while transitioning
And more
Some effects, use seed for their inner workings. You can set the initial seed and determine when this seed is going to be randomized.
Note that although these three concepts allow for a large amount of possible effects, some options won’t work quite well together.
Sharing your effect
To share the effect you can simply copy and share the URL.
We would love to see what you come up with. Please share your effect in the comments or tag us on Twitter using @anemolito and @codrops.
Adding your effect to your site
Now that you made your custom effect, it is time to add it to your site. Let’s see how to do that, step by step.
First, download the code and copy some of the required files over:
Note: You can use any IDs or classes you want as long as you use them when instantiating the effect.
Inside #itemsWrapper we are going to have the HTML items for our effect.
Our HTML items inside #itemsWrapper can have almost any structure. The only requirement is that it has two image elements as the first two children of the item.
The first element is for the small-scale image and the second element is the large-scale image.
Aside from that, you can have any caption or description you may want to add at the bottom. Take a look at how we did ours in our previous post:
You may add as many items as you want. If you add enough items to make your container scrollable. Make sure to send your container in the options, so the effect can account for its scroll.
With our HTML items in place, let’s get the effect up and running.
We’ll instantiate GridToFullscreenEffect, add our custom options, and initialize it.
Our effect is now mounted and working. But clicking on an item makes the image disappear and we end up with a black square.
The effect doesn’t take care of loading the images. Instead, it requires you to give them to the effect whenever they load. This might seem a bit inconvenient, but it allows you to load your images the way it’s most suitable for your application.
You could preload all the images upfront, or you could only load the images that are on screen, and load the other ones when needed. It’s up to how you want to do that.
We decided to preload all the images using imagesLoaded like this:
imagesLoaded(document.querySelectorAll("img"), instance => {
document.body.classList.remove("loading");
// Make Images sets for creating the textures.
let images = [];
for (var i = 0, imageSet = {}; i < instance.elements.length; i++) {
let image = {
element: instance.elements[i],
image: instance.images[i].isLoaded ? instance.images[i].img : null
};
if (i % 2 === 0) {
imageSet = {};
imageSet.small = image;
}
if (i % 2 === 1) {
imageSet.large = image;
images.push(imageSet);
}
}
configurator.effect.createTextures(images);
});
With that last piece of code, our effect is running and it shows the correct images. If you are having troubles with adding it to your site, let us know!
Our Creations
While working on this configurator, we managed to create some interesting results of our own. Here are three examples. You can use the parameters and attach it to the URL or use the settings:
To better explain that title right off the bat, here’s what we’re about to learn, and it’s easier than you think. Give it a go, change the shirt from yellow to blue by using the color picker in the bottom right corner:
You can see another example of this in the demo Color this sofa! where you can change the color of a sofa and its background gradient.
Imagine this for a second: You’ve finally done it, over the summer, you and a buddy are about to launch your screen printing start up out of your shared house, it’s not much, but you have a working setup and a few local bands and non-profits have already shown interest. Your supplier for t-shirts, pants, hats and $2 sunglasses is exactly what you’ve been looking for, they supply 25 colors per item and you couldn’t be happier that your website has finally been signed off. Now all you need to do is upload some photos of your merchandise! Problem is, 25 colors per item? Thats 125 different options, how do you approach this?
You’ve seen this often happen online, and the solution is almost always a picture of one color, and little dots that represent the rest of the options.
And for good reason too, nobody wants to spend the time or money photographing someone wearing over 125 items, so like the masses before you, you get your friend to model one color of each and let your potential customer use their imagination to determine what the others look like based on a dotted color.
The reason for the preamble is to be clear about why the solution you’re about to learn is so valuable. With just a small to moderate amount of setup, you can take one photo of your product and let your web page dynamically change the color of a t-shirt, hat, sunglasses and pants to any color you throw at it, in fact, if you wanted to you can change all of these items independently on the same image, on the same web page, without it reloading.
So what’s happening here? What magic is this?
It’s an SVG element with an image behind it, and a vector shape (path element) drawn over the part(s) you want the color to change. You simply change the fill color of your path element, and use the CSS property mix-blend-mode: multiply to stain that color onto the image.
Let’s Get Started
Download this photo if you’d like to follow along.
This part is important: you want to know the dimensions of your photo, and it should ideally be something customized to your website’s needs (a square photo at 1000x1000px for instance). My example is 1920x1280px and it’s a dimension we should take note of when we set this up.
Second, the part of the photo you’re changing needs to be white. Our t-shirt is white, if we were changing a sofa material or the cladding on the side of house, the material and cladding needs to be white.
Part 1: Creating the Vector Shape / SVG Markup
The easiest way to do this is via Adobe Illustrator, and it’s the program we will be using here, but if you don’t have that, you can follow along using the free online alternative Method Draw.
Create a canvas at the same dimensions as your photo (1920 x 1280px) and place your photo directly centred in the frame.
Tip: Lock this photo so that it doesn’t budge.
The image has the Lock enabled in the layer panel to the right.
Using the Pen Tool, draw a path around the t-shirt, be sure to zoom in as much as possible and really focus on getting this path as close to pixel perfect as possible. It sounds daunting, but doing this by hand will result in the most accurate final outcome and the Pen Tool is something that while takes a bit of practice, gets faster the more you use it.
Don’t know how to use the Pen Tool? here’s a great instructional video (~5min)
A couple pointers
Undo is your friend here, it’s easier to undo an anchor and try again than it is to move it into the correct place
For what we want to do, having it a pixel bigger than the t-shirt is better than having it cutting into the t-shirt a pixel
Disable both stroke color and fill color while you do this, they seem to get in the way
Remember, you can make multiple paths around different sections of the photo if you for instance wanted to change a hat color as well as the t-shirt.
Exporting Your SVG
You now have your path around the shirt, fill it with any color to see the result, you can safely delete the underlying photo leaving you with this:
When we export this SVG, it’s important that Illustrator keeps the white space around the shape, because the photo will fill this area on our web page.
If you’re using Method Draw instead, go to View > Source, this is done by default
This isn’t done by default in Illustrator though, so we need to use File > Export > Export for Screens which exports the whole artboard, keeping the relative position intact.
Export the SVG as a file to your computer, wherever makes sense to you. Open this file with your text editor and you’ll see the SVG markup inside. This is the HTML that we will use on our webpage. Copy all of it.
Paste your markup and click on Markup, your SVG is now cleaner than what Illustrator exported.
If you’ve drawing multiple paths around different items that should be color changed independently, its worth disabling the “Merge Paths” option. This can be helpful in situations like our example though, where the shirt is two paths in the original SVG markup and ideally we’d want this as a single path element.
You want to add two ID’s to your HTML, these will be up to you, but for this example let’s use product-svg for the SVG element and product-shape for the path element.
Wrap your SVG inside a parent div, we will add the ID container for this tutorial. Add the position: relative style for the container.
The two child elements in our container should now be stacked on top of each other.
It’s important that the image never shifts in size or position compared to the SVG, we want to keep them the same size aspect ratio at all times.
In your SVG, your path element will likely have a fill attribute that includes the HEX of the color you added in Illustrator. Let’s remove that attribute and add these styles to the product-shape element.
#product-shape {
fill: #DBED64;
}
The fill property is the HEX color we will dynamically update in the next step.
Its not a stretch from here conclude that the possibilities from here are massive, all you have to do is use JavaScript to change out the fill color of your path element to any color you want, (or any color you have for offer).
If you’re comfortable with JavaScript, and you have ideas on how you would update the fill property, you’ve learned the fundamentals and can safely head off and continue on your own, if however you’d like some insight in how to do that, the rest of this tutorial covers a basic function that will let you update the fill property.
I’ve prepared some color options for us. Copy and paste the HTML below your container, and copy and paste the CSS at the bottom of your CSS.
Lets write a simple changeColor function that takes a path and a HEX
JavaScript
// Reference the color shape that was drawn over the image
const overlay = document.getElementById("product-shape");
// Click on a color
var el = document.getElementsByClassName("color");
for (var i = 0; i < el.length; i++) {
el[i].onclick = changeColor;
}
function changeColor(e) {
// get the hex color
let hex = e.target.getAttribute("data-hex");
// set the hex color
overlay.style.fill = hex;
}
Using these principles you can imagine how useful it could be for a store to be able to show actual images of all of their color options, just from an array of HEX colors. Wild.
The Fine Print
Browser support for this feature is always growing, but you should know that as of now (mid-late 2019), IE, Edge, and unfortunately Chrome for Android (but only for Android) currently don’t support the mix-blend-mode property. More info here
Worth mentioning that Mozilla considers Safari and and Safari on iOS to not support mix-blend-mode on SVG element, however it appears to work fine in both.
Final Word
As a parting gift, here’s a more complete CodePen. I’ve used the library jscolor which sends a HEX to our changeColor function. And a very useful function that will take our SVG, and simulate the CSS property background-size: cover inside a parent div. This function is worth investigating if you’re planning on implementing this feature on a website where the photo + SVG is inside a fluid parent.
Today we’d like to share a little draggable experiment with you. The idea is to show a strip of differently sized images that can be dragged. When clicking and holding to drag, a title element appears and the images get scaled. This separates the images and gives the whole thing an interesting look. When a number gets clicked, the same separation happens and then the images fly up. Another larger version of the images slides in from the bottom.
The animations are powered by TweenMax and we use Dave DeSandro’s Draggabilly.
Attention: Note that the demo is experimental and that we use modern CSS properties that might not be supported in older browsers.
The initial view looks as follows:
When we click to drag the image strip, we show the title:
When clicking on a number, we slide the images up and show a larger view with some content:
Here’s a preview of the whole thing in motion:
We hope you enjoy this experiment and find it useful!
Animations play a big role in how users feels about your website. They convey a lot of the personality and feel of your site. They also help the user navigate new and already known screens with more ease.
In this tutorial we want to look at how to create some interesting grid-to-fullscreen animations on images. The idea is to have a grid of smaller images and when clicking on one, the image enlarges with a special animation to cover the whole screen. We’ll aim for making them accessible, unique and visually appealing. Additionally, we want to show you the steps for making your own.
The building blocks
Before we can start doing all sorts of crazy animations, timing calculations and reality deformation we need to get the basic setup of the effect ready:
Initialize Three.js and the plane we’ll use
Position and scale the plane so it is similar to the item’s image whenever the user clicks an item
Animate the plane so it covers the complete screen
For the sake of not going too crazy with all the effects we can make, we’ll focus on making a flip effect like the one in our first demo.
Initialization
To begin, lets make a basic Three.js setup and add a single 1×1 plane which we’ll re-use for the animation of every grid item. Since only one animation can happen at the time. We can have better performance by only using one plane for all animations.
This simple change is going to allow us to have any number of HTML items without affecting the performance of the animation.
As a side note, in our approach we decided to only use Three.js for the time of the animation. This means all the items are good old HTML.
This allows our code to have a natural fallback for browsers that don’t have WebGL support. And it also makes our effect more accessible.
class GridToFullscreenEffect {
...
init(){
...
const segments = 128;
var geometry = new THREE.PlaneBufferGeometry(1, 1, segments, segments);
// We'll be using the shader material later on ;)
var material = new THREE.ShaderMaterial({
side: THREE.DoubleSide
});
this.mesh = new THREE.Mesh(geometry, material);
this.scene.add(this.mesh);
}
}
Setting the the plane geometry’s size to be 1×1 simplifies things a little bit. It removes a some of the math involved with calculating the correct scale. Since 1 scaled by any number is always going to return that same number.
Positioning and resizing
Now, we’ll resize and position the plane to match the item’s image. To do this, we’ll need to get the item’s getBoundingClientRect. Then we need to transform its values from pixels to the camera’s view units. After, we need to transform them from relative to the top left, to relative from the center. Summarized:
Map pixel units to camera’s view units
Make the units relative to the center instead of the top left
Make the position’s origin start on the plane’s center, not on the top left
Scale and position the mesh using these new values
class GridToFullscreenEffect {
...
onGridImageClick(ev,itemIndex){
// getBoundingClientRect gives pixel units relative to the top left of the pge
const rect = ev.target.getBoundingClientRect();
const viewSize = this.getViewSize();
// 1. Transform pixel units to camera's view units
const widthViewUnit = (rect.width * viewSize.width) / window.innerWidth;
const heightViewUnit = (rect.height * viewSize.height) / window.innerHeight;
let xViewUnit =
(rect.left * viewSize.width) / window.innerWidth;
let yViewUnit =
(rect.top * viewSize.height) / window.innerHeight;
// 2. Make units relative to center instead of the top left.
xViewUnit = xViewUnit - viewSize.width / 2;
yViewUnit = yViewUnit - viewSize.height / 2;
// 3. Make the origin of the plane's position to be the center instead of top Left.
let x = xViewUnit + widthViewUnit / 2;
let y = -yViewUnit - heightViewUnit / 2;
// 4. Scale and position mesh
const mesh = this.mesh;
// Since the geometry's size is 1. The scale is equivalent to the size.
mesh.scale.x = widthViewUnit;
mesh.scale.y = heightViewUnit;
mesh.position.x = x;
mesh.position.y = y;
}
}
As a side note, scaling the mesh instead of scaling the geometry is more performant. Scaling the geometry actually changes its internal data which is slow and expensive, while scaling the mesh happens at rendering. This decision will come into play later on, so keep it in mind.
Now, bind this function to each item’s onclick event. Then our plane resizes to match the item’s image.
It’s a very simple concept, yet quite performant in the long run. Now that our plane is ready to go when clicked, lets make it cover the screen.
We need to update uMeshScale and uMeshPositon uniforms whenever we click an item.
class GridToFullscreenEffect {
...
onGridImageClick(ev,itemIndex){
...
// Divide by scale because on the fragment shader we need values before the scale
this.uniforms.uMeshPosition.value.x = x / widthViewUnit;
this.uniforms.uMeshPosition.value.y = y / heightViewUnit;
this.uniforms.uMeshScale.value.x = widthViewUnit;
this.uniforms.uMeshScale.value.y = heightViewUnit;
}
}
Since we scaled the mesh and not the geometry, on the vertex shader our vertices still represent a 1×1 square in the center of the scene. But it ends up rendered in another position and with a different size because of the mesh. As a consequence of this optimization, we need to use “down-scaled” values in the vertex shaders. With that out of the way, lets make the effect happen in our vertex Shader:
Calculate the scale needed to match the screen size using our mesh’s scale
Move the vertices by their negative position so they move to the center
Multiply those values by the progress of the effect
We start the tween whenever we click an item. And there you go, our plane goes back and forth no matter which item we choose.
Pretty good, but not too impressive yet.
Now that we have the basic building blocks done, we can start making the cool stuff. For starters, lets go ahead and add timing.
Activation and timing
Scaling the whole plane is a little bit boring. So, lets give it some more flavor by making it scale with different patterns: Top-to-bottom, left-to-right, topLeft-to-bottomRight.
Lets take a look at how those effects behave and figure out what we need to do:
By observing the effects for a minute, we can notice that the effect is all about timing. Some parts of the plane start later than others.
What we are going to do is to create an “activation” of the effect. We’ll use that activation to determine which vertices are going to start later than others.
With this little change, our animation is not much more interesting.
Note that the gradients on the demo are there for demonstration purposes. They have nothing to do with the effect itself.
The great thing about these “activation” and “timing” concepts is that they are interchangeable implementations. This allows us to create a ton of variations.
With the activation and timing in place, lets make it more interesting with transformations.
Transformations
If you haven’t noticed, we already know how to make a transformation. We successfully scaled and moved the plane forwards and backwards.
We interpolate or move from one state to another using vertexProgress. Just like we are doing in the scale and movement:
...
const vertexShader = `
...
void main(){
...
// Base state = 1.
// Target state = uScaleToViewSize;
// Interpolation value: vertexProgress
scale = vec2(
1. + uScaleToViewSize * vertexProgress
);
// Base state = pos
// Target state = -uPlaneCenter;
// Interpolation value: vertexProgress
pos.y += -uPlaneCenter.y * vertexProgress;
pos.x += -uPlaneCenter.x * vertexProgress;
...
}
`
Lets apply this same idea to make a flip transformation:
Base state: the vertex’s current position
Target state: The vertex flipped position
Interpolate with: the vertex progress
...
const vertexShader = `
...
void main(){
...
float vertexProgress = smoothstep(startAt,1.,uProgress);
// Base state: pos.x
// Target state: flippedX
// Interpolation with: vertexProgress
float flippedX = -pos.x;
pos.x = mix(pos.x,flippedX, vertexProgress);
// Put vertices that are closer to its target in front.
pos.z += vertexProgress;
...
}
`;
Note that, because this flip sometimes puts vertices on top of each other we need to bring some of them slightly to the front to make it look correctly.
Combining these flips with different activations, these are some of the variations we came up with:
If you pay close attention to the flip you’ll notice it also flips the color/image backwards. To fix this issue we have to flip the UVs along with the position.
And there we have it! We’ve not only created an interesting and exciting flip effect, but also made sure that using this structure we can discover all kinds of effects by changing one or more of the pieces.
In fact, we created the effects seen in our demos using the configurations as part of our creative process.
There is so much more to explore! And we would love to see what you can come up with.
Here are the most interesting variations we came up with:
Different timing creation:
Activation based on mouse position, and deformation with noise:
Distance deformation and mouse position activation:
We hope you enjoyed this tutorial and find it helpful!
Today we’d like to share a little layout with you. The idea is based on the current trend of a grid layout where the columns are animated. You can see this kind of animation in Aristide Benoist’s amazing design for Everest or Hrvoje Grubisic’s GETZ — Photography Portfolio Website concept. In our demo, we animate a decorative image grid and make the columns move away in an alternating way, revealing some content underneath. We use a playful hover effect for the menu items and mimic the animating when they fly away. We also added some slight mouse move interaction for the columns.
Attention: Note that the demo is experimental and that we use modern CSS properties that might not be supported in older browsers.
The initial view of the demo is the navigation with the decorative grid in the background.
When clicking on one of the menu items, we animate the grid out by moving the columns in an alternating fashion, rotating them slightly. We mimic this behavior on the letters of the menu item.
When the grid moves away, the content area underneath is revealed:
We hope you enjoy this experiment and find it useful!
If you recently browsed Awwwards or FWA you might have stumbled upon Ultranoir’s website. An all-round beautifully crafted website, with some amazing WebGL effects. One of which is a sticky effect for images in their project showcase. This tutorial is going to show how to recreate this special effect.
The same kind of effect can be seen on the amazing website of MakeReign.
Understanding the effect
When playing with the effect a couple of times we can make a very simple observation about the “stick”.
In either direction of the effect, the center always reaches its destination first, and the corners last. They go at the same speed, but start at different times.
With this simple observation we can extrapolate some of the things we need to do:
Differentiate between the unsticky part of the image which is going to move normally and the sticky part of the image which is going to start with an offset. In this case, the corners are sticky and the center is unsticky.
Sync the movements
Move the unsticky part to the destination while not moving the sticky part.
When the unsticky part reaches its destination, start moving the sticky part
Getting started
For this recreation we’ll be using three.js, and Popmotion’s Springs. But you can implement the same concepts using other libraries.
We’ll define a plane geometry with its height as the view height, and its width as 1.5 of the view width.
const camera = new THREE.PerspectiveCamera(45, 1, 0.1, 10000);
const fovInRadians = (camera.fov * Math.PI) / 180;
// Camera aspect ratio is 1. The view width and height are equal.
const viewSize = Math.abs(camera.position.z * Math.tan(fovInRadians / 2) * 2);
const geometry = new THREE.PlaneBufferGeometry(viewSize *1.5,viewSize,60,60)
Then we’ll define a shader material with a few uniforms we are going to use later on:
u_progress Elapsed progress of the complete effect.
u_direction Direction to which u_progress is moving.
u_offset Largest z displacement
const material = new THREE.ShaderMaterial({
uniforms: {
// Progress of the effect
u_progress: { type: "f", value: 0 },
// In which direction is the effect going
u_direction: { type: "f", value: 1 },
u_waveIntensity: { type: "f", value: 0 }
},
vertexShader: vertex,
fragmentShader: fragment,
side: THREE.DoubleSide
});
We are going to focus on the vertex shader since the effect mostly happens in there. If you have an interest in learning about the things that happen in the fragment shader, check out the GitHub repo.
Into the stick
To find which parts are going to be sticky we are going to use a normalized distance from the center. Lower values mean less stickiness, and higher values mean more sticky. Since the corners are the farthest away from the center, they end up being most sticky.
Since our effect is happening in both directions, we are going to have it stick both ways. We have two separate variables:
One that will stick to the front. Used when the effect is moving away from the screen.
And a second one that will stick to the back. Used when the effect is moving towards the viewer.
Depending on the direction, we are going to determine which parts are not going to move as much. Until we want them to stop being sticky and move normally.
The Animation
For the animation we have a few options to choose from:
Tween and timelines: Definitely the easiest option. But we would have to reverse the animation if it ever gets interrupted which would look awkward.
Springs and vertex-magic: A little bit more convoluted. But springs are made so they feel more fluid when interrupted or have their direction changed.
In our demo we are going to use Popmotion’s Springs. But tweens are also a valid option and ultranoir’s website actually uses them.
Note: When the progress is either 0 or 1, the direction will be instant since it doesn’t need to transform.
And we are going to sequence the movements by moving through a wave using u_progress.
This wave is going to start at 0, reach 1 in the middle, and come back down to 0 in the end. Making it so the stick grows in the beginning and decreases in the end.
Now, the last step is to move the plane back or forward as the stick is growing.
Since the stick grow starts in different values depending on the direction, we’ll also move and start the plane offset depending on the direction.
void main(){
...
float offsetIn = clamp(waveIn,0.,1.);
// Invert waveOut to get the slope moving upwards to the right and move 1 the left
float offsetOut = clamp(1.-waveOut,0.,1.);
float offsetProgress = mix(offsetIn,offsetOut,u_direction);
pos.z += stickEffect * u_offset * stickProgress - u_offset * offsetProgress;
gl_Position =
projectionMatrix *
modelViewMatrix *
vec4(pos, 1.0);
}
And here is the final result:
Conclusion
Simple effects like this one can make our experience look and feel great. But they only become amazing when complemented with other amazing details and effects. In this tutorial we’ve covered the core of the effect seen on ultranoir’s website, and we hope that it gave you some insight on the workings of such an animation. If you’d like to dive deeper into the complete demo, please feel free to explore the code.
We hope you enjoyed this tutorial, feel free to share your thoughts and questions in the comments!