From our sponsor: Ready to show your plugin skills? Enter the Penpot Plugins Contest (Nov 15-Dec 15) to win cash prizes!
Today we’d like to show you how to create a little experimental glitch-like effect on an image. The effect will be powered by CSS animations and the clip-path property. The technique involves using several layers of images where each one will have a clip-path, a blend mode and a translation applied to it. It was inspired by the technique seen on the speakers page of the 404 conference (link is dead, sorry).
We also use CSS variables for setting some properties that will allow for an easy adjustment of the effect.
Breaking down the effect
When searching the web for an easy to use and light-weight glitch implementation, we came across this question on Reddit. Somebody was asking how the glitch effect was pulled off on the speaker line up page of the 404 conference. The glitch effect was made using CSS animations on a stack of three images that are the same. The animations consist of a rapidly changing clip property on all layers except the first one. Additionally, the layers are being moved slightly. So what we are seeing, is slices of the image, slightly offset and in constant movement.
We wanted to experiment with this and recreate the effect using the clip-path property instead. Although it has less browser support (it doesn’t work in IE or Edge), it allows for a more flexible usage since we can use percentage values and apply it to elements that are not necessarily positioned absolutely.
Combining the effect with background blend modes, allows us to create some interesting looking image effects.
The way this works is to create an image stack where each overlaying image will animate its clip-path in, what looks like, random sizes. We’ll use a stack of 5 images:
<div class="glitch glitch--style-1">
<div class="glitch__img"></div>
<div class="glitch__img"></div>
<div class="glitch__img"></div>
<div class="glitch__img"></div>
<div class="glitch__img"></div>
</div>
Let’s have a look at the main styles for the hover effect that you can see in the last demo. Note that we’ve defined some variables previously, but they should be self-explanatory:
.glitch {
position: relative;
width: var(--glitch-width);
max-width: 400px;
height: var(--glitch-height);
max-height: calc(400px * 1.25);
overflow: hidden;
margin: 0 auto;
}
.glitch:hover {
cursor: pointer;
}
.glitch__img {
position: absolute;
top: calc(-1 * var(--gap-vertical));
left: calc(-1 * var(--gap-horizontal));
width: calc(100% + var(--gap-horizontal) * 2);
height: calc(100% + var(--gap-vertical) * 2);
background: url(../img/1.jpg) no-repeat 50% 0;
background-color: var(--blend-color-1);
background-size: cover;
background-blend-mode: var(--blend-mode-1);
}
We don’t want to show the sides being cut off, so we make sure that the image dimensions take the gap, i.e. the movement into consideration.
Then, we set the background colors and blend modes for each layer:
/* Set the background colors for the glitch images*/
.glitch__img:nth-child(2) {
background-color: var(--blend-color-2);
background-blend-mode: var(--blend-mode-2);
}
.glitch__img:nth-child(3) {
background-color: var(--blend-color-3);
background-blend-mode: var(--blend-mode-3);
}
.glitch__img:nth-child(4) {
background-color: var(--blend-color-4);
background-blend-mode: var(--blend-mode-4);
}
.glitch__img:nth-child(5) {
background-color: var(--blend-color-5);
background-blend-mode: var(--blend-mode-5);
}
As this is going to be a hover effect, we want all layers except the first one to be hidden by default:
.glitch__img:nth-child(n+2) {
opacity: 0;
}
Then, on hover, we show all layers:
.glitch:hover .glitch__img:nth-child(n+2) {
opacity: 1;
}
And we also apply the animations and a transform to each layer:
.glitch:hover .glitch__img:nth-child(2) {
transform: translate3d(var(--gap-horizontal),0,0);
animation: glitch-anim-1-horizontal var(--time-anim) infinite linear alternate;
}
.glitch:hover > .glitch__img:nth-child(3) {
transform: translate3d(calc(-1 * var(--gap-horizontal)),0,0);
animation: glitch-anim-2-horizontal var(--time-anim) infinite linear alternate;
}
.glitch:hover > .glitch__img:nth-child(4) {
transform: translate3d(0, calc(-1 * var(--gap-vertical)), 0) scale3d(-1,-1,1);
animation: glitch-anim-3-horizontal var(--time-anim) infinite linear alternate;
}
/* Hover flash animation on last image */
.glitch:hover > .glitch__img:nth-child(5) {
animation: glitch-anim-flash 0.5s steps(1,end) infinite;
}
The calc(-1 * var(--gap-horizontal))
basically allows us to set a negative value of a given variable.
Have a look at this slow motion visualization to see what’s going on under the hood (this GIF is quite big, so it might take a while to load):
The last layer is only flashing and moving slightly while the others also get cut by a clip-path.
Let’s have a look at one of the animations for setting the clip-path:
@keyframes glitch-anim-1-horizontal {
0% {
-webkit-clip-path: polygon(0 2%, 100% 2%, 100% 5%, 0 5%);
clip-path: polygon(0 2%, 100% 2%, 100% 5%, 0 5%);
}
10% {
-webkit-clip-path: polygon(0 15%, 100% 15%, 100% 15%, 0 15%);
clip-path: polygon(0 15%, 100% 15%, 100% 15%, 0 15%);
}
20% {
-webkit-clip-path: polygon(0 10%, 100% 10%, 100% 20%, 0 20%);
clip-path: polygon(0 10%, 100% 10%, 100% 20%, 0 20%);
}
30% {
-webkit-clip-path: polygon(0 1%, 100% 1%, 100% 2%, 0 2%);
clip-path: polygon(0 1%, 100% 1%, 100% 2%, 0 2%);
}
40% {
-webkit-clip-path: polygon(0 33%, 100% 33%, 100% 33%, 0 33%);
clip-path: polygon(0 33%, 100% 33%, 100% 33%, 0 33%);
}
50% {
-webkit-clip-path: polygon(0 44%, 100% 44%, 100% 44%, 0 44%);
clip-path: polygon(0 44%, 100% 44%, 100% 44%, 0 44%);
}
60% {
-webkit-clip-path: polygon(0 50%, 100% 50%, 100% 20%, 0 20%);
clip-path: polygon(0 50%, 100% 50%, 100% 20%, 0 20%);
}
70% {
-webkit-clip-path: polygon(0 70%, 100% 70%, 100% 70%, 0 70%);
clip-path: polygon(0 70%, 100% 70%, 100% 70%, 0 70%);
}
80% {
-webkit-clip-path: polygon(0 80%, 100% 80%, 100% 80%, 0 80%);
clip-path: polygon(0 80%, 100% 80%, 100% 80%, 0 80%);
}
90% {
-webkit-clip-path: polygon(0 50%, 100% 50%, 100% 55%, 0 55%);
clip-path: polygon(0 50%, 100% 50%, 100% 55%, 0 55%);
}
100% {
-webkit-clip-path: polygon(0 70%, 100% 70%, 100% 80%, 0 80%);
clip-path: polygon(0 70%, 100% 70%, 100% 80%, 0 80%);
}
}
The slices will go from tiny, to a bit larger and also nothing, leaving a “pause” on some of the frames and then starting again from another position.
A great tool to visualize clip paths is Clippy by Bennett Feely.
The final animation is a simple flash of the last layer:
@keyframes glitch-anim-flash {
0% {
opacity: 0.2;
transform: translate3d(var(--gap-horizontal), var(--gap-vertical), 0);
}
33%, 100% {
opacity: 0;
transform: translate3d(0,0,0);
}
}
This looks especially interesting when applying an overlay blend mode with a fitting color.
Note that we can also apply this effect to text elements. Check out the demos to see it in action!
And that’s it! We hope you’ve found some inspiration in this little experiment.
References and Credits
- Images by Unsplash.com
- imagesLoaded by Dave DeSandro
Nice 🙂
Although the last demo didn’t seem to work for me in Chrome Version 63.0.3239.84 (Official Build) (64-bit)
You need to click on the photo
Hi Mary,
this is a great post and really a great job, people can how passionate you are in what you’re doing. Keep going thanks for the post
Very cool, thanks 🙂
Really nice effects, thanks Mary !
I’m just as impressed with the visualization. Nice work.
“Does not work in IE or Edge” is a shortcut key on my computer.
Do I need a preprocessor to use this?
Awesome. It’s really amazing work. Thanks!
Amazing as always. Thanks for sharing this!
Really nice effects, I want this effect thanks!
How can I put this on a SquareSpace page?
Hi
I think this is one of the coolest effects I’ve seen. I’ve actually only seen this effect done in video editing software. I’m going to forward this to my developer to try out. Thanks for this and also for pointing out the potential issues.
Cheers
David
A very convincing effect with an elegantly simple implementation. Lovely!
very nice ! 🙂
Terrifying but equally awesome.
nice
is this disqus?