Mastering Enter/Exit Animations with Sandal SCSS Mixins and ES6
This tutorial explains how to implement enter and exit animations using Sandal's SCSS mixins and an ES6 JavaScript class, covering mixin usage, generated CSS, customizable parameters, and practical usage examples for seamless UI effects.
This tutorial series provides a practical guide to implementing enter and exit animations using the Sandal UI framework and Sheral UI, originally published on imweb, w3cplus, and the “Frontend Talk” WeChat channel.
Enter/Exit Animations
In
_animation.scsssix basic animation groups are defined (fade‑in/out, shrink‑in/out, up‑in/out, down‑in/out, left‑in/out, right‑in/out). The example below shows how to use the fade‑in/out mixins.
<code>@include animation-fade-in;
@include animation-fade-out;</code>The compiled CSS for the fade animations is:
<code>.fade-in, .fade-out {
-webkit-animation-duration: 0.3s;
animation-duration: 0.3s;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}
.fade-in {
-webkit-animation-name: fadeIn;
animation-name: fadeIn;
}
@-webkit-keyframes fadeIn {
0% { opacity: 0; }
100% { opacity: 1; }
}
@keyframes fadeIn {
0% { opacity: 0; }
100% { opacity: 1; }
}
.fade-out {
-webkit-animation-name: fadeOut;
animation-name: fadeOut;
}
@-webkit-keyframes fadeOut {
0% { opacity: 1; }
100% { opacity: 0; }
}
@keyframes fadeOut {
0% { opacity: 1; }
100% { opacity: 0; }
}</code>The mixins accept two optional parameters:
animation-fade-in($className: fade, $from: 0),
animation-fade-out($className: fade, $to: 0), where the first sets the class name (automatically appending “‑in” or “‑out”) and the second defines the start or end opacity.
ES6 Encapsulated Animation Class
<code>export class AnimateInOut {
constructor({ele, className, inCallback, outCallback}) {
this.ele = ele.nodeType === 1 ? ele : document.querySelector(ele);
this.inClass = className + '-in';
this.outClass = className + '-out';
this.inCallback = inCallback;
this.outCallback = outCallback;
this.animationend = this.whichEndEvent();
this.endBind = this.end.bind(this);
}
// Enter
enter() {
this.ele.classList.add(this.inClass);
this.ele.addEventListener(this.animationend, this.endBind);
}
// Leave
leave() {
this.ele.classList.add(this.outClass);
this.ele.addEventListener(this.animationend, this.endBind);
}
// Animation end handler
end() {
const ele = this.ele,
eleClassList = ele.classList,
isIn = eleClassList.contains(this.inClass),
isOut = eleClassList.contains(this.outClass);
ele.removeEventListener(this.animationend, this.endBind);
if (isIn) {
eleClassList.remove(this.inClass);
this.inCallback && this.inCallback();
}
if (isOut) {
eleClassList.remove(this.outClass);
this.outCallback && this.outCallback();
}
}
// Detect appropriate animationend event
whichEndEvent() {
const el = document.createElement('div');
const animations = {
"animation": "animationend",
"WebkitAnimation": "webkitAnimationEnd"
};
for (let k in animations) {
if (el.style[k] !== undefined) {
return animations[k];
}
}
}
}
</code>Note: This implementation uses CSS
animationrather than
transitionbecause adding a transition class when the element changes from
display:noneto
display:blockdoes not trigger an animation, whereas
animationdoes.
Usage
<code>function leaveEnd() {
console.log('hello the world');
}
const animateInOut = new AnimateInOut({ele: $el, className: 'fade', outCallback: leaveEnd});
animateInOut.enter(); // trigger enter animation
animateInOut.leave(); // trigger leave animation
</code>The tutorial series is ongoing and more content will be added.
Tencent IMWeb Frontend Team
IMWeb Frontend Community gathering frontend development enthusiasts. Follow us for refined live courses by top experts, cutting‑edge technical posts, and to sharpen your frontend skills.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.