Hello everyone, today I’m going to share with you how to implement a simple Tabbar dynamic effect for SVG and adapt it to mobile platforms. The demo link
Dynamic effect analysis
Let’s first analyze the movement of the element shown in Tabbar:
- Animation beginning stage: the whole icon similar to the heartbeat effect of shaking, and the internal small icon to disappear
- Animation runs in the middle: the icon has three layers of color inside the splash screen effect, the movement ends and stops to the last layer
- The final stage of animation: the middle small circle of the awkward chat icon is displayed, and the other three small internal ICONS turn white and display line movement.
Tabbar icon drawing
After the animation analysis, we first prepared for the work. Since there is no original file for the animation effect, we need to export the 48×48 size icon drawing on the software by ourselves, or draw by ourselves with the code.
Most designers provide source files, so I won’t go through the tutorial of icon drawing.
This is my drawing icon SVG file and source code by Figma: link: pan.baidu.com/s/1K8lK-c7P… Extraction code: M2wn
The basic layout of Tabbar
The page adopts REM layout and flexible. Js for adaptation. We will first write a tabbar without drawing, with the overall height of tababr 100px, the width and height of ICONS 48px and the text 20px.
<! DOCTYPEhtml>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<title></title>
<link rel="stylesheet" type="text/css" href="css/index.css" />
<script src="flexible.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div class="tabbar">
<div class="tabbar__item">
<div class="tabbar__svg"></div>
<div class="tabbar__name">Home page</div>
</div>
<div class="tabbar__item">
<div class="tabbar__svg"></div>
<div class="tabbar__name">explore</div>
</div>
<div class="tabbar__item">
<div class="tabbar__svg"></div>
<div class="tabbar__name">to make the conversation awkward</div>
</div>
<div class="tabbar__item">
<div class="tabbar__svg"></div>
<div class="tabbar__name">my</div>
</div>
</div>
</body>
</html>
Copy the code
* {padding: 0;
margin: 0;
}
/** * rem base value, divide 750 into 10 parts, each 1rem = 75px */
$baseVal : 750 / 10;
/**
* px转rem
* @param {Object} $px
*/
@function pxToRem($px) {@return $px / $baseVal * 1rem;
}
.tabbar{
position: fixed;
left: 0;
right: 0;
bottom: 0;
display: flex;
height: pxToRem(100);
background-color: #FFFFff;
border-top: 1px solid #eee;
&__item{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
flex: 1;
}
&__svg{
display: flex;
width:pxToRem(48);
height:pxToRem(48);
background-color: #AFB8CC;
}
&__name{
padding-top: pxToRem(4);;
font-size: pxToRem(20);
transition: color .3s;
color: #AFB8CC; }}Copy the code
Home page icon drawing
Create SVG
In the first tabbar__svg, we add an SVG tag, set the visual range to 48 48, and fill internally with none
<svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"></svg>
Copy the code
Example Create a cutting path
Since the flash screen effect of three colors is carried out internally and is not visible beyond the edge line, we need to set a clip-path with the same shape and size as the icon to make the animation beyond the visibility.
Open home-icon. SVG and find the clipping path with id home-clip-path:
<path id="home-clip-path" d="M27.9774 1.12273c25.5352-0.37424 22.4648-0.374244 20.0226 1.12272L6.00262 9.71614C2.44202 11.5204 0 15.2271 0 19.5068 v37.0411c0 43.0935 4.88417 48 10.9091 48h37.0909c43.1158 48 48 43.0935 48 37.0411 v19.5068c48 15.2271 45.558 11.5204 41.9974 9.71615 1.12273 L27.9774 Z" fill="#C4C4C4"/>
Copy the code
In SVG we take the first clipping path, name it id=”clipPath1″, place the path with id home-clip-path in clipPath, and then we have our clipping path made.
<clipPath id="clipPath1">
<path id="home-clip-path" d="M27.9774 1.12273c25.5352-0.37424 22.4648-0.374244 20.0226 1.12272L6.00262 9.71614C2.44202 11.5204 0 15.2271 0 19.5068 v37.0411c0 43.0935 4.88417 48 10.9091 48h37.0909c43.1158 48 48 43.0935 48 37.0411 v19.5068c48 15.2271 45.558 11.5204 41.9974 9.71615 1.12273 L27.9774 Z" fill="#C4C4C4"/>
</clipPath>
Copy the code
Add the element content in the icon
We define a
group, use the clip-path attribute in the
element to refer to the clipped path clipPath1 already defined, and copy the icon outer box element and middle small circle element from home.svg into the group as follows:
<div class="tabbar__svg">
<svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<! -- Cutting path -->
<clipPath id="clipPath1">
<path id="home-clip-path" d="M27.9774 1.12273c25.5352-0.37424 22.4648-0.374244 20.0226 1.12272L6.00262 9.71614C2.44202 11.5204 0 15.2271 0 19.5068 v37.0411c0 43.0935 4.88417 48 10.9091 48h37.0909c43.1158 48 48 43.0935 48 37.0411 v19.5068c48 15.2271 45.558 11.5204 41.9974 9.71615 1.12273 L27.9774 Z" fill="#C4C4C4"/>
</clipPath>
<! -- Group, set of elements in the icon path -->
<g clip-path="url(#clipPath1)">
<! -- Icon box -->
<path id="home-border" d="M27.9774 1.12273c25.5352-0.37424 22.4648-0.374244 20.0226 1.12272L6.00262 9.71614C2.44202 11.5204 0 15.2271 0 19.5068 v37.0411c0 43.0935 4.88417 48 10.9091 48h37.0909c43.1158 48 48 43.0935 48 37.0411 v19.5068c48 15.2271 45.558 11.5204 41.9974 9.71615 1.12273 L27.9774 Z" stroke="#B1BACD" stroke-width="8"/>
<! -- Gray circle -->
<path id="home-circle" d="M31 28C31 31.866 27.866 35 24 35C20.134 35 17 31.866 17 28C17 24.134 20.134 21 24 21C27.866 21 31 24.134 31 28Z" stroke="#B1BACD" stroke-width="4"/>
</g>
</svg>
</div>
Copy the code
So our icon is displayed, but the home-border set stroke-width=”8″ is actually displayed as 4px.
The reason:
SVG stroke mode is centered with extended strokes. That is, the inside and outside will expand by 4px, plus the clipping path cuts off the outside by 4px, so stroke-width is 8:
Create a splash screen element
Here we actually use three linear gradient circles, moving from the bottom left to the center, to delay the motion of the screen.
So we’ll create three gradient circles with radius 35 in the center cx=”24″ cy=”24″, then translate to the lower left. Since each tabbar entry has this splash screen element, we’ll create an SVG component that holds a common element for multiple graphics reuse.
<svg width="0" height="0">
<defs>
<! Transition gradient -->
<linearGradient id="transition1_linear" x1="55.4167" y1="7.29167" x2="15.3125" y2="60.5208" gradientUnits="userSpaceOnUse">
<stop stop-color="#F96C81" />
<stop offset="1" stop-color="#FFB2C1" />
</linearGradient>
<linearGradient id="transition2_linear" x1="55.4167" y1="7.29167" x2="15.3125" y2="60.5208" gradientUnits="userSpaceOnUse">
<stop stop-color="#FED966" />
<stop offset="1" stop-color="#FFECB0" />
</linearGradient>
<linearGradient id="transition3_linear" x1="55.4167" y1="7.29167" x2="15.3125" y2="60.5208" gradientUnits="userSpaceOnUse">
<stop stop-color="#86B4FF" />
<stop offset="1" stop-color="#698AFF" />
</linearGradient>
<! -- Transition element -->
<g id="transition_move1">
<circle cx="24" cy="24" r="35" fill="url(#transition1_linear)"></circle>
</g>
<g id="transition_move2">
<circle cx="24" cy="24" r="35" fill="url(#transition2_linear)"></circle>
</g>
<g id="transition_move3">
<circle cx="24" cy="24" r="35" fill="url(#transition3_linear)"></circle>
</g>
</defs>
</svg>
Copy the code
The tabar home page icon is referenced
<svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<! -- Cutting path -->
<clipPath id="clipPath1">
<path id="home-clip-path" d="M27.9774 1.12273c25.5352-0.37424 22.4648-0.374244 20.0226 1.12272L6.00262 9.71614C2.44202 11.5204 0 15.2271 0 19.5068 v37.0411c0 43.0935 4.88417 48 10.9091 48h37.0909c43.1158 48 48 43.0935 48 37.0411 v19.5068c48 15.2271 45.558 11.5204 41.9974 9.71615 1.12273 L27.9774 Z" fill="#C4C4C4"/>
</clipPath>
<! -- Group, set of elements in the icon path -->
<g clip-path="url(#clipPath1)">
<! -- Icon box -->
<path id="home-border" d="M27.9774 1.12273c25.5352-0.37424 22.4648-0.374244 20.0226 1.12272L6.00262 9.71614C2.44202 11.5204 0 15.2271 0 19.5068 v37.0411c0 43.0935 4.88417 48 10.9091 48h37.0909c43.1158 48 48 43.0935 48 37.0411 v19.5068c48 15.2271 45.558 11.5204 41.9974 9.71615 1.12273 L27.9774 Z" stroke="#B1BACD" stroke-width="8"/>
<! -- Three splash screens introduced ⚪ -->
<use class="transition_move1" href="#transition_move1"></use>
<use class="transition_move2" href="#transition_move2"></use>
<use class="transition_move3" href="#transition_move3"></use>
<! -- Gray circle -->
<path id="home-circle" d="M31 28C31 31.866 27.866 35 24 35C20.134 35 17 31.866 17 28C17 24.134 20.134 21 24 21C27.866 21 31 24.134 31 28Z" stroke="#B1BACD" stroke-width="4"/>
</g>
</svg>
Copy the code
At this point, the three circles overlap and we use CSS to move the circles out of sight in the lower left corner.
.tabbar{
.transition_move1..transition_move2..transition_move3{
transform: translate(-50px.50px); }}Copy the code
All the elements of the first icon are added, so let’s start our animation!
Icon heartbeat animation
Add the selected status. On to the first HTML item and add an animation to the tag inside. On:
<div class="tabbar__item on">.</div>
Copy the code
The heartbeat animation is implemented by scaling CSS:
.tabbar{
.on{
svg{
animation: heartbeat 1s; }}}/** ** Heartbeat animation */
@keyframes heartbeat{
0%{transform: scale(1); 20%} {transform: scale(0.8); 40%} {transform: scale(1); 60%} {transform: scale(0.9); 80%} {transform: scale(1);}
}
Copy the code
Middle circle path disappearing animation
The key to path animation is the stroke-dasharray and stroke-offset attributes.
- Stroke-dasharray: used to create dashed lines;
- Stroke-dashoffset: offset of the entire path;
We first obtain the path length of the small circle through JS, which is about 44:
let homeCirclePath = document.querySelector('#home-circle')
console.log(homeCirclePath.getTotalLength()); // 43.9885, etc
Copy the code
In SCSS, set the dotted line length and offset value of the circle, and define the key frame to make the circle’s path offset 44 to achieve the effect of path disappearance, then we can see the path animation of the small circle
.tabbar{
#home-circle{
stroke-dasharray:44 44;
stroke-dashoffset:0;
}
.on{
#home-circle{
animation: home-circle 0.5 sease forwards; }}}@keyframes home-circle{
0%{stroke-dashoffset:0; } 100%{ stroke-dashoffset:44; }}Copy the code
The splash screen animation
Move the three splash screen elements from the bottom left to the center and delay the time movement to create the splash screen effect.
.tabbar{
.transition_move1..transition_move2..transition_move3{
transform: translate(-50px.50px);
}
.on{
.transition_move1{animation: transition-move 0.5 s 0.3 sforwards; }.transition_move2{animation: transition-move 0.5 s 0.35 sforwards; }.transition_move3{animation: transition-move 0.5 s 0.4 sforwards; }}}/* Flash screen move */
@keyframes transition-move{
100%{
transform: translate(0.0); }}Copy the code
Middle small circle path display animation
After the flash screen animation, the middle circle turns white and is displayed according to the path of the small circle
Let’s change the style of @keyframes circle-path:
-
0-50% small circle gray, and small circle hidden;
-
50.1%-100% small circle white, and small circle display;
-
Change the animation duration to 1s;
.tabbar{
#circle{
stroke-dasharray:44 44;
stroke-dashoffset:0;
}
.on{
#circle{
animation: circle-path 1sease forwards; }}}/** * small circle path animation */
@keyframes circle-path{
0%{
stroke:#B1BACD;
stroke-dashoffset:0;
}
50%{
stroke:#B1BACD;
stroke-dashoffset:44;
}
501.%{
stroke:#fff;
stroke-dashoffset:-44;
}
100%{
stroke:#fff;
stroke-dashoffset:0; }}Copy the code
Change the text to blue
When dot on is selected, we’ll change the text to blue
.tabbar{
.on{
.tabbar__name{
color: #698AFF; }}}Copy the code
Explore, awkward chat, my icon draw
Icon to draw
Explore, awkward chat, my icon drawing is the same as the home page icon drawing steps.
We open the SVG files for these three and copy the required elements into THE HTML. Add the splash screen element to the middle
<! - to explore - >
<div class="tabbar__item">
<div class="tabbar__svg">
<svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<! -- Cutting path -->
<clipPath id="clipPath2">
<path id="explore-clip-path" d="M0 14.5714C0 7.62842 5.60617 2 12.5217 2H35.4783C42.3938 2 48 7.62842 48 14.5714V33.4286C48 40.3716 42.3938 46 35.4783 46H12.5217C5.60617 46 0 40.3716 0 33.4286V14.5714Z" fill="#C4C4C4"/>
</clipPath>
<! -- Group, set of elements in the icon path -->
<g clip-path="url(#clipPath2)">
<! -- Explore -- Border path -->
<path id="explore-border" d="M0 14.5714C0 7.62842 5.60617 2 12.5217 2H35.4783C42.3938 2 48 7.62842 48 14.5714V33.4286C48 40.3716 42.3938 46 35.4783 46H12.5217C5.60617 46 0 40.3716 0 33.4286V14.5714Z" stroke="#B1BACD" stroke-width="8"/>
<! -- Splash screen element -->
<use class="transition_move1" href="#transition_move1"></use>
<use class="transition_move2" href="#transition_move2"></use>
<use class="transition_move3" href="#transition_move3"></use>
<! -- Explore -- Arrow -->
<path id="explore-arrow" d="M19.4845 19.2494C19.4845 18.4796 20.3178 17.9984 20.9845 18.3833L30.1441 23.6717C30.8108 24.0566 30.8108 25.0188 30.1441 25.4037L20.9845 30.692C20.3178 31.0769 19.4845 30.5958 19.4845 29.826V19.2494Z" stroke="#B1BACD" stroke-width="4" stroke-linejoin="round"/>
</g>
</svg>
</div>
<div class="tabbar__name">explore</div>
</div>
<! - in conjunction to chat -- -- >
<div class="tabbar__item">
<div class="tabbar__svg">
<svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<! -- Cutting path -->
<clipPath id="clipPath3">
<path id="chat-clip-path" d="M24 48C37.2548 48 37.2548 48 24C48 10.7452 37.2548 0 24 0C10.7452 0 0 10.7452 0 24C0 30.5467 2.62123 36.4811 6.87143 40.8111L4.74164 44.5C3.97184 45.8333 4.93408 47.5 6.47369 47.5L23.5 48C23.5 48 23.5035 47.9983 23.5102 47.9951C23.673 48 24 48Z" fill="#C4C4C4"/>
</clipPath>
<! -- Group, set of elements in the icon path -->
<g clip-path="url(#clipPath3)">
<! -- ga -- border path -->
<path id="chat-border" d="M24 48C37.2548 48 37.2548 48 24C48 10.7452 37.2548 0 24 0C10.7452 0 0 10.7452 0 24C0 30.5467 2.62123 36.4811 6.87143 40.8111L4.74164 44.5C3.97184 45.8333 4.93408 47.5 6.47369 47.5L23.5 48C23.5 48 23.5035 47.9983 23.5102 47.9951C23.673 48 24 48Z" stroke="#B1BACD" stroke-width="8" stroke-linejoin="round"/>
<! -- Splash screen element -->
<use class="transition_move1" href="#transition_move1"></use>
<use class="transition_move2" href="#transition_move2"></use>
<use class="transition_move3" href="#transition_move3"></use>
<! -- Awkward chat -->
<circle id="chat-circle1" cx="13" cy="24" r="3" fill="#B1BACD"/>
<circle id="chat-circle2" cx="25" cy="24" r="3" fill="#B1BACD"/>
<circle id="chat-circle3" cx="37" cy="24" r="3" fill="#B1BACD"/>
</g>
</svg>
</div>
<div class="tabbar__name">to make the conversation awkward</div>
</div>
<!-- 我的 -->
<div class="tabbar__item">
<div class="tabbar__svg">
<svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<! -- Cutting path -->
<clipPath id="clipPath4">
<path id="me-clip-path" d="M48 24C48 37.2548 37.2548 48 24 48C10.7452 48 0 37.2548 0 24 0c0 10.7452 10.7452 0 24 0C37.2548 0 48 10.7452 48 24Z" fill="#C4C4C4"/>
</clipPath>
<! -- Group, set of elements in the icon path -->
<g clip-path="url(#clipPath4)">
<path id="me-border" d="M48 24C48 37.2548 37.2548 48 24 48C10.7452 48 0 37.2548 0 24 0c0 10.7452 10.7452 0 24 0C37.2548 0 48 10.7452 48 24Z" stroke="#AFB8CC" stroke-width="8"/>
<! -- Splash screen element -->
<use class="transition_move1" href="#transition_move1"></use>
<use class="transition_move2" href="#transition_move2"></use>
<use class="transition_move3" href="#transition_move3"></use>
<! -- My line -->
<path id="me-line" d="M14 30C20.5 35.5 21 30 24 30C26.5008 30 26.5 35.5 34 30" stroke="#AFB8CC" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>
</div>
<div class="tabbar__name">my</div>
</div>
Copy the code
Gets the intermediate path length
let homeCirclePath = document.querySelector('#home-circle')
let exploreArrowPath = document.querySelector('#explore-arrow')
let meLinePath = document.querySelector('#me-line')
console.log(homeCirclePath.getTotalLength()); / / 43.9885
console.log(exploreArrowPath.getTotalLength()); / / 38.0181
console.log(meLinePath.getTotalLength()); / / 22.7404
Copy the code
Keyframs @ minxi
The graphics inside the home page, Explore and my three ICONS are all path animations with the same effect. We can encapsulate @minxi and directly call to generate key frames:
/** * Generate path animation * @params {string} $name Keyframe name * @params {string} $length Path length * @params {string} $startColor Start animation path color * @params {string} $endColor End animation path color */
@mixin pathKeyframes($name.$length.$startColor:#B1BACD.$endColor:#ffffff) {
@keyframes# {$name}{
0%{
stroke:$startColor;
stroke-dashoffset:0;
}
50%{
stroke:$startColor;
stroke-dashoffset:$length;
}
501.%{
stroke:$endColor;
stroke-dashoffset:$length* -1;
}
100%{
stroke:$endColor;
stroke-dashoffset:0; }}}// Generate a small circular path animation for the home page icon
@include pathKeyframes(home-circle,44);
// Generate an arrow path animation to explore the icon
@include pathKeyframes(explore-arrow,39);
// Generate a median path animation for my icon
@include pathKeyframes(me-line,23);
Copy the code
Add a dotted line to the icon and an offset value that defaults to 0. Add an animation to the selected state.
.tabbar{
// ...
#home-circle{
stroke-dasharray:44 44;
stroke-dashoffset:0;
}
#explore-arrow{
stroke-dasharray:39 39;
stroke-dashoffset:0;
}
#me-line{
stroke-dasharray:23 23;
stroke-dashoffset:0;
}
.on{
// ...
#home-circle{
animation: home-circle 1s ease forwards;
}
#explore-arrow{
animation: explore-arrow 1s ease forwards;
}
#home-circle{
animation: me-line 1sease forwards; }}}Copy the code
Click the icon to switch the animation
Now that we’ve added the animation, let’s switch the display by clicking on the icon.
Click the icon, first clear the selected state. On, and then add. On to the icon clicked, so we can realize the click to switch animation.
let tabbarItem = document.querySelectorAll('.tabbar__item')
tabbarItem.forEach(item= >{
item.addEventListener('click'.function(){
tabbarItem.forEach(item= >{
item.classList.remove('on')})this.classList.add('on')})})Copy the code
Awkward chat middle small circle animation
The small circle animation in the middle of the conversation is different from other ICONS and needs to be processed separately.
The small circle in the middle is first shrunk and hidden, then displayed and moved up and down:
.tabbar{
// Set the base position of the small circle. The base position is the center coordinate of the small circle.
#chat-circle1{
transform-origin: 13px 24px;
}
#chat-circle2{
transform-origin: 25px 24px;
}
#chat-circle3{
transform-origin: 37px 24px;
}
.on{
// ...
// Use an animation that lasts for 1s to make ⚪ move, increase the delay time, make ⚪ feel random, and let the animation stay in the end position
#chat-circle1{
animation: chat-circle 1s ease forwards;
}
#chat-circle2{
animation: chat-circle 1s .15S ease forwards;
}
#chat-circle3{
animation: chat-circle 1s .3S ease forwards;
}
// ...}}// Set the keyframe of the small circle, 0-50% first zoom, 50.1-100% first display, then move up and down
@keyframes chat-circle{
0%{
fill:#B1BACD;
transform: scale(1);
}
50%{
fill:#B1BACD;
transform: scale(0);
}
501.%{
fill:#ffffff;
transform: scale(1) translateY(0);
}
60%{
fill:#ffffff;
transform: scale(1) translateY(-10px);
}
85%{
fill:#ffffff;
transform: scale(1) translateY(10px);
}
100%{
fill:#ffffff;
transform: scale(1) translateY(0); }}Copy the code
A simple Tabbar animation is done. This simple example shows how fast and easy it is to animate SVG + CSS3 after the UI has created an SVG file. The demo link
The last
If you like Tabbar animations, you can refer to UI animations, and this example is based on one of them.