Gerard Sans | @gerardsans
SANS
GERARD
Spoken at 97 events in 26 countries
900
1.5K
DURATION
opacity: 0;
opacity: 1;
PROPERTY
transition: opacity 1s ease-in 1s;
DURATION
TIMING
DELAY
<div class="container">
<div class="circle base elastic"></div>
</div>
.circle {
border-radius: 50%;
position: absolute; opacity:0;
will-change: opacity, transform;
}
.base {
transition: opacity 1s ease,
transform 1s ease;
}
.elastic {
transition-timing-function:
cubic-bezier(.8,-0.6,0.2,1.5);
}
.container:hover .circle {
transform: translateX(80px);
opacity:1;
}
all background* border* bottom box-shadow clip clip-path color filter font* height left margin* mask* offset* opacity outline* padding* perspective* right text-decoration text-shadow top transform vertical-align visibility width z-index
DURATION
0%
100%
KEYFRAMES
NAME
animation:  fade 1s 1s infinite linear;
DURATION
DELAY
ITERATIONS
TIMING
<div class="circle base elastic"></div>
.base {
animation: fade 2s infinite;
}
@keyframes fade {
0% {
transform: translateX(0px);
opacity: 0;
}
40%, 60% {
transform: translateX(80px);
opacity: 1;
}
100% {
transform: translateX(0px);
opacity: 0;
}
}
60 FPS
Time
16.66ms
16.66ms
16.66ms
1000/60ms
paint
frame
paint
frame
paint
frame
😃
paint
frame
paint
frame
60 FPS
Time
16.66ms
16.66ms
16.66ms
😱
Move
transform: translateX(10px);
Resize
transform: scale(0.5);
Rotate
transform: rotate(1turn);
Fade
opacity: 0;
<div class="circle base elastic"></div>
.base {
animation: move 2s infinite;
}
.elastic {
animation-timing-function:
cubic-bezier(.8,-0.6,0.2,1.5);
}
@keyframes move {
0% {
transform: translateX(-250px);
}
40%, 60% {
transform: translateX(50px);
}
100% {
transform: translateX(250px);
}
}
<div class="square base linear"></div>
.base {
animation: spin 2s infinite;
}
.linear {
animation-timing-function: linear;
}
@keyframes spin {
to {
transform: rotate(1turn);
}
}
<div class="circle base"></div>
.base {
animation: size 2s infinite;
}
@keyframes size {
0%, 40%, 100% {
transform: scale(1);
}
25%, 60% {
transform: scale(1.1);
}
}
.fade-in
.fade-out
opacity: 1
opacity: 0
transition: opacity 1s;
.fade
show/hide
<template>
<div class="box" @click="show=!show">
<div class="circle fade"
:class="show?'fade-in':'fade-out'"></div>
</div>
</template>
<script>
export default {
data() {
return {
show: false
};
} }
</script>
src/app.component.ts
src/App.vue
<style>
.fade {
transition:
opacity 400ms cubic-bezier(.8,-0.6,0.2,1.5),
transform 400ms cubic-bezier(.8,-0.6,0.2,1.5);
}
.fade-in {
opacity: 1;
transform: translateX(80px);
}
.fade-out {
opacity: 0;
}
</style>
src/app.component.ts
src/App.vue
CSS
JS
v-leave
v-leave-active
v-leave-to
v-enter
v-enter-active
v-enter-to
opacity:0
opacity:1
setup
setup
DOM added
DOM removed
raf
raf
opacity:0
opacity:1
<template>
<div class="box" @click="show=!show">
<transition name="fade">
<div class="circle" v-if="show"></div>
</transition>
</div>
</template>
<script>
export default {
data() {
return {
show: false
};
</script>
src/app.component.ts
src/App.vue
<style>
.circle {
transform: translateX(80px);
}
.fade-enter, .fade-leave-to {
opacity: 0;
transform: translateX(-80px);
}
.fade-enter-active, .fade-leave-active {
transition: opacity 400ms cubic-bezier(.8,-0.6,0.2,1.5),
transform 400ms cubic-bezier(.8,-0.6,0.2,1.5);
}
.fade-enter-to, .fade-leave {
opacity: 1;
}
</style>
src/app.component.ts
src/App.vue
CSS
JS
<template>
<div class="box" @click="show=!show">
<transition name="fade" :css="false"
@enter="fadeIn" @leave="fadeOut">
<div ref="circle" class="circle" v-if="show"></div>
</transition>
</div>
</template>
<script>
export default {
methods: {
fadeIn: function(el, done){ ... },
} };
</script>
src/app.component.ts
src/App.vue
fadeIn: function(el, done) {
const animation = this.toggle(el, 0, 1);
animation.onfinish = done;
},
toggle: function(el, from, to){
const start = from?80:0;
const end = from?0:80;
return el.animate([
{ opacity: from, transform: `translateX(${start}px)` },
{ opacity: to, transform: `translateX(${end}px)` }
], {
duration: 400, fill: "both",
easing: 'cubic-bezier(.8,-0.6,0.2,1.5)'
});
}
src/app.component.ts
src/App.vue
source: blog
<div class="buttons-container">
<button @click="add()">+</button>
<button @click="remove(0)" :disabled="list.length===0">-</button>
</div>
<div class="list-container">
<transition-group name="list">
<div class="box"
v-for="(item, index) in list"
:key="item">
</div>
</transition-group>
</div>
src/app.component.ts
src/App.vue
.list-enter-to
scale(1)
opacity 1
scale(0.5)
opacity 0
.list-enter
.list-enter-active
add
<style>
.list-enter {
transform: scale(0.5);
opacity: 0;
}
.list-enter-to {
transform: scale(1);
opacity: 1;
}
.list-enter-active {
transition: transform 1s cubic-bezier(0.8,-0.6,0.2,1.5),
opacity 1s cubic-bezier(0.8,-0.6,0.2,1.5);
}
</style>
src/app.component.ts
src/App.vue
STATE
STATE
scale(0.5)
opacity 0
scale(1)
opacity 1
.list-leave-active
remove
.list-leave-to
.list-leave
<style>
.box.list-leave {
transform: scale(1); opacity: 1;
height: 50px; margin: 5px;
}
.box.list-leave-to {
transform: scale(0.5); opacity: 0;
height: 0px; margin: 0px;
}
.list-leave-active {
transition: transform 1s cubic-bezier(0.8,-0.6,0.2,1.5),
opacity 1s cubic-bezier(0.8,-0.6,0.2,1.5),
height 1s cubic-bezier(0.8,-0.6,0.2,1.5),
margin 1s cubic-bezier(0.8,-0.6,0.2,1.5);
}
</style>
src/app.component.ts
src/App.vue
time
<div class="list-container">
<transition-group name="list"
appear @appear="this.stagger = true">
<div class="box"
v-for="(item, index) in list" :key="item"
:style="this.stagger?null:{transitionDelay: index*300+'ms'}">
</div>
</transition-group>
</div>
src/app.component.ts
src/App.vue