Hey coders, Welcome to today’s blog. Today we will be creating a unique and exciting project. You must have seen different types of login forms, and you must have created different types of login pages. But have you ever seen an animated SVG login form in which a white polar bear looks at your mail text and, when you enter your password, he will put his hands on his eyes?

See How mannered he is, but in today’s time, most people are more interested in others phones. Whenever I travel in the metro, people will look at my phone. But today we will create a mannered polar bear animated login form using HTML, CSS,CSS and JavaScript.
Let’s have a look at our project and see how interactive it is. We will be creating this project from scratch, so those who don’t have any idea, don’t worry. I will be creating this project from scratch.

Prerequisites:
- Basic HTML Structure knowledge
- CSS styling and Animation concept
- Javascript functions.
100+ JavaScript Projects For Beginners With Source Code
HTML Structure:
We will be first creating the structure of an animated SVG login form. For this, we will use the <form> element, and inside the form element, using the <div> tag with class “svgContainer,” we will create a frame of the form, and inside it, using the block-level element, we will add the link to the SVG file.
A SVG is a scalable vector graphic that doesn’t lose its quality when embedded inside the website. We will be adding SVG for all parts of the polar bear, such as the face,, eyebrows, and mouth. Just copy this svg class element and paste it inside your html structure, and here the main role of CSS is to add animation to the project.
Now we will create a container inside which we will create two fields for entering the email ID and password. Using the label tag, we will create a label so that people can know exactly what they were asked to input inside the field. Similarly, we will create a field for passwords and a submit button.
<form> <div class="svgContainer"> <div> <svg class="mySVG" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 200 200"> <defs> <circle id="armMaskPath" cx="100" cy="100" r="100"/> </defs> <clipPath id="armMask"> <use xlink:href="#armMaskPath" overflow="visible"/> </clipPath> <circle cx="100" cy="100" r="100" fill="#a9ddf3"/> <g class="body"> <path fill="#FFFFFF" d="M193.3,135.9c-5.8-8.4-15.5-13.9-26.5-13.9H151V72c0-27.6-22.4-50-50-50S51,44.4,51,72v50H32.1 c-10.6,0-20,5.1-25.8,13l0,78h187L193.3,135.9z"/> <path fill="none" stroke="#3A5E77" stroke-width="2.5" stroke-linecap="round" stroke-linejoinn="round" d="M193.3,135.9 c-5.8-8.4-15.5-13.9-26.5-13.9H151V72c0-27.6-22.4-50-50-50S51,44.4,51,72v50H32.1c-10.6,0-20,5.1-25.8,13"/> <path fill="#DDF1FA" d="M100,156.4c-22.9,0-43,11.1-54.1,27.7c15.6,10,34.2,15.9,54.1,15.9s38.5-5.8,54.1-15.9 C143,167.5,122.9,156.4,100,156.4z"/> </g> <g class="earL"> <g class="outerEar" fill="#ddf1fa" stroke="#3a5e77" stroke-width="2.5"> <circle cx="47" cy="83" r="11.5"/> <path d="M46.3 78.9c-2.3 0-4.1 1.9-4.1 4.1 0 2.3 1.9 4.1 4.1 4.1" stroke-linecap="round" stroke-linejoin="round"/> </g> <g class="earHair"> <rect x="51" y="64" fill="#FFFFFF" width="15" height="35"/> <path d="M53.4 62.8C48.5 67.4 45 72.2 42.8 77c3.4-.1 6.8-.1 10.1.1-4 3.7-6.8 7.6-8.2 11.6 2.1 0 4.2 0 6.3.2-2.6 4.1-3.8 8.3-3.7 12.5 1.2-.7 3.4-1.4 5.2-1.9" fill="#fff" stroke="#3a5e77" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/> </g> </g> <g class="earR"> <g class="outerEar" fill="#ddf1fa" stroke="#3a5e77" stroke-width="2.5"> <circle cx="155" cy="83" r="11.5"/> <path d="M155.7 78.9c2.3 0 4.1 1.9 4.1 4.1 0 2.3-1.9 4.1-4.1 4.1" stroke-linecap="round" stroke-linejoin="round"/> </g> <g class="earHair"> <rect x="131" y="64" fill="#FFFFFF" width="20" height="35"/> <path d="M148.6 62.8c4.9 4.6 8.4 9.4 10.6 14.2-3.4-.1-6.8-.1-10.1.1 4 3.7 6.8 7.6 8.2 11.6-2.1 0-4.2 0-6.3.2 2.6 4.1 3.8 8.3 3.7 12.5-1.2-.7-3.4-1.4-5.2-1.9" fill="#fff" stroke="#3a5e77" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/> </g> </g> <path class="chin" d="M84.1 121.6c2.7 2.9 6.1 5.4 9.8 7.5l.9-4.5c2.9 2.5 6.3 4.8 10.2 6.5 0-1.9-.1-3.9-.2-5.8 3 1.2 6.2 2 9.7 2.5-.3-2.1-.7-4.1-1.2-6.1" fill="none" stroke="#3a5e77" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/> <path class="face" fill="#DDF1FA" d="M134.5,46v35.5c0,21.815-15.446,39.5-34.5,39.5s-34.5-17.685-34.5-39.5V46"/> <path class="hair" fill="#FFFFFF" stroke="#3A5E77" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" d="M81.457,27.929 c1.755-4.084,5.51-8.262,11.253-11.77c0.979,2.565,1.883,5.14,2.712,7.723c3.162-4.265,8.626-8.27,16.272-11.235 c-0.737,3.293-1.588,6.573-2.554,9.837c4.857-2.116,11.049-3.64,18.428-4.156c-2.403,3.23-5.021,6.391-7.852,9.474"/> <g class="eyebrow"> <path fill="#FFFFFF" d="M138.142,55.064c-4.93,1.259-9.874,2.118-14.787,2.599c-0.336,3.341-0.776,6.689-1.322,10.037 c-4.569-1.465-8.909-3.222-12.996-5.226c-0.98,3.075-2.07,6.137-3.267,9.179c-5.514-3.067-10.559-6.545-15.097-10.329 c-1.806,2.889-3.745,5.73-5.816,8.515c-7.916-4.124-15.053-9.114-21.296-14.738l1.107-11.768h73.475V55.064z"/> <path fill="#FFFFFF" stroke="#3A5E77" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" d="M63.56,55.102 c6.243,5.624,13.38,10.614,21.296,14.738c2.071-2.785,4.01-5.626,5.816-8.515c4.537,3.785,9.583,7.263,15.097,10.329 c1.197-3.043,2.287-6.104,3.267-9.179c4.087,2.004,8.427,3.761,12.996,5.226c0.545-3.348,0.986-6.696,1.322-10.037 c4.913-0.481,9.857-1.34,14.787-2.599"/> </g> <g class="eyeL"> <circle cx="85.5" cy="78.5" r="3.5" fill="#3a5e77"/> <circle cx="84" cy="76" r="1" fill="#fff"/> </g> <g class="eyeR"> <circle cx="114.5" cy="78.5" r="3.5" fill="#3a5e77"/> <circle cx="113" cy="76" r="1" fill="#fff"/> </g> <g class="mouth"> <path class="mouthBG" fill="#617E92" d="M100.2,101c-0.4,0-1.4,0-1.8,0c-2.7-0.3-5.3-1.1-8-2.5c-0.7-0.3-0.9-1.2-0.6-1.8 c0.2-0.5,0.7-0.7,1.2-0.7c0.2,0,0.5,0.1,0.6,0.2c3,1.5,5.8,2.3,8.6,2.3s5.7-0.7,8.6-2.3c0.2-0.1,0.4-0.2,0.6-0.2 c0.5,0,1,0.3,1.2,0.7c0.4,0.7,0.1,1.5-0.6,1.9c-2.6,1.4-5.3,2.2-7.9,2.5C101.7,101,100.5,101,100.2,101z"/> <path style="display: none;" class="mouthSmallBG" fill="#617E92" d="M100.2,101c-0.4,0-1.4,0-1.8,0c-2.7-0.3-5.3-1.1-8-2.5c-0.7-0.3-0.9-1.2-0.6-1.8 c0.2-0.5,0.7-0.7,1.2-0.7c0.2,0,0.5,0.1,0.6,0.2c3,1.5,5.8,2.3,8.6,2.3s5.7-0.7,8.6-2.3c0.2-0.1,0.4-0.2,0.6-0.2 c0.5,0,1,0.3,1.2,0.7c0.4,0.7,0.1,1.5-0.6,1.9c-2.6,1.4-5.3,2.2-7.9,2.5C101.7,101,100.5,101,100.2,101z"/> <path style="display: none;" class="mouthMediumBG" d="M95,104.2c-4.5,0-8.2-3.7-8.2-8.2v-2c0-1.2,1-2.2,2.2-2.2h22c1.2,0,2.2,1,2.2,2.2v2 c0,4.5-3.7,8.2-8.2,8.2H95z"/> <path style="display: none;" class="mouthLargeBG" d="M100 110.2c-9 0-16.2-7.3-16.2-16.2 0-2.3 1.9-4.2 4.2-4.2h24c2.3 0 4.2 1.9 4.2 4.2 0 9-7.2 16.2-16.2 16.2z" fill="#617e92" stroke="#3a5e77" stroke-linejoin="round" stroke-width="2.5"/> <defs> <path id="mouthMaskPath" d="M100.2,101c-0.4,0-1.4,0-1.8,0c-2.7-0.3-5.3-1.1-8-2.5c-0.7-0.3-0.9-1.2-0.6-1.8 c0.2-0.5,0.7-0.7,1.2-0.7c0.2,0,0.5,0.1,0.6,0.2c3,1.5,5.8,2.3,8.6,2.3s5.7-0.7,8.6-2.3c0.2-0.1,0.4-0.2,0.6-0.2 c0.5,0,1,0.3,1.2,0.7c0.4,0.7,0.1,1.5-0.6,1.9c-2.6,1.4-5.3,2.2-7.9,2.5C101.7,101,100.5,101,100.2,101z"/> </defs> <clipPath id="mouthMask"> <use xlink:href="#mouthMaskPath" overflow="visible"/> </clipPath> <g clip-path="url(#mouthMask)"> <g class="tongue"> <circle cx="100" cy="107" r="8" fill="#cc4a6c"/> <ellipse class="tongueHighlight" cx="100" cy="100.5" rx="3" ry="1.5" opacity=".1" fill="#fff"/> </g> </g> <path clip-path="url(#mouthMask)" class="tooth" style="fill:#FFFFFF;" d="M106,97h-4c-1.1,0-2-0.9-2-2v-2h8v2C108,96.1,107.1,97,106,97z"/> <path class="mouthOutline" fill="none" stroke="#3A5E77" stroke-width="2.5" stroke-linejoin="round" d="M100.2,101c-0.4,0-1.4,0-1.8,0c-2.7-0.3-5.3-1.1-8-2.5c-0.7-0.3-0.9-1.2-0.6-1.8 c0.2-0.5,0.7-0.7,1.2-0.7c0.2,0,0.5,0.1,0.6,0.2c3,1.5,5.8,2.3,8.6,2.3s5.7-0.7,8.6-2.3c0.2-0.1,0.4-0.2,0.6-0.2 c0.5,0,1,0.3,1.2,0.7c0.4,0.7,0.1,1.5-0.6,1.9c-2.6,1.4-5.3,2.2-7.9,2.5C101.7,101,100.5,101,100.2,101z"/> </g> <path class="nose" d="M97.7 79.9h4.7c1.9 0 3 2.2 1.9 3.7l-2.3 3.3c-.9 1.3-2.9 1.3-3.8 0l-2.3-3.3c-1.3-1.6-.2-3.7 1.8-3.7z" fill="#3a5e77"/> <g class="arms" clip-path="url(#armMask)"> <g class="armL"> <path fill="#ddf1fa" stroke="#3a5e77" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2.5" d="M121.3 97.4L111 58.7l38.8-10.4 20 36.1z"/> <path fill="#ddf1fa" stroke="#3a5e77" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2.5" d="M134.4 52.5l19.3-5.2c2.7-.7 5.4.9 6.1 3.5.7 2.7-.9 5.4-3.5 6.1L146 59.7M160.8 76.5l19.4-5.2c2.7-.7 5.4.9 6.1 3.5.7 2.7-.9 5.4-3.5 6.1l-18.3 4.9M158.3 66.8l23.1-6.2c2.7-.7 5.4.9 6.1 3.5.7 2.7-.9 5.4-3.5 6.1l-23.1 6.2M150.9 58.4l26-7c2.7-.7 5.4.9 6.1 3.5.7 2.7-.9 5.4-3.5 6.1l-21.3 5.7"/> <path fill="#a9ddf3" d="M178.8 74.7l2.2-.6c1.1-.3 2.2.3 2.4 1.4.3 1.1-.3 2.2-1.4 2.4l-2.2.6-1-3.8zM180.1 64l2.2-.6c1.1-.3 2.2.3 2.4 1.4.3 1.1-.3 2.2-1.4 2.4l-2.2.6-1-3.8zM175.5 54.9l2.2-.6c1.1-.3 2.2.3 2.4 1.4.3 1.1-.3 2.2-1.4 2.4l-2.2.6-1-3.8zM152.1 49.4l2.2-.6c1.1-.3 2.2.3 2.4 1.4.3 1.1-.3 2.2-1.4 2.4l-2.2.6-1-3.8z"/> <path fill="#fff" stroke="#3a5e77" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M123.5 96.8c-41.4 14.9-84.1 30.7-108.2 35.5L1.2 80c33.5-9.9 71.9-16.5 111.9-21.8"/> <path fill="#fff" stroke="#3a5e77" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M108.5 59.4c7.7-5.3 14.3-8.4 22.8-13.2-2.4 5.3-4.7 10.3-6.7 15.1 4.3.3 8.4.7 12.3 1.3-4.2 5-8.1 9.6-11.5 13.9 3.1 1.1 6 2.4 8.7 3.8-1.4 2.9-2.7 5.8-3.9 8.5 2.5 3.5 4.6 7.2 6.3 11-4.9-.8-9-.7-16.2-2.7M94.5 102.8c-.6 4-3.8 8.9-9.4 14.7-2.6-1.8-5-3.7-7.2-5.7-2.5 4.1-6.6 8.8-12.2 14-1.9-2.2-3.4-4.5-4.5-6.9-4.4 3.3-9.5 6.9-15.4 10.8-.2-3.4.1-7.1 1.1-10.9M97.5 62.9c-1.7-2.4-5.9-4.1-12.4-5.2-.9 2.2-1.8 4.3-2.5 6.5-3.8-1.8-9.4-3.1-17-3.8.5 2.3 1.2 4.5 1.9 6.8-5-.6-11.2-.9-18.4-1 2 2.9.9 3.5 3.9 6.2"/> </g> <g class="armR"> <path fill="#ddf1fa" stroke="#3a5e77" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2.5" d="M265.4 97.3l10.4-38.6-38.9-10.5-20 36.1z"/> <path fill="#ddf1fa" stroke="#3a5e77" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2.5" d="M252.4 52.4L233 47.2c-2.7-.7-5.4.9-6.1 3.5-.7 2.7.9 5.4 3.5 6.1l10.3 2.8M226 76.4l-19.4-5.2c-2.7-.7-5.4.9-6.1 3.5-.7 2.7.9 5.4 3.5 6.1l18.3 4.9M228.4 66.7l-23.1-6.2c-2.7-.7-5.4.9-6.1 3.5-.7 2.7.9 5.4 3.5 6.1l23.1 6.2M235.8 58.3l-26-7c-2.7-.7-5.4.9-6.1 3.5-.7 2.7.9 5.4 3.5 6.1l21.3 5.7"/> <path fill="#a9ddf3" d="M207.9 74.7l-2.2-.6c-1.1-.3-2.2.3-2.4 1.4-.3 1.1.3 2.2 1.4 2.4l2.2.6 1-3.8zM206.7 64l-2.2-.6c-1.1-.3-2.2.3-2.4 1.4-.3 1.1.3 2.2 1.4 2.4l2.2.6 1-3.8zM211.2 54.8l-2.2-.6c-1.1-.3-2.2.3-2.4 1.4-.3 1.1.3 2.2 1.4 2.4l2.2.6 1-3.8zM234.6 49.4l-2.2-.6c-1.1-.3-2.2.3-2.4 1.4-.3 1.1.3 2.2 1.4 2.4l2.2.6 1-3.8z"/> <path fill="#fff" stroke="#3a5e77" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M263.3 96.7c41.4 14.9 84.1 30.7 108.2 35.5l14-52.3C352 70 313.6 63.5 273.6 58.1"/> <path fill="#fff" stroke="#3a5e77" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M278.2 59.3l-18.6-10 2.5 11.9-10.7 6.5 9.9 8.7-13.9 6.4 9.1 5.9-13.2 9.2 23.1-.9M284.5 100.1c-.4 4 1.8 8.9 6.7 14.8 3.5-1.8 6.7-3.6 9.7-5.5 1.8 4.2 5.1 8.9 10.1 14.1 2.7-2.1 5.1-4.4 7.1-6.8 4.1 3.4 9 7 14.7 11 1.2-3.4 1.8-7 1.7-10.9M314 66.7s5.4-5.7 12.6-7.4c1.7 2.9 3.3 5.7 4.9 8.6 3.8-2.5 9.8-4.4 18.2-5.7.1 3.1.1 6.1 0 9.2 5.5-1 12.5-1.6 20.8-1.9-1.4 3.9-2.5 8.4-2.5 8.4"/> </g> </g> </svg> </div> </div> <div class="inputGroup inputGroup1"> <label for="email1">Email</label> <input type="text" id="email" class="email" maxlength="256"/> <p class="helper helper1">[email protected]</p> <span class="indicator"></span> </div> <div class="inputGroup inputGroup2"> <label for="password">Password</label> <input type="password" id="password" class="password" /> </div> <div class="inputGroup inputGroup3"> <button id="login">Log in</button> </div> </form>

Adding CSS to our project:
Adding Basic Styling:
Using the HTML tag, we will add the basic styling to our project. We will set the width and height to 100%, and using the body tag, we will set the body background color to light blue, and the position of the body content is relative to the page.
Also using the font family property, we will set the font family styling to “Source Sans Pro” and the font weight to 400.
html { width: 100%; height: 100%; } body { background-color: #eff3f4; position: relative; width: 100%; height: 100%; font-size: 16px; font-family: "Source Sans Pro", sans-serif; font-weight: 400; -webkit-font-smoothing: antialiased; }

Form and SVG Styling:
Using the form tag, we will add styling to the form structure; using the position property, we will set the position as “absolute,” and using the top and left properties, we will add 50% space to both sides. Also, using the border radius property, we will set the border radius to give a soft corner look.
Now using the child class selector, we will add the styling to our SVG avatar. We will set the position to relative, the background color to white, and the width to 100%.
form { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); display: block; width: 100%; max-width: 400px; background-color: #FFF; margin: 0; padding: 2.25em; box-sizing: border-box; border: solid 1px #DDD; border-radius: 0.5em; font-family: "Source Sans Pro", sans-serif; } form .svgContainer { position: relative; width: 200px; height: 200px; margin: 0 auto 1em; border-radius: 50%; background: none; border: solid 2.5px #3A5E77; overflow: hidden; pointer-events: none; } form .svgContainer div { position: relative; width: 100%; height: 0; overflow: hidden; padding-bottom: 100%; } form .svgContainer .mySVG { position: absolute; left: 0; top: 0; width: 100%; height: 100%; pointer-events: none; }

Adding form Element Styling:
Using the form tag, with class inputGroup, we will add a margin of 2 em to the left and right sides of the input field, and using the margin bottom, we will add a bottom margin of 0 px. Also, using the label tag, we will add the styling to the label; we will set the display to block; and using the color property, we will set the color to green and blue.
Also, we will add some styling to the button tag, and we will add a hover property to the button in which, as we hover over the button, the background color of the button gets changed.
.
form .inputGroup { margin: 0 0 2em; padding: 0; position: relative; } form .inputGroup:last-of-type { margin-bottom: 0; } form label { margin: 0 0 12px; display: block; font-size: 1.25em; color: #217093; font-weight: 700; font-family: inherit; } form input[type=email], form input[type=text], form input[type=password] { display: block; margin: 0; padding: 0 1em 0; background-color: #f3fafd; border: solid 2px #217093; border-radius: 4px; -webkit-appearance: none; box-sizing: border-box; width: 100%; height: 65px; font-size: 1.55em; color: #353538; font-weight: 600; font-family: inherit; transition: box-shadow 0.2s linear, border-color 0.25s ease-out; } form input[type=email]:focus, form input[type=text]:focus, form input[type=password]:focus { outline: none; box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1); border: solid 2px #4eb8dd; } form input[type=email], form input[type=text] { padding: 14px 1em 0px; } form button { display: block; margin: 0; padding: 0.65em 1em 1em; background-color: #4eb8dd; border: none; border-radius: 4px; box-sizing: border-box; box-shadow: none; width: 100%; height: 65px; font-size: 1.55em; color: #FFF; font-weight: 600; font-family: inherit; transition: background-color 0.2s ease-out; } form button:hover, form button:active { background-color: #217093; } form .inputGroup1 .helper { position: absolute; z-index: 1; font-family: inherit; } form .inputGroup1 .helper1 { top: 0; left: 0; transform: translate(1.4em, 2.6em) scale(1); transform-origin: 0 0; color: #217093; font-size: 1.25em; font-weight: 400; opacity: 0.65; pointer-events: none; transition: transform 0.2s ease-out, opacity 0.2s linear; } form .inputGroup1.focusWithText .helper { /*input[type='email']:focus + .helper {*/ transform: translate(1.4em, 2em) scale(0.65); opacity: 1; }

Adding Javascript:
Creating variables:
We will be creating some variables and assigning the value of html elements using the variables email, password, mySVG, armL, armR, eyeL, and eyeR. We will select all the elements of the HTML tag for adding SVG avatar animation, and using the document.queryselector element, we will assign the value to the variable.
var email = document.querySelector('#email'), password = document.querySelector('#password'), mySVG = document.querySelector('.svgContainer'), armL = document.querySelector('.armL'), armR = document.querySelector('.armR'), eyeL = document.querySelector('.eyeL'), eyeR = document.querySelector('.eyeR'), nose = document.querySelector('.nose'), mouth = document.querySelector('.mouth'), mouthBG = document.querySelector('.mouthBG'), mouthSmallBG = document.querySelector('.mouthSmallBG'), mouthMediumBG = document.querySelector('.mouthMediumBG'), mouthLargeBG = document.querySelector('.mouthLargeBG'), mouthMaskPath = document.querySelector('#mouthMaskPath'), mouthOutline = document.querySelector('.mouthOutline'), tooth = document.querySelector('.tooth'), tongue = document.querySelector('.tongue'), chin = document.querySelector('.chin'), face = document.querySelector('.face'), eyebrow = document.querySelector('.eyebrow'), outerEarL = document.querySelector('.earL .outerEar'), outerEarR = document.querySelector('.earR .outerEar'), earHairL = document.querySelector('.earL .earHair'), earHairR = document.querySelector('.earR .earHair'), hair = document.querySelector('.hair'); var caretPos, curEmailIndex, screenCenter, svgCoords, eyeMaxHorizD = 20, eyeMaxVertD = 10, noseMaxHorizD = 23, noseMaxVertD = 10, dFromC, eyeDistH, eyeLDistV, eyeRDistV, eyeDistR, mouthStatus = "small";
Creating Function:
We will create a function called getcoord (e). This function will help us find the coordinates of or the distance between the input field and the cursor. Using var carPos, which is the position of the cursor in the input field email, and using the div and span tags to measure the text dimensions,. The email coordinates{}, caret coordinates{}, center coordinates{} we will store different dimensions of the elment.
function getCoord(e) { var carPos = email.selectionEnd, div = document.createElement('div'), span = document.createElement('span'), copyStyle = getComputedStyle(email), emailCoords = {}, caretCoords = {}, centerCoords = {}; [].forEach.call(copyStyle, function(prop) { div.style[prop] = copyStyle[prop]; }); div.style.position = 'absolute'; document.body.appendChild(div); div.textContent = email.value.substr(0, carPos); span.textContent = email.value.substr(carPos) || '.'; div.appendChild(span); emailCoords = getPosition(email); //console.log("emailCoords.x: " + emailCoords.x + ", emailCoords.y: " + emailCoords.y); caretCoords = getPosition(span); //console.log("caretCoords.x " + caretCoords.x + ", caretCoords.y: " + caretCoords.y); centerCoords = getPosition(mySVG); //console.log("centerCoords.x: " + centerCoords.x); svgCoords = getPosition(mySVG); screenCenter = centerCoords.x + (mySVG.offsetWidth / 2); //console.log("screenCenter: " + screenCenter); caretPos = caretCoords.x + emailCoords.x; //console.log("caretPos: " + caretPos); dFromC = screenCenter - caretPos; //console.log("dFromC: " + dFromC); var pFromC = Math.round((caretPos / screenCenter) * 100) / 100; if (pFromC < 1) { } else if (pFromC > 1) { pFromC -= 2; pFromC = Math.abs(pFromC); }
horizontal Eye movements:
We will calculate the horizontal distance in which eyes should be moved; we will calculate it as a small portion from the center, and we will use the clamping to move the eye distance at maximum.
eyeDistH = -dFromC * .05; if (eyeDistH > eyeMaxHorizD) { eyeDistH = eyeMaxHorizD; } else if (eyeDistH < -eyeMaxHorizD) { eyeDistH = -eyeMaxHorizD; }
Adding Animation: We will be adding animation using GSAP platform for adding various facial feature movements.
Adding Facial movement coordinates:
ADVERTISEMENT
Using the variable we will store the coordinates of left eye , right eye , nose and the mouth coordinates relative to the SVG elements.
ADVERTISEMENT
var eyeLCoords = {x: svgCoords.x + 84, y: svgCoords.y + 76}; var eyeRCoords = {x: svgCoords.x + 113, y: svgCoords.y + 76}; var noseCoords = {x: svgCoords.x + 97, y: svgCoords.y + 81}; var mouthCoords = {x: svgCoords.x + 100, y: svgCoords.y + 100};
Calculating Angles for Movements:
ADVERTISEMENT
We will be using the angles concepts here. Using the angle function, we will select the angles, and then, using the x and y axis angle positions, we will set the angle of the eye.
ADVERTISEMENT
Adding Facial features and transformation:
ADVERTISEMENT
These variables chinx and chinY will help in storing the movements and transformations of the elements, and using the tweenMax GSAP property, we will add the styling to our animated SVG avatar.
var chinX = mouthX * .8; var chinY = mouthY * .5; var chinS = 1 - ((dFromC * .15) / 100); if(chinS > 1) { chinS = 1 - (chinS - 1); } var faceX = mouthX * .3; var faceY = mouthY * .4; var faceSkew = Math.cos(mouthAngle) * 5; var eyebrowSkew = Math.cos(mouthAngle) * 25; var outerEarX = Math.cos(mouthAngle) * 4; var outerEarY = Math.cos(mouthAngle) * 5; var hairX = Math.cos(mouthAngle) * 6; var hairS = 1.2; TweenMax.to(eyeL, 1, {x: -eyeLX, y: -eyeLY, ease: Expo.easeOut}); TweenMax.to(eyeR, 1, {x: -eyeRX, y: -eyeRY, ease: Expo.easeOut}); TweenMax.to(nose, 1, {x: -noseX, y: -noseY, rotation: mouthR, transformOrigin: "center center", ease: Expo.easeOut}); TweenMax.to(mouth, 1, {x: -mouthX, y: -mouthY, rotation: mouthR, transformOrigin: "center center", ease: Expo.easeOut}); TweenMax.to(chin, 1, {x: -chinX, y: -chinY, scaleY: chinS, ease: Expo.easeOut}); TweenMax.to(face, 1, {x: -faceX, y: -faceY, skewX: -faceSkew, transformOrigin: "center top", ease: Expo.easeOut}); TweenMax.to(eyebrow, 1, {x: -faceX, y: -faceY, skewX: -eyebrowSkew, transformOrigin: "center top", ease: Expo.easeOut}); TweenMax.to(outerEarL, 1, {x: outerEarX, y: -outerEarY, ease: Expo.easeOut}); TweenMax.to(outerEarR, 1, {x: outerEarX, y: outerEarY, ease: Expo.easeOut}); TweenMax.to(earHairL, 1, {x: -outerEarX, y: -outerEarY, ease: Expo.easeOut}); TweenMax.to(earHairR, 1, {x: -outerEarX, y: outerEarY, ease: Expo.easeOut}); TweenMax.to(hair, 1, {x: hairX, scaleY: hairS, transformOrigin: "center bottom", ease: Expo.easeOut});
Adding Animation to the password field:
We will create a function called onEmailInput, trigger the function called getcoord, and using the target.value, we will add movement to the avatar according to the length of the email. Using the if and else condition, we will add the email validation, in which we will use the TweenMax gsap animation property. The longer the length of the email, the more the mouth of the avatar will open wide.
Then we will add the animation to the password field, in which we will add the left and right hand animations. As soon as the user clicks on the password field, we will add an event listener to the password input field, and when the user clicks on the field, the animation of the hand gets triggered and the value is assigned to the avatar.
function onEmailInput(e) { getCoord(e); var value = e.target.value; curEmailIndex = value.length; // very crude email validation for now to trigger effects if(curEmailIndex > 0) { if(mouthStatus == "small") { mouthStatus = "medium"; TweenMax.to([mouthBG, mouthOutline, mouthMaskPath], 1, {morphSVG: mouthMediumBG, shapeIndex: 8, ease: Expo.easeOut}); TweenMax.to(tooth, 1, {x: 0, y: 0, ease: Expo.easeOut}); TweenMax.to(tongue, 1, {x: 0, y: 1, ease: Expo.easeOut}); TweenMax.to([eyeL, eyeR], 1, {scaleX: .85, scaleY: .85, ease: Expo.easeOut}); } if(value.includes("@")) { mouthStatus = "large"; TweenMax.to([mouthBG, mouthOutline, mouthMaskPath], 1, {morphSVG: mouthLargeBG, ease: Expo.easeOut}); TweenMax.to(tooth, 1, {x: 3, y: -2, ease: Expo.easeOut}); TweenMax.to(tongue, 1, {y: 2, ease: Expo.easeOut}); TweenMax.to([eyeL, eyeR], 1, {scaleX: .65, scaleY: .65, ease: Expo.easeOut, transformOrigin: "center center"}); } else { mouthStatus = "medium"; TweenMax.to([mouthBG, mouthOutline, mouthMaskPath], 1, {morphSVG: mouthMediumBG, ease: Expo.easeOut}); TweenMax.to(tooth, 1, {x: 0, y: 0, ease: Expo.easeOut}); TweenMax.to(tongue, 1, {x: 0, y: 1, ease: Expo.easeOut}); TweenMax.to([eyeL, eyeR], 1, {scaleX: .85, scaleY: .85, ease: Expo.easeOut}); } } else { mouthStatus = "small"; TweenMax.to([mouthBG, mouthOutline, mouthMaskPath], 1, {morphSVG: mouthSmallBG, shapeIndex: 9, ease: Expo.easeOut}); TweenMax.to(tooth, 1, {x: 0, y: 0, ease: Expo.easeOut}); TweenMax.to(tongue, 1, {y: 0, ease: Expo.easeOut}); TweenMax.to([eyeL, eyeR], 1, {scaleX: 1, scaleY: 1, ease: Expo.easeOut}); } } function onEmailFocus(e) { e.target.parentElement.classList.add("focusWithText"); getCoord(); } function onEmailBlur(e) { if(e.target.value == "") { e.target.parentElement.classList.remove("focusWithText"); } resetFace(); } function onPasswordFocus(e) { coverEyes(); } function onPasswordBlur(e) { uncoverEyes(); } function coverEyes() { TweenMax.to(armL, .45, {x: -93, y: 2, rotation: 0, ease: Quad.easeOut}); TweenMax.to(armR, .45, {x: -93, y: 2, rotation: 0, ease: Quad.easeOut, delay: .1}); } function uncoverEyes() { TweenMax.to(armL, 1.35, {y: 220, ease: Quad.easeOut}); TweenMax.to(armL, 1.35, {rotation: 105, ease: Quad.easeOut, delay: .1}); TweenMax.to(armR, 1.35, {y: 220, ease: Quad.easeOut}); TweenMax.to(armR, 1.35, {rotation: -105, ease: Quad.easeOut, delay: .1}); } function resetFace() { TweenMax.to([eyeL, eyeR], 1, {x: 0, y: 0, ease: Expo.easeOut}); TweenMax.to(nose, 1, {x: 0, y: 0, scaleX: 1, scaleY: 1, ease: Expo.easeOut}); TweenMax.to(mouth, 1, {x: 0, y: 0, rotation: 0, ease: Expo.easeOut}); TweenMax.to(chin, 1, {x: 0, y: 0, scaleY: 1, ease: Expo.easeOut}); TweenMax.to([face, eyebrow], 1, {x: 0, y: 0, skewX: 0, ease: Expo.easeOut}); TweenMax.to([outerEarL, outerEarR, earHairL, earHairR, hair], 1, {x: 0, y: 0, scaleY: 1, ease: Expo.easeOut}); } function getAngle(x1, y1, x2, y2) { var angle = Math.atan2(y1 - y2, x1 - x2); return angle; } function getPosition(el) { var xPos = 0; var yPos = 0; while (el) { if (el.tagName == "BODY") { // deal with browser quirks with body/window/document and page scroll var xScroll = el.scrollLeft || document.documentElement.scrollLeft; var yScroll = el.scrollTop || document.documentElement.scrollTop; xPos += (el.offsetLeft - xScroll + el.clientLeft); yPos += (el.offsetTop - yScroll + el.clientTop); } else { // for all other non-BODY elements xPos += (el.offsetLeft - el.scrollLeft + el.clientLeft); yPos += (el.offsetTop - el.scrollTop + el.clientTop); } el = el.offsetParent; } return { x: xPos, y: yPos }; } email.addEventListener('focus', onEmailFocus); email.addEventListener('blur', onEmailBlur); email.addEventListener('input', onEmailInput); password.addEventListener('focus', onPasswordFocus); password.addEventListener('blur', onPasswordBlur); TweenMax.set(armL, {x: -93, y: 220, rotation: 105, transformOrigin: "top left"}); TweenMax.set(armR, {x: -93, y: 220, rotation: -105, transformOrigin: "top right"});
Final Output:
Conclusion:
Congratulations, You have completed your Animated SVG Avatar Login form and further you can enhance this as per your preferences. You can deploy it on the Github, Vercel, or Netfliy platforms and make it live for your friends.
I hope you liked this Tutorial and must have learned Something new. If you have any questions regarding this feel free to drop your comments below and contact our team on Instagram @Codewithrandom.
Codepen Preview:
See the Pen Animated SVG Avatar by Darin Senneff by Oscar Diaz (@kingplatano) on CodePen.
Author: Arun
Codeby: Oscar