Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shshaw/0f5e9a89e2cafcfcb9103496f1c3dd57 to your computer and use it in GitHub Desktop.
Save shshaw/0f5e9a89e2cafcfcb9103496f1c3dd57 to your computer and use it in GitHub Desktop.
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