Страницы

Поиск по вопросам

четверг, 28 ноября 2019 г.

Создание CSS3 эффекта мигающего века

#javascript #css3 #html5 #svg #css_animation


Я пытаюсь создать экран ожидания 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;
}

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


Ответы

Ответ 1



Двигаем 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 ? 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); } 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 => ` `).join(''); g.innerHTML = eyes.map(e => ` `).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 ? 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`; e.elements.clipPath.setAttribute('d', `M-7 3${d}L7 3z`); e.elements.eyelid.setAttribute('d', `M${d}`); }) requestAnimationFrame(draw); } PS: функция сглаживания взята отсюда: https://gist.github.com/gre/1650294

Ответ 2



Решение SVG SMIL Анимация века глаза достигается изменением атрибута "d" с помощью перехода из верхнего положения вниз. Для реалистичности изображения века (придания объема) использованы радиальные градиенты Паузы в верхнем и нижнем положении века достигаются повторением позиций Более подробно об анимациях с паузами - Как в 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; }
100
Связанный вопрос: Анимация svg глаз кролика с помощью атрибутов path “d” и “keyTimes”

Ответ 3



Версия на Smil

Ответ 4



Я сделал бы это по-другому и рассмотрел бы вращение для получения эффекта мерцания. Трюк заключается в том, чтобы создать глаз с двумя элементами (веко), чтобы они имели возможность моргать. Вот код только с анимацией мерцания: .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);} }
Вот более реалистичное мигание век у всего глаза: 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; } }
Источник ответа: @Temani Afif

Ответ 5



Размер зрачка зависит от расстояния до мышки. Глаз поворачивается в направлении указателя. Процент загрузки выражается размером сектора радужной оболочки. 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(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); // процент загрузки 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})`) // анимируем градиент 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 ? 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}`); }

Комментариев нет:

Отправить комментарий