Нажмите, чтобы оставить отзыв
Не знаю, обратили ли внимание мои друзья на такую деталь.Некоторые кнопки приложений, ссылки и интерактивные карточки кажутся очень кликабельными, а другие как будто на белой бумаге.Что заставило их использовать?Есть ли у пользователей такие четкое различие? ...
Маленькая рука при перемещении мыши, анимация нажатия кнопки и всплывающее окно при нажатии мыши, вибрация экрана при нажатии приложения сенсорного экрана — эти эффекты дают пользователю своего рода是我的行为产生了这样的效果Эти эффекты также в совокупности называются обратной связью по щелчку.Хотя они кажутся деталями приложения, если вложить немного мысли, улучшение пользовательского опыта, вызванное этим, очень очевидно.
эффект волны воды
Здесь автор рекомендует друзьям один из любимых эффектов обратной связи автора. Когда пользователь нажимает, создается волновой эффект диффузии водяных волн с центром клика в качестве центра. Он подходит для различных сценариев, красивый и не преувеличенный. Ключ в том, чтобы дать пользователю очень интуитивно понятную обратную связь.
увидеть реализацию
Прежде всего, он инкапсулирован на основе пользовательских инструкций Vue 3. По сравнению с Vue 2, пользовательские инструкции Vue 3 не сильно изменились. Подробнее см.Пользовательская директива Vue3. Наша цель — завершить базовый прототип инструкции по волне на воде, которая здесь постепенно развивается.
Настройте стиль водной ряби по умолчанию
Водяная рябь на самом деле проходит через用户点击的位置Процесс создания небольшого круга и постепенного увеличения его размера до всего элемента, по которому щелкнули, поэтому здесь мы сначала формулируем базовый стиль волн на воде и устанавливаем чрезмерную анимацию.Выберите настройку кривой, если вы не знаете, как отлаживать кривая анимация, вы можете видеть этоэта статья
.my-ripple {
position: absolute;
top: 0;
left: 0;
z-index: 100;
border-radius: 50%;
background-color: currentColor;
opacity: 0;
transition: transform 0.2s cubic-bezier(0.68, 0.01, 0.62, 0.6), opacity 0.08s linear;
will-change: transform, opacity;
pointer-events: none;
}
Рассчитать положение и диаметр ряби на воде
если определено水波的直径,创建时的(x,y),过度动画结束时的(x,y)мы можем пройтиtransitionПерейдите к рендерингу анимации волны воды,创建时的(x,y)где пользователь щелкнул, но水波的直径а также过度动画结束时的(x,y)Как его рассчитать? Все наши элементы являются прямоугольниками. Независимо от того, щелкнет ли пользователь по любой координате элемента, окружность с гипотенузой прямоугольника в качестве диаметра может идеально покрыть весь элемент. Чтобы вычислить гипотенузу, мы используем小学数学知识Найдите сумму квадратов с обеих сторон и извлеките квадратный корень, чтобы получить следующее:过度动画结束时的水波推演图.
第一个箭头: 期望得到的水波
第二个箭头: 元素(0,0)点创建的水波
第三个箭头: 元素(0,0)点创建的水波, 不带圆角效果
Мы можем обнаружить, что волна воды, созданная точкой элемента (0,0), может получить волну воды, которую мы хотим, с определенным смещением, из которого мы можем сделать вывод
动画结束时的水波的尺寸 = 圆的斜边
创建时的(x,y) = 用户点击的位置
过度动画结束时的(x,y) = 元素(0,0)点创建的水波进行x和y的偏移得到
function computeRippleStyles(element, event) {
const { top, left } = element.getBoundingClientRect()
const { clientWidth, clientHeight } = element
const radius = Math.sqrt(clientWidth ** 2 + clientHeight ** 2) / 2
const size = radius * 2
const localX = event.clientX - left
const localY = event.clientY - top
const centerX = (clientWidth - radius * 2) / 2
const centerY = (clientHeight - radius * 2) / 2
const x = localX - radius
const y = localY - radius
return { x, y, centerX, centerY, size }
}
Создайте волну воды, когда нажата мышь
Затем нам нужно создать волну воды при нажатии мыши и отслеживать событие нажатия мыши.Вот пример стороны ПК, которая используется, когда только создается волна воды.transformуменьшить масштаб до0.3, это попытка автора создать относительно подходящий размер, а затем изменить преобразование, чтобы вызвать чрезмерную анимацию диффузии волн воды.Здесь также добавлена чрезмерная прозрачность, которая может сделать водную рябь более текстурированной.
function createRipple(event) {
const container = this
const { x, y, centerX, centerY, size } = computeRippleStyles(container, event)
const ripple = document.createElement('div')
ripple.classList.add('my-ripple')
ripple.style.opacity = `0`
ripple.style.transform = `translate(${x}px, ${y}px) scale3d(.3, .3, .3)`
ripple.style.width = `${size}px`
ripple.style.height = `${size}px`
// 记录水波的创建时间
ripple.dataset.createdAt = String(performance.now())
const { position } = window.getComputedStyle(container)
container.style.overflow = 'hidden'
position === 'static' && (this.style.position = 'relative')
container.appendChild(ripple)
window.setTimeout(() => {
ripple.style.transform = `translate(${centerX}px, ${centerY}px) scale3d(1, 1, 1)`
ripple.style.opacity = `.25`
})
}
const VRipple = {
mounted(el) {
el.addEventListener('mousedown', createRipple)
}
}
Уничтожьте водную волну, когда мышь поднята
Когда мышь поднята, вам нужно только найти сгенерированный узел водной волны, чтобы изменить прозрачность, а затем удалить узел водяной ряби после окончания анимации изменения прозрачности.
function removeRipple() {
const container = this
const ripples = container.querySelectorAll('.my-ripple')
if (!ripples.length) {
return
}
const lastRipple = ripples[ripples.length - 1]
// 通过水波的创建时间计算出扩散动画还需要执行多久,确保每一个水波都完整的执行了扩散动画
const delay = 300 - performance.now() + Number(lastRipple.dataset.createdAt)
setTimeout(() => {
lastRipple.style.opacity = `0`
setTimeout(() => lastRipple.parentNode?.removeChild(lastRipple), 300)
}, delay)
}
const VRipple = {
mounted(el) {
el.addEventListener('mousedown', createRipple)
document.addEventListener('mouseup', removeRipple)
},
unmounted(el) {
el.removeEventListener('mousedown', createRipple)
document.removeEventListener('mouseup', removeRipple)
}
}
Расширьте параметры волны с помощью привязок команд
Вы также можете расширить свои инструкции через привязку, например, вы можете предоставить такие параметры, как изменение цвета, отключение состояния и т. д., которые здесь не будут подробно описываться. Давайте посмотрим на результаты.
напиши в конце
Пока что мы реализовали простую инструкцию ripple, которая также доступна в нашей библиотеке компонентов, так что более полная версия может попасть в наш исходный код. В первую очередь хотелось бы поблагодарить сообщество Nuggets.Некоторые мелкие партнеры уже начали загружать код на наш склад.Также мы очень рады сделать такое с небольшими партнерами в сообществе.Кроме того, наш компонент Мы набираем энтузиастов для участия в работе. Заинтересованные партнеры могут присоединиться к обсуждению. Чтобы присоединиться, нужно перейти непосредственно на склад, отправить вопрос и оставить адрес электронной почты. Мы будем решать его как как можно скорее. Если вы заинтересованы, вы хотели бы стать звездой для нас. Следуйте за нами и сообществом Поддержка и интерес малых партнеров - наша самая большая мотивация.