Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Circular Mask Transition with CSS | 💺🔴 Chair Circle | @keyframers 2.2.0

Circular Mask Transition with CSS | 💺🔴 Chair Circle | @keyframers 2.2.0

David Khourshid and Stephen Shaw round up to make this circular image transition using CSS masks, some fancy text effects and mouse position tracking with CSS variables.

Skip around: 1:50 Animation overview 3:06 Start coding 1:03:50 Keyflections

Like what we're doing? There are many ways you can support @keyframers so we can keep live coding awesome animations!

Topics covered:

  • CSS transitions
  • CSS masks
  • CSS Variables for mouse position

A Pen by @keyframers on CodePen.

License.

<a href="https://youtu.be/PDSWq6WaJMk" target="_blank" data-keyframers-credit style="color: #000"></a>
<script src="https://codepen.io/shshaw/pen/QmZYMG.js"></script>
<main id="app">
<div class="product">
<figure class="photo">
<img src="https://source.unsplash.com/RECZjSWMPVI/1200x900" alt="">
</figure>
<div class="title">
<h2>
<div><span>BEEF</span></div>
<div><span>WELLINGTON</span></div>
<div><span>CHAIR</span></div>
</h2>
</div>
</div>
<div class="product">
<figure class="photo">
<img src="https://source.unsplash.com/3IX-Tz_0ZN0/1200x900" alt="">
</figure>
<div class="title">
<h2>
<div><span>SO MANY</span></div>
<div><span>CHAIR</span></div>
<div><span>TABLES</span></div>
</h2>
</div>
</div>
<div class="product">
<figure class="photo">
<img src="https://source.unsplash.com/hTPUYIIvZBY/1200x900" alt="">
</figure>
<div class="title">
<h2>
<div><span>I HAVE</span></div>
<div><span>THIS IKEA</span></div>
<div><span>CHAIR</span></div>
</h2>
</div>
</div>
<div class="product">
<figure class="photo">
<img src="https://source.unsplash.com/m1JPKkCHhkc/1200x900" alt="">
</figure>
<div class="title">
<h2>
<div><span>CLOSE-UP</span></div>
<div><span>OF A</span></div>
<div><span>CHAIR</span></div>
</h2>
</div>
</div>
<div class="mouse-tracker">
</div>
</main>
console.clear();
const elApp = document.querySelector("#app");
const elProducts = document.querySelectorAll(".product");
let product = 0;
elProducts[product].dataset.active = true;
elApp.dataset.product = product + 1;
elApp.dataset.productCount = elProducts.length;
app.addEventListener("click", () => {
delete elProducts[product].dataset.active;
product = (product + 1) % elProducts.length;
elApp.dataset.product = product + 1;
elProducts[product].dataset.active = true;
});
/* ---------------------------------- */
document.addEventListener("mousemove", e => {
document.documentElement.style.setProperty("--mouse-x", e.clientX);
document.documentElement.style.setProperty("--mouse-y", e.clientY);
});
@import url("https://fonts.googleapis.com/css?family=Roboto");
*,
*:before,
*:after {
box-sizing: border-box;
position: relative; // fight me
}
body,
html {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
font-family: Roboto, Helvetica, sans-serif;
font-size: 300;
background: #aaa;
}
// _+_+_+_+_+_+_+_+_+-=-=3-=-3=-=3-=3-3=-3=-
#app {
height: 100%;
width: 100%;
&:before, &:after {
position: absolute;
font-size: 3vmin;
letter-spacing: -.1vmin;
z-index: 100000;
}
&:before {
content: 'KEY FRAMERS';
position: absolute;
top: 2vh;
left: 2vw;
}
&:after {
content: attr(data-product) ' / ' attr(data-product-count);
position: absolute;
bottom: 2vh;
left: 2vw;
font-size: 6vmin;
}
}
.product {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
&:after {
content: "";
}
> .photo {
grid-column: 1 / -1;
grid-row: 1 / -1;
margin: 0;
background: #fff;
img {
opacity: 0.8;
display: block;
object-fit: cover;
width: 100%;
height: 100%;
}
}
> .title {
grid-column: 2;
grid-row: 2;
font-size: 8vmin;
line-height: 1;
display: flex;
flex-direction: column;
justify-content: flex-end;
padding: 1rem;
h2 {
margin: 0;
font-weight: 300;
letter-spacing: -0.5vmin;
line-height: 0.9;
}
span {
display: block;
white-space: nowrap;
overflow: hidden;
}
}
}
.title div {
overflow: hidden;
&:nth-child(2) {
text-align: center;
}
&:nth-child(3) {
text-align: right;
}
}
/* ---------------------------------- */
:root {
--duration: 1s;
--easing: cubic-bezier(.2, 0, .3, 1);
}
.product {
> .photo {
mask-image: radial-gradient(#000 70%, transparent 70.1%);
mask-repeat: no-repeat;
mask-position: center center;
// mask-position:
// calc(50% + (var(--mouse-down-x) / 50vw))
// calc(50% + (var(--mouse-down-y) / 50vh));
mask-size: 0vmax 0vmax;
transition: mask-size var(--duration) var(--easing);
transition-delay: 0.5s;
}
.title span {
transform: translateY(-100%);
transition: transform var(--duration) var(--easing);
}
.title div:nth-child(2) span {
transform: translateY(100%);
}
}
.product[data-active] {
z-index: 10;
> .photo {
mask-size: 150vmax 150vmax;
transition-delay: 0s;
}
.title span {
transform: none !important;
}
+ .product {
.title span {
visibility: hidden;
transform: translateY(100%);
}
.title div:nth-child(2) span {
transform: translateY(-100%);
}
}
}
/* ---------------------------------- */
:root {
--mouse-x: 50vw;
--mouse-y: 50vh;
}
.mouse-tracker {
z-index: 999;
position: fixed;
top: 0;
left: 0;
width: 10vmin;
height: 10vmin;
background: rgba(255,255,255,.2);
border-radius: 50%;
border: solid 2px #000;
opacity: 0.8;
transform:
translate(-50%, -50%)
translate(
calc(var(--mouse-x) * 1px),
calc(var(--mouse-y) * 1px)
);
pointer-events: none;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.