As you can see, the current implementation of the library implies changing the display
property from none
to block
. The only problem with this approach is that this property cannot be animated via CSS, which is why the modal window opens too sharply. Today we are implementing support for CSS animations when opening/closing a window.
CSS animation
Animations help improve the perception of your content and make your project truly unique.
There are several ways to implement animations, including JavaScript and CSS. CSS animations are now a very powerful tool and the main plus of such animations in performance and optimization at the browser level.
Let's take a closer look at how it works.
Keyframes
keyframes
are used to specify animation property values at various points in the animation. Keyframes determine the behavior of one animation loop. Animation can be repeated zero or more times.
The basic syntax looks like this:
@keyframes animationName {
from {
/* code here*/
}
to {
/* code here*/
}
}
In this syntax, we have a description of the animation in two states. There is also a more flexible syntax if your animation is more complex and cannot be described only by the initial and final state
@keyframes animationName {
0% {
/* code here*/
}
100% {
/* code here*/
}
}
Usage example:
CSS Animation Properties
The property that was used in the example animation: shade .5s alternate infinite ease;
it is a short-hand which can consist of 8 separate properties.
animation-name
This is the name of the keyframe specified by @keyframes
rule.
animation-name: none;
animation-name: rebound;
animation-name: rebound-slow;
animation-name: reboundFast;
animation-duration
This property determines the duration of one animation cycle. Set in seconds with s
or milliseconds with ms
. If an element has more than one animation specified, then you can set a different time for each, listing the values separated by commas.
animation-duration: 1s;
animation-duration: 200ms;
animation-duration: .25s, 2s;
animation-timing-function
This property describes how an animation will develop between each pair of keyframes.
The property accepts the following value:
- linear
- ease
- ease-in
- ease-out
- ease-in-out
- cubic-bezier
- step-start
- step-end
- steps
animation-timing-function: linear;
animation-timing-function: ease;
animation-timing-function: cubic-bezier(.2, .5, .3, .9);
animation-timing-function: steps(3, end);
animation-delay
This property is determined when the animation begins. Set in seconds with s
or milliseconds with ms
.
animation-delay: .25s;
animation-delay: 1s, 100ms;
animation-iteration-count
This property indicates how many times the animation loop is played; this is 1
by default. The value of infinite
indicates that the animation will play repeat forever.
animation-iteration-count: infinite;
animation-iteration-count: 2;
animation-direction
This property indicates whether the animation should play forward, backward, or alternately forward and backward.
Available property values:
-
normal
- This is the default value and the animation played as normal (forwards) -
reverse
- The animation is played backward -
alternate
- The animation reverses direction each cycle -
alternate-reverse
- The animation reverses direction each cycle, with the first iteration being played backward
animation-direction: normal;
animation-direction: reverse;
animation-direction: alternate;
animation-direction: alternate-reverse;
animation-fill-mode
This property defines how to apply styles to the animation object before and after it is executed. Available property values:
-
none
- Do not apply any styles to the animated element before or after the animation is executing -
forwards
- Retain styles from the last keyframe -
backwards
- Get styles from the first keyframe and retain this style during animation-delay state -
both
- Extend animation properties in both directions forwards and backwards
animation-fill-mode: none;
animation-fill-mode: forwards;
animation-fill-mode: backwards;
animation-fill-mode: both;
animation-fill-mode: none, backwards;
animation-play-state
This property determines whether the animation will start or pause. Stopping animation inside a loop is possible using JavaScript. You can also stop the animation if the state is hover
. Available property values:
-
running
- The animation is currently playing. Default value -
paused
- The animation is currently paused
animation-play-state: running;
animation-play-state: paused;
Implement animation support
Now we implement animation support in the library.
Let's start by adding a new hasAnimation
property. It'll take a boolean
value that is false
by default. In order to add an animation of the "appearance" of a modal window, we need to add a new class at the time of its opening that will contain a property that describes the animation. It seems that we can use the previously written open
method. But we need this class for the duration of the animation and after the animation is complete, it should be deleted. All this logic will be written in a separate preparationOpeningModal
method
/**
* Preparing a modal window for opening
*/
preparationOpeningModal() {
if (this.hasAnimation) {
this.$modal?.classList.add(CLASS_NAMES.IS_OPENING);
const handler = () => {
this.$modal?.classList.remove(CLASS_NAMES.IS_OPENING);
this.$modal?.removeEventListener('animationend', handler);
};
this.$modal?.addEventListener('animationend', handler);
}
}
If hasAnimation
is false we don't need to do anything. The animationend
event is fired when a CSS animation has completed. In our case, after the modal window is opened, the class name .isOpening
is added and the animationend
event is subscribed. After the animation is complete, we delete the subscription and the .isOpening
class name.
Styles for an opening modal window:
:root {
--animation: cubic-bezier(0.66, 0.28, 0.09, 0.53);
}
.modal.isOpening {
animation: fadeIn .35s var(--animation);
}
.modal.isOpening .modal__container {
animation: downUp .35s var(--animation);
}
@keyframes downUp {
0% {
transform: translateY(8%);
}
100% {
transform: translateY(0);
}
}
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
And here is our result. A modal window that appears smoothly
But closing this window is still fast, since we haven't added a separate method for this. Let's fix this
/**
* Preparing a modal window for closing
*/
preparationClosingModal() {
if (this.hasAnimation) {
this.$modal?.classList.add(CLASS_NAMES.IS_CLOSING);
const handler = () => {
this.$modal?.classList.remove(CLASS_NAMES.IS_CLOSING);
this.$modal?.classList.remove(this.openClass);
this.$modal?.removeEventListener('animationend', handler);
};
this.$modal?.addEventListener('animationend', handler);
} else {
this.$modal?.classList.remove(this.openClass);
}
}
As you can see, this is very similar to what we did in preparationOpeningModal
, but there is still a significant difference. Deleting the main class should only occur after the animation is complete (if there is one), so we will transfer this part of the code from the close
method to the preparationClosingModal
method. For the animation when closing, we'll use the class name .isClosing
.
Add styles:
.modal.isClosing {
animation: fadeOut .35s var(--animation);
}
.modal.isClosing .modal__container {
animation: centerUp .35s var(--animation);
}
@keyframes centerUp {
0% {
transform: translateY(0);
}
100% {
transform: translateY(-8%);
}
}
@keyframes fadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
Now we have animation in two directions
Thanks for reading! Next time we'll add a few callbacks and add a small implementation for keyboard control. Also, very soon I'll return to what I love endlessly - creating templates, and these will be ready-made templates for modal Windows that you can immediately use with the library. Subscribe, it'll be interesting! See you soon 👋
Top comments (0)