#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 секунд, просто в качестве иллюстрации, чтобы он продолжал работать в течение долгого времени - в реальном приложении время ожидания, вероятно, будет значительно меньше. Пока этот эффект движется в правильном направлении, но ему все еще не хватает эффекта моргания века. Я подозреваю, что это легко выполнимо с помощью правильной манипуляцией 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; } Связанный вопрос: Анимация 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}`); }
Комментариев нет:
Отправить комментарий