How to achieve a CSS blink animation for an eye

I am currently working on a unique countdown screen featuring an eye that blinks and has an iris effect. The idea is to create an engaging spinner that looks back at the viewer while counting down.

document.getElementById('waitDia').showModal();

var ticks = 300,
    ticker = setInterval(changeTick,1000);

function changeTick()
{
 document.getElementById('spnTick').innerText = --ticks;
 if (0 === ticks) clearInterval(ticker);
}
#waitDia
{
 position:absolute;
 left:0 !important;
 top:0 !important;
 width:100vw !important;
 height:100vh !important; 
 padding:0; 
 min-width:100vw;
 min-height:100vh; 
 background-color:transparent !important;
}

#waitDia::backdrop{background-color:rgba(127,127,127,0.2);}

#spnTick
{
 position:absolute;
 display:inline-block;
 width:100%;
 left:0;
 top:0;
} 
#waitbox
{
 left:0 !important;
 top:0 !important;
 width:100vw !important;
 height:100vh !important;
 position:absolute;
 overflow:hidden;
}


#eyeball
{
 position:relative;
 top:-10vh;
 left:-6px;
 width:calc(24vh + 12px);
 height:calc(24vh + 12px);
 box-sizing:border-box;
 background:rgba(0,128,128,0.5);
 border-radius:100%;
 border:1px solid transparent;
 box-shadow:inset 0 0 18px 2px blue;
 z-index:99999998;
}


#waitsecs
{
 position:absolute;
 left:calc(50vw - 12vh);
 top:46vh;
 width:24vh;
 height:24vh;
 font-size:8vh;
 text-align:center;
 display:block;
 
}

#waitEye
{
 position:absolute;
 top:27vh;
 left:calc(50vw - 23vh);
 width: 46vh;
 height: 46vh;
 background-color: rgba(255,255,255,.9);
 border-radius: 100% 0px;
 transform: rotate(45deg); 
 mix-blend-mode:overlay;
 z-index:199999999;
 box-shadow:0 -0.5vh 0 2px #f1c27d,inset 0 6px 4px 4px black;
}
body,html
{
 background:black;
 font-family:arial;
}
<dialog id='waitDia' class='waitdia'>
   <div id='waitbox'>
    <div id='waitsecs'><span id='spnTick'>300</span><div id='eyeball'></div></div>
   <div id='waitEye'></div> 
   </div>  
  </dialog>

The current progress of my project can be seen below. Although I have set the timer for 300 seconds for illustrative purposes, it will likely be shorter in the final product.

While the project is promising, I still need to implement the eyelid blink effect. I believe this can be achieved through 'box-shadow' manipulation and a simple animation. Unfortunately, my CSS skills are limited, so I would appreciate any suggestions or improvements from those more experienced.

Answer №1

My approach to achieving a blink effect involves using rotation creatively. To create the blinking effect, I recommend creating the eye with two elements (the eyelid) that can be animated to simulate blinking.

Below is the code snippet showcasing the blink animation:

.eye {
  width: 250px;
  height: 80px;
  margin: 50px;
  display:inline-block;
  perspective: 200px;
  background:
    radial-gradient(circle 100px at 50% 250%,#f1c27d 99% ,transparent 100%) top/100% 50%,
    radial-gradient(circle 100px at 50% -150%,#f1c27d 99% ,transparent 100%) bottom/100% 50%;
  background-repeat:no-repeat
}

.eye>div {
  height: 50%;
  position:relative;
  overflow:hidden;
  transform-origin:bottom;
  animation:b1 0.8s  infinite ease-out alternate;
}
.eye>div:last-child {
  transform-origin:top;
  animation-name:b2;
}
.eye>div:before {
  content: "";
  position: absolute;
  top:0;
  left:10%;
  right:10%;
  padding-top:80%;
  border-radius:50%;
  background:#fff;
  box-shadow:
    -2px 0 0 3px inset #f1c27d,
    inset -5px 5px 2px 4px black;
}
.eye>div:last-child:before {
  bottom:0;
  top:auto;
  box-shadow:
    -2px 0 0 3px inset #f1c27d,
    inset -6px -4px 2px 4px black;
}


body {
 background:#000;
}

@keyframes b1{
  to { transform:rotateX(-88deg);}
}
@keyframes b2{
  to {transform:rotateX(88deg);}
}
<div class="eye">
  <div></div>
  <div></div>
</div>

If you're looking for a more realistic blink animation with a full eye, check out this example:

var ticks = 300,ticker;
setTimeout(function() { ticker = setInterval(changeTick,1600);},500);

function changeTick()
{
 document.querySelector('.eye span').setAttribute('data-text', --ticks);
 if (0 === ticks) clearInterval(ticker);
}
.eye {
  width: 250px;
  height: 80px;
  margin: 50px;
  display:inline-block;
  perspective: 200px;
  background:
    radial-gradient(circle 100px at 50% 250%,#f1c27d 99% ,transparent 100%) top/100% 50%,
    radial-gradient(circle 100px at 50% -150%,#f1c27d 99% ,transparent 100%) bottom/100% 50%;
  background-repeat:no-repeat;
  transform-style:preserve-3d;
  position:relative;
}

.eye>div {
  height: 50%;
  position:relative;
  overflow:hidden;
  transform-origin:bottom;
  z-index:1;
  animation:b1 0.8s  infinite ease-out alternate;
}
.eye>div:last-child {
  transform-origin:top;
  animation:none;
}
.eye>div:before {
  content: "";
  position: absolute;
  top:0;
  left:10%;
  right:10%;
  padding-top:80%;
  border-radius:50%;
  background:#fff;
  box-shadow:
    -2px 0 0 3px inset #f1c27d,
    inset -5px 5px 2px 4px black;
  animation:inherit;
  animation-name:color;
}
.eye>div:last-child:before {
  bottom:0;
  top:auto;
  box-shadow:
    -2px 0 0 3px inset #f1c27d,
    inset -6px -4px 2px 4px black;
}
.eye > span {
  position:absolute;
  width:45px;
  height:45px;
  bottom:18px;
  left:50%;
  transform:translateX(-50%) translateZ(55px);
  overflow:hidden;
  border-radius:20% 20% 0 0;
  z-index:2;
  animation:b2 0.8s  infinite ease-out alternate;
}
.eye > span:before {
  position:absolute;
  left:0;
  bottom:0;
  height:45px;
  width:100%;
  content:attr(data-text);
  border-radius:50%;
  background:#000;
  color:#fff;
  text-align:center;
  line-height:45px;
}


body {
 background:#000;
}

@keyframes b1{
  to { 
    transform:rotateX(-170deg);
  }
}
@keyframes b2{
  50% {
    height:20px;
  }
  60%,100% {
    height:0px;
  }
}
@keyframes color{
  0%,40% {
    background:#fff;
    box-shadow:
      -2px 0 0 3px inset #f1c27d,
      inset -5px 5px 2px 4px black;
  }
  40.1%,100% { 
    background:#f1c27d;
    box-shadow:none;
  }
}
<div class="eye">
  <div></div>
    <span data-text="300"></span>
  <div></div>
</div>

Answer №2

Optimized SVG Animation

No Counter Version:

  • The eyelid animation in this SVG is achieved by transitioning the attribute "d" from the top to the bottom.
  • To create a realistic effect on the eyelid for volume, radial gradients are utilized.
  • Pausing the animation at both upper and lower positions of the eyelid is accomplished through repeating the attribute values d.

<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"
   width="25%" height="25%" viewBox="0 0 200 200" preserveAspectRatio="xMinYMin meet">
<defs>
 <radialGradient id="grad1" cx="40%" cy="20%" r="100%" fx="45%" fy="20%">

   <stop stop-color="#B7B3B8" offset="10%"/>
   <stop stop-color="#CDC9D0" offset="65%"/>
   <stop stop-color="#9D90A2" offset="85%"/>
   <stop stop-color="#CDBED3" offset="100%"/>
 </radialGradient>
 </defs>
<image xlink:href="https://i.sstatic.net/gDG2U.jpg" width="100%" height="100%" />

 <path id="veko" fill="url(#grad1)" stroke="#B9B5BB" stroke-width="4" d="m12.7 132c0 0 25.6-19.6 39.8-27.1 11.7-6.2 23.9-11.9 36.9-14.4 10.7-2.1 21.9-2 32.6-0.4 8.5 1.3 16.8 4.3 24.6 7.8 9 4.1 17.1 9.9 25.2 15.5 2.2 1.5 4.9 2.6 6.4 4.9 0.4 0.6 1.1 2.4 0.4 2.1-6.7-2.5-17.2-10.3-26.1-14.8-6.6-3.....
 The code block continues with various path animations...
 </path>
</svg>

Variant with Countdown Added:

// Initialize variables
var checks = 100,
    checker = setInterval(Count, 2100);

// Function to update countdown text
function Count() {
    document.getElementById('txt1').textContent = --checks;
    if (0 === checks) clearInterval(checker);
}
.container {
 background:silver;
}
svg {
display:block;
width:15%;
height:23%;
padding-left:0.5em;
padding-bottom:1.5em;
margin:1em;
border-radius:50%;
-webkit-box-shadow: 7px 7px 5px 0px rgba(50, 50, 50, 0.75);
-moz-box-shadow:    7px 7px 5px 0px rgba(50, 50, 50, 0.75);
box-shadow:         7px 7px 5px 0px rgba(50, 50, 50, 0.75);
background:#8C6282;
}
#txt1 {
  font-size: 40px;
  font-weight:bold;
  fill:#FFDD00;
  stroke:#917E00;
  text-anchor:middle;
}
<div class="container">

<svg id="svg1" version="1.1" xmlns="http://www.w3.org/2000/svg" 
    xmlns:xlink="http://www.w3.org/1999/x......
 The code block continues with additional SVG elements and animations...
 </svg>
 </div>

Answer №3

SVG and JavaScript combo

The concept involves adjusting the positions of two control points on a bezier curve to create the shape of an eyelid based on the time interval between animation frames:

requestAnimationFrame(draw);

function draw(t) {
  circle.setAttribute('cx', Math.sin(t/1000)*2);
  grad.setAttribute('offset', 40 + Math.sin(t/3000)*20 + '%');
  t = Math.max(0, Math.sin(t/300));
  t = (t<.5 ? 16*t*t*t*t*t : 1+16*(--t)*t*t*t*t)*6-3;
  let d = `-7 0C-2 ${t} 2 ${t} 7 0`;
  mask.setAttribute('d', `M-7 -7${d}L7 -7z`)
  eyelid.setAttribute('d', `M${d}`)
  requestAnimationFrame(draw);
}
<svg viewbox="-10 -10 20 20" height="90vh">
  <defs><radialGradient id="g1" cx="50%" cy="50%" r="50%">
      <stop stop-color="black" offset="0%"/>
      <stop id="grad" stop-color="teal" offset="30%"/>
      <stop stop-color="white" offset="100%"/>
  </radialGradient></defs>
  <circle id="circle" r="2.4" stroke="black" fill="url(#g1)" stroke-width="0.2"></circle>
  <path id="mask" stroke="none" fill="white"></path>
  <path id="eyelid" stroke="black" fill="none"></path>
<svg>

An alternative approach with progression and responsiveness to mouse movements

let mouse = {x:0, y:0}, 
    progress = 0;
    
setInterval(e => progress = (progress + Math.random()/100)%1, 100)
requestAnimationFrame(draw);
addEventListener('pointermove', e => {mouse.x = e.x, mouse.y = e.y})

function draw(t) {

  requestAnimationFrame(draw);
  
  // moving the eye gaze
  let dx = mouse.x - innerWidth/2,
      dy = mouse.y - innerHeight/2,
      len = Math.sqrt(dx*dx + dy*dy),
      ml = Math.min(len*10/innerHeight, 1),
      a = Math.atan2(dy, dx),
      x = Math.cos(a) * ml, 
      y = Math.sin(a)/2 * ml;
      
  circle1.setAttribute('cx', x);
  circle1.setAttribute('cy', y);
  circle2.setAttribute('cx', x);
  circle2.setAttribute('cy', y);
  
  // loading percentage
  let r = 1.8, 
      p = progress *2 * Math.PI, 
      px = r*Math.cos(p), 
      py = r*Math.sin(p),
      arc = 1-Math.round(progress);
      
  load.setAttribute('d', `M${r},0 A${r},${r},0,${arc},0,${px},${py}L0,0z`)
  load.setAttribute('transform', `translate(${x}, ${y})`)
  
  // animating the gradient
  let offset = Math.max(0.2, (0.5 - len/2/innerHeight))*100 + "%";  
  grad1.setAttribute('offset', offset);
  grad2.setAttribute('offset', offset);
  
  // smoothing out time using easeInOutQuint formula
  t = Math.max(0, Math.sin(t/300));
  t = (t<.5 ? 16*t*t*t*t*t : 1+16*(--t)*t*t*t*t)*6-3;
  
  // Bezier curve based on smoothed time value
  let d = `-7 0C-2 ${t} 2 ${t} 7 0`;
  mask.setAttribute('d', `M-7 -7${d}L7 -7z`);
  eyelid.setAttribute('d', `M${d}`);
}
<body style="margin:0 calc(50vw - 50vh); overflow:hidden;">
<svg viewbox="-10 -10 20 20" height="100vh">
  <defs>
    <radialGradient id="g1" cx="50%" cy="50%" r="50%">
        <stop stop-color="black" offset="0%"/>
        <stop id="grad1" stop-color="#4f899d" offset="30%"/>
        <stop stop-color="white" offset="100%"/>
    </radialGradient>
    <radialGradient id="g2" cx="50%" cy="50%" r="50%">
        <stop stop-color="black" offset="0%"/>
        <stop id="grad2" stop-color="#885d33" offset="30%"/>
        <stop stop-color="white" offset="100%"/>
    </radialGradient>
    <mask id="m1">
      <path id="load" fill="#fff"></path>
    </mask>
  </defs>
  <circle id="circle1" r="2" stroke="black" stroke-width="0.2" fill="url(#g2)"></circle>
  <circle id="circle2" r="2" fill="url(#g1)" mask="url(#m1)"></circle>
  <path id="mask" stroke="none" fill="white"></path>
  <path id="eyelid" stroke="black" fill="none"></path>
<svg>

Answer №4

document.getElementById('loadingDialog').showModal();

var ticks = 300,
    ticker = setInterval(changeTick,1000);

function changeTick()
{
 document.getElementById('tickCounter').innerText = --ticks;
 if (0 === ticks) clearInterval(ticker);
}
#loadingDialog
{
 position:absolute;
 left:0 !important;
 top:0 !important;
 width:100vw !important;
 height:100vh !important; 
 padding:0; 
 min-width:100vw;
 min-height:100vh; 
 background-color:transparent !important;
}

#loadingDialog::backdrop{background-color:rgba(127,127,127,0.2);}

#tickCounter
{
 position:absolute;
 display:inline-block;
 width:100%;
 left:0;
 top:0;
} 
#loadingBox
{
 left:0 !important;
 top:0 !important;
 width:100vw !important;
 height:100vh !important;
 position:absolute;
 overflow:hidden;
}


#eyeball
{
 position:relative;
 top:-10vh;
 left:-6px;
 width:calc(24vh + 12px);
 height:calc(24vh + 12px);
 box-sizing:border-box;
 background:rgba(0,128,128,0.5);
 border-radius:100%;
 border:1px solid transparent;
 box-shadow:inset 0 0 18px 2px blue;
 z-index:99999998;
}


#secondsCounter
{
 position:absolute;
 left:calc(50vw - 12vh);
 top:46vh;
 width:24vh;
 height:24vh;
 font-size:8vh;
 text-align:center;
 display:block;
 
}

#eyeIcon
{
 position:absolute;
 top:27vh;
 left:calc(50vw - 23vh);
 width: 46vh;
 height: 46vh;
 background-color: rgba(255,255,255,.9);
 border-radius: 100% 0px;
 transform: rotate(45deg); 
 mix-blend-mode:overlay;
 z-index:199999999;
 box-shadow:0 -0.5vh 0 2px #f1c27d,inset 0 6px 4px 4px black;
}
body,html
{
 background:black;
 font-family:arial;
}
<dialog id='loadingDialog' class='loadingDialog'>
   <div id='loadingBox'>
    <div id='secondsCounter'><span id='tickCounter'>10</span><div id='eyeball'></div></div>
   <div id='eyeIcon'></div> 
   </div>  
  </dialog>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

I'm having trouble keeping items aligned on my navigation bar right after a form. Can anyone help me with this?

I'm having trouble fitting a navbar with these elements: My brand logo A lengthy search bar and submit button filling the entire height of the navbar. Space at the end of #2 for another button of equal height. ... all in one line without collapsing ...

Positioning of header, main content, and footer elements

My webpage is structured into three sections: the header, the main, and the footer. The header at the top has a fixed height of 109px with a 6px border. The main section should extend across the entire page below the header and reach the bottom where the ...

Preview a WordPress theme in its pure form (without any website content)

My local WordPress site is called Creative Architects and I am currently using the Twenty Sixteen theme. Now, as I search for themes for another website, I encounter an issue where the selected theme applies my current website's content to its preview ...

Which is more effective: coding with just plain JavaScript and CSS, or utilizing frameworks?

As a student, is it more beneficial to focus on utilizing pure JavaScript & CSS or frameworks? And which approach is best suited for the professional field? ...

The expected response from CSS3 animated images may not be fully realized

I've been working on this project with 4 images. While hovering over one image causes the others to change size as intended, I can't figure out why the ones on the left are not following the same pattern even though I have specified that they sho ...

I could use some input on the best way to make a background video fill the screen while incorporating overlay text

Struggling to make the background video fit perfectly on all devices and browsers? While it looks great in Chrome now, I'm still facing some clipping issues with IE Edge. Any experts out there who can offer their validation or suggest improvements? I ...

The text alongside the radio button is not aligned vertically

Hello, I am attempting to vertically align text next to my radio button but encountering some issues. .radio { cursor: pointer; display: inline-flex; } .cui-a-radio-button__input { display: none; } .cui-a-radio-button__input:disabled + .cui-a-rad ...

Is it possible to have unique styles for individual tabs in a mat-tab-group using Angular Material?

Is it possible to have different text colors for each tab in a mat-tab-group and change the color of the blue outline at the bottom? If so, can you provide guidance on how to achieve this? Here is the default appearance of the tabs in my app: https://i.st ...

Executing a jQuery script on various elements of identical types containing different content (sizes)

I am currently working on a jQuery script that will center my images based on the text block next to them. Since I am using foundation 5, I have to use a jQuery script to override the CSS instead of using vertical-align: middle. The script looks like thi ...

How come my overlay consistently extends beyond the boundaries of my image?

The overlay on my current image is extending beyond the image's current size. I initially resized the image because it was too large for my needs, and now the overlay is not functioning correctly HTML: <div class="col-md-5 order-md-2 text-cen ...

Is it possible to create HTML code that remains unaffected by alterations in CSS class names?

Having been using Twitter Bootstrap 3 for some time, the arrival of Twitter Bootstrap 4 has caught my attention. The new Twitter Booststrap comes with various changes, such as the renaming of .table-condensed to .table-sm, for instance. In anticipation of ...

Element with negative z-index overlapping background of parent element

I am currently working on creating a CSS3 animated menu, but I am facing an issue where elements with low z-index values are displaying on top of their containing elements. This is causing the "sliding out" effect to be ruined. Here is the HTML: <html ...

The issue with property unicode malfunctioning in bootstrap was encountered

I have tried to find an answer for this question and looked into this source but unfortunately, when I copy the code into my project, it doesn't display Unicode characters. section { padding: 60px 0; } section .section-title { color: #0d2d3e ...

elements overlap when styled differently

Hello all, I'm in need of some assistance with arranging the items on my webpage. As a beginner, I require detailed explanations as some things may be obvious to you but not to me :) Specifically, I am looking to enhance my header design. My goal is t ...

Moving a blank block to the top using CSS transformation

After trying to implement the translate3d(150px,0px,0px); on my "content" div, I encountered an issue where an empty block appears at the top of the page. Unfortunately, I am unsure about how to resolve this issue. <!DOCTYPE html> <html> <h ...

Utilizing extra CSS for a full-width Wordpress featured image makes for a visually stunning design

Is there a way to customize the CSS of a featured image in Wordpress Version 5.9.2 to appear full width? Currently, the image has a large margin and does not fill the entire width as seen on the page below: I attempted to achieve this by adding code to th ...

Implementing a Fixed Navbar in VueJS on Scroll

I am seeking help with creating a Fixed Navbar on Scrolling using Vue.js. I initially wrote some jQuery code for this functionality, but now I want to transition it to Vue.js. The updated code can be found in a file named navbar.js. Previous jQuery CODE ...

Firefox experiencing issues with width scaling

I'm having an issue where the main image is not resizing when I adjust the window size, specifically in Firefox. Here is the URL in question: How can I solve this problem? I attempted using width: 100% in .banner_right, but it only made things worse ...

Can display-inline-block support top margin and text alignment?

Can you please explain if the top margin and text-align properties work for both display: inline and display:inline-block? If they do, could you provide some insight as to why? ...

Is it possible to deactivate certain parameters in CSS transitions that are behaving strangely?

Currently, I have implemented a transition on the elements of a table to create a stylish border effect, and everything is functioning as expected. However, a sporadic issue arises upon page refresh where each individual box will transition in as if all v ...