14

Я пытаюсь создать экран ожидания c цифрами обратного отсчета, который показывает глаз вместе с веком, и глазное яблоко с эффектом радужной оболочки.

Учитывая, что многие из нас бессмысленно проводят время, глядя на такие лоадеры, я хочу создать такой эффект лоадера, при котором вращающийся "глаз" смотрит на зрителя и мигает.

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>

То, чего я смог достичь к настоящему времени, показано ниже - я установил здесь тикер на 300 секунд, просто в качестве иллюстрации, чтобы он продолжал работать в течение долгого времени - в реальном приложении время ожидания, вероятно, будет значительно меньше.

Пока этот эффект движется в правильном направлении, но ему все еще не хватает эффекта моргания века. Я подозреваю, что это легко выполнимо с помощью правильной манипуляцией box-shadow и простой анимацией.

Я был бы очень признателен всем, кто мог бы предложить улучшения для завершения этой реализации.

Свободный перевод вопроса Creating a CSS blinking eyelid effect от участника @DroidOS.

Alexandr_TT
  • 110,146
  • 23
  • 114
  • 384
  • ассоциация:https://stackoverflow.com/q/56804355/7394871 – Alexandr_TT Jun 29 '19 at 07:58
  • 2
    Я выбрал этот топик для перевода из-за интересной авторской идеи в подходе к созданию лоадеров. Согласитесь, уже приелись в теме лоадеров всякие вращающиеся элементы. Опираясь на идею моргания глаз, в качестве лоадера, можно придумать много интересных решений. Галочка и плюсы за оригинальные ответы гарантированы. Жду новых ответов. Добавляю тег SVG, для расширения области ответов. – Alexandr_TT Jun 29 '19 at 08:18
  • Добавлен вариант ответа с обратным счётчиком – Alexandr_TT Jun 29 '19 at 19:06
  • Учитывая, что многие из нас бессмысленно проводят время, глядя на такие лоадеры, я хочу создать такой эффект лоадера, при котором вращающийся "глаз" смотрит на зрителя и мигает. - повеселил.... )))) Прикольно звучит... – Air Jun 29 '19 at 19:50
  • @Air это же перевод, слова не мои, а автора вопроса enso – Alexandr_TT Jun 29 '19 at 19:52
  • Автоперевод ввёл ошибку: "Я подозреваю, что это легко выполнимо с помощью правильной манипуляцией box-shadow и простой анимацией."
    Надо так: "..с помощью правильной манипуляции.."
    – Варлам Ерофеич Jul 03 '19 at 04:47
  • @ Варлам Ерофеич это стилистика и она целиком зависит от уровня переводчика, видимо ваш уровень выше .. Я технический смысл в перевода не исказил? И то, что вы указали I suspect that в переводе звучит однозначно - Я подозреваю, что Мне было бы интересно увидеть от вас ваш вариант решения, в дополнительном ответе- вы это можете, я это знаю. – Alexandr_TT Jul 03 '19 at 05:15

5 Answers5

10

Двигаем 2 опорные точки в кривой Безье в зависимости от времени :

requestAnimationFrame(draw);

function draw(t) {

// двигаем зрачок circle.setAttribute('cx', Math.sin(t/1000)*2);

// анимируем градиент grad.setAttribute('offset', 40 + Math.sin(t/3000)*20 + '%');

// сглаживаем время по формуле easeInOutQuint t = Math.max(0, Math.sin(t/300)); t = (t<.5 ? 16ttttt : 1+16(--t)tttt)*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>

let rnd = (a, b) => (a||0) + ((b-a)||1) * Math.random();
let eyes = Array(22).fill(0).map((e, i) => ({
  i, 
  t: rnd(1, 10), 
  x: rnd(-96, 96), 
  y: rnd(-46, 46), 
  k: rnd(0.05,0.2), 
  r: rnd(-33,33)
}))

defs.innerHTML = eyes.map(e => &lt;radialGradient id="gradient_${e.i}" cx="50%" cy="50%" r="50%"&gt; &lt;stop stop-color="black" offset="0%"/&gt; &lt;stop id="color_${e.i}" stop-color="hsl(${rnd(0, 360)},55%,35%)" offset="30%"/&gt; &lt;stop stop-color="white" offset="100%"/&gt; &lt;/radialGradient&gt; &lt;clipPath id="clip_${e.i}"&gt; &lt;path&gt;&lt;/path&gt; &lt;/clipPath&gt;).join('');

g.innerHTML = eyes.map(e => &lt;g id="eye_${e.i}" transform="rotate(${e.r})scale(${e.k})translate(${e.x},${e.y})"&gt; &lt;circle r="2.4" stroke="black" fill="url(#gradient_${e.i})" stroke-width="0.2" clip-path="url(#clip_${e.i})"&gt;&lt;/circle&gt; &lt;path stroke="black" fill="none"&gt;&lt;/path&gt; &lt;/g&gt;).join('');

eyes.forEach(e => e.elements = { circle: document.querySelector(#eye_${e.i} circle), clipPath: document.querySelector(#clip_${e.i} path), eyelid: document.querySelector(#eye_${e.i} path), gradient: document.querySelector(#color_${e.i}) })

requestAnimationFrame(draw);

function draw(t) {

eyes.forEach(e => { e.elements.circle.setAttribute('cx', Math.sin(t/100/(e.t+e.i))2); e.elements.gradient.setAttribute('offset', 40 + Math.sin(t/300/(e.t+e.i))20 + '%'); let T = Math.max(0, Math.sin(t/50/(e.t+e.i))); T = (T<.5 ? 16TTTTT : 1+16(--T)TTTT)*6-3; let d = -7 0C-2 ${T} 2 ${T} 7 0; e.elements.clipPath.setAttribute('d', M-7 3${d}L7 3z); e.elements.eyelid.setAttribute('d', M${d}); })

requestAnimationFrame(draw); }

<svg viewbox="-20 -10 40 20" height="90vh">
  <defs id="defs"></defs>
  <g id="g"></g>
</svg>

PS: функция сглаживания взята отсюда: https://gist.github.com/gre/1650294

7

Версия на Smil

<svg width="300px" xmlns="http://www.w3.org/2000/svg">
 <g transform="translate(-8,-121)">
 <g id="eye">
  <ellipse cx="106.2113" cy="150.72321" rx="27.59226" ry="22.300594"/>
  <ellipse cx="108.47916" cy="149.58928" rx="10.205358" ry="8.3154764" fill="#fff"/>

<animateTransform attributeName="transform" attributeType="XML" type="translate" from="-5 0" to="5 0" dur="6s" repeatCount="indefinite"/> <animate attributeName="fill" dur="3000ms" repeatCount="indefinite" values="#1eb287; #1eb287; #1ca69e; #188fc2; #188fc2; #bb625e; #ca5f52; #1eb287;"/> </g> <path d="" fill="#fff" stroke="#000" stroke-width="12" stroke-linecap="round"> <animate attributeName="d" dur="1.8s" repeatCount="indefinite" values= "m195.79166 129.17857c-59.10423 2.90575-116.58946 5.9255-177.6488-3.02381; M 195.79166,129.17857 C 140.04024,174.83267 83.866918,194.57929 18.142857,126.15476; M 195.79166,129.17857 C 140.04024,174.83267 83.866918,194.57929 18.142857,126.15476; M 195.79166,129.17857 C 140.04024,174.83267 83.866918,194.57929 18.142857,126.15476; m195.79166 129.17857c-59.10423 2.90575-116.58946 5.9255-177.6488-3.02381;" keyTimes="0; 0.11; 0.21; 0.72; 1" /> <animate attributeName="stroke" dur="8000ms" repeatCount="indefinite" keyTimes="0; .0625; .208333333; .3125; 1" values="red; green; blue; yellow; pink;"/> </path> </g>

</svg>

7

Решение SVG SMIL

  • Анимация века глаза достигается изменением атрибута "d" <path> с помощью перехода из верхнего положения вниз.
  • Для реалистичности изображения века (придания объема) использованы радиальные градиенты
  • Паузы в верхнем и нижнем положении века достигаются повторением позиций

    Более подробно об анимациях с паузами - Как в SVG реализовать анимацию туда и обратно

Вариант решения без счётчика

<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.stack.imgur.com/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.3-13.1-6.8-20.1-9.1-5.2-1.7-10.6-2.9-16.1-3.4-8.6-0.8-17.5-1.3-26.1 0.4-12.4 2.5-24 8.4-35.4 14-14.6 7.1-42.2 24.6-42.2 24.6z" > <animate attributeName="d" dur="4s" repeatCount="indefinite" values=" 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.3-13.1-6.8-20.1-9.1-5.2-1.7-10.6-2.9-16.1-3.4-8.6-0.8-17.5-1.3-26.1 0.4-12.4 2.5-24 8.4-35.4 14-14.6 7.1-42.2 24.6-42.2 24.6z;
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.3-13.1-6.8-20.1-9.1-5.2-1.7-10.6-2.9-16.1-3.4-8.6-0.8-17.5-1.3-26.1 0.4-12.4 2.5-24 8.4-35.4 14-14.6 7.1-42.2 24.6-42.2 24.6z;

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 6.4 4.9 6.4 4.9-0.1 0.7 2.8-0.5 0.4 2.1-4.7 5.2-11 10.6-17.4 14.6-6 3.8-12.7 6.5-19.5 8.9-6.1 2.1-12.4 3.9-18.9 4.7-8.3 1-16.8 0.5-25.2 0-13-0.8-26-2.3-38.8-4.7-15.6-2.9-46.2-11.9-46.2-11.9z;
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 6.4 4.9 6.4 4.9-0.1 0.7 2.8-0.5 0.4 2.1-4.7 5.2-11 10.6-17.4 14.6-6 3.8-12.7 6.5-19.5 8.9-6.1 2.1-12.4 3.9-18.9 4.7-8.3 1-16.8 0.5-25.2 0-13-0.8-26-2.3-38.8-4.7-15.6-2.9-46.2-11.9-46.2-11.9z; 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.3-13.1-6.8-20.1-9.1-5.2-1.7-10.6-2.9-16.1-3.4-8.6-0.8-17.5-1.3-26.1 0.4-12.4 2.5-24 8.4-35.4 14-14.6 7.1-42.2 24.6-42.2 24.6z" /> </path> </svg>

Вариант с обратным счётчиком

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

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/xlink"
   width="50%" height="50%"   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> 
  <mask id="msk1" > 
   <path fill="white" d="M0.9 129.9C10.6 74.7 114.5 44.3 176.2 88.8c8 5.1 16.2 15.8 16.8 23.4C200 200 1.1 166.8 0.9 129.9" />
  </mask>
 </defs> 
 <g mask="url(#msk1)" >
<image xlink:href="https://i.stack.imgur.com/gDG2U.jpg" width="100%" height="100%"   />

<text id="txt1" x="98" y="130"  >100</text> 

 <path id="veko" fill="url(#grad1)" stroke="#B9B5BB" stroke-width="1" 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.3-13.1-6.8-20.1-9.1-5.2-1.7-10.6-2.9-16.1-3.4-8.6-0.8-17.5-1.3-26.1 0.4-12.4 2.5-24 8.4-35.4 14-14.6 7.1-42.2 24.6-42.2 24.6z" >
 <animate attributeName="d" dur="2.4s" repeatCount="indefinite" values="
   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.3-13.1-6.8-20.1-9.1-5.2-1.7-10.6-2.9-16.1-3.4-8.6-0.8-17.5-1.3-26.1 0.4-12.4 2.5-24 8.4-35.4 14-14.6 7.1-42.2 24.6-42.2 24.6z;    
   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.3-13.1-6.8-20.1-9.1-5.2-1.7-10.6-2.9-16.1-3.4-8.6-0.8-17.5-1.3-26.1 0.4-12.4 2.5-24 8.4-35.4 14-14.6 7.1-42.2 24.6-42.2 24.6z;

   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 6.4 4.9 6.4 4.9-0.1 0.7 2.8-0.5 0.4 2.1-4.7 5.2-11 10.6-17.4 14.6-6 3.8-12.7 6.5-19.5 8.9-6.1 2.1-12.4 3.9-18.9 4.7-8.3 1-16.8 0.5-25.2 0-13-0.8-26-2.3-38.8-4.7-15.6-2.9-46.2-11.9-46.2-11.9z;   
   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 6.4 4.9 6.4 4.9-0.1 0.7 2.8-0.5 0.4 2.1-4.7 5.2-11 10.6-17.4 14.6-6 3.8-12.7 6.5-19.5 8.9-6.1 2.1-12.4 3.9-18.9 4.7-8.3 1-16.8 0.5-25.2 0-13-0.8-26-2.3-38.8-4.7-15.6-2.9-46.2-11.9-46.2-11.9z;
   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.3-13.1-6.8-20.1-9.1-5.2-1.7-10.6-2.9-16.1-3.4-8.6-0.8-17.5-1.3-26.1 0.4-12.4 2.5-24 8.4-35.4 14-14.6 7.1-42.2 24.6-42.2 24.6z"    />
 </path>
 </g>

 </svg> 
 </div>

Связанный вопрос: Анимация svg глаз кролика с помощью атрибутов path “d” и “keyTimes”

0xdb
  • 51,614
Alexandr_TT
  • 110,146
  • 23
  • 114
  • 384
  • 1
    @MaximLensky неравномерность движения keyTimes про кролика помнишь :) https://ru.stackoverflow.com/a/976129/28748 – Alexandr_TT Jun 29 '19 at 13:10
  • @MaximLensky мне кажется такое многоцветье избыточно. Но, как говорится,- у каждого на вкус и цвет, свои фломастеры. Да и паузу для века, лучше сделать и в верхнем положении. – Alexandr_TT Jun 29 '19 at 14:42
  • Саша а вот keyTimes задаётся только от меньшего к большему..а можно ли сделать разные Times ..к примеру не 0; 0.2; 0.3; 1; а 0; 0.6; 0.2; 0.4; 1; вот так можно как не будь ? – Резидент Казахстана Jul 01 '19 at 04:22
  • @MaximLensky Первое значение обязательно 0 последнее - 1 внутри диапазона могут быть любые значения, но общее количество должно быть равно количеству значений в values Для подбора значений - Генератор keyTimes – Alexandr_TT Jul 01 '19 at 06:59
  • а даже у тебя там в генераторе всё равно по возрастанию – Резидент Казахстана Jul 01 '19 at 07:37
5

Я сделал бы это по-другому и рассмотрел бы вращение для получения эффекта мерцания. Трюк заключается в том, чтобы создать глаз с двумя элементами (веко), чтобы они имели возможность моргать.

Вот код только с анимацией мерцания:

.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>

Вот более реалистичное мигание век у всего глаза:

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>

Свободный перевод ответа Creating a CSS blinking eyelid effect от участника @Temani Afif.

Alexandr_TT
  • 110,146
  • 23
  • 114
  • 384
  • 2
    Не сказал бы что сплющивание зрачка смотрится реалистично) – Qwertiy Jul 04 '19 at 09:10
4

Размер зрачка зависит от расстояния до мышки.

Глаз поворачивается в направлении указателя.

Процент загрузки выражается размером сектора радужной оболочки.

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);

// двигаем зрачок let dx = mouse.x - innerWidth/2, dy = mouse.y - innerHeight/2, len = Math.sqrt(dxdx + dydy), ml = Math.min(len10/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);

// процент загрузки let r = 1.8, p = progress 2 Math.PI, px = rMath.cos(p), py = rMath.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}))

// анимируем градиент let offset = Math.max(0.2, (0.5 - len/2/innerHeight))*100 + "%";
grad1.setAttribute('offset', offset); grad2.setAttribute('offset', offset);

// сглаживаем время по формуле easeInOutQuint t = Math.max(0, Math.sin(t/300)); t = (t<.5 ? 16ttttt : 1+16(--t)tttt)*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}); }

<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>
</body>