предисловие
На этот раз я поделюсь с вами дизайнерскими идеями и практиками обычных мобильных событий в одно касание.
Основная технология
В основном это использование следующих трех сенсорных событий на мобильном терминале для имитации и реализации пользовательских операций жестов.
- touchstart: срабатывает, когда палец касается экрана
- touchmove: срабатывает при движении пальца по экрану
- touchend: срабатывает, когда палец покидает экран
Концепция кардинга
Когда событие касания срабатывает, есть 3 набора данных, которые могут получить информацию о касании. Вы можете быть немного сбиты с толку этими наборами данных. Я постараюсь объяснить это вам простым языком в соответствии с моим собственным пониманием.
- касания: коллекция всех точек касания на всем экране
- targetTouches: коллекция всех точек касания текущего элемента DOM.
- changeTouches: набор изменений относительно последней точки касания.
Давайте сначала посмотрим на картинку
Как показано на рисунке, мы привязываем событие касания к узлу B, а кружок представляет собой точку касания.
На данный момент узел B имеет 3 точки касания, а именноtargetTouches
Массив имеет 3 элемента, которые хранят информацию о точке касания соответственно.touches
а такжеtargetTouches
Подобные.
Когда мы перемещаем палец 3 из узла B (всегда держите 3 пальца касающимися экрана), срабатывает событие touchmove,targetTouches
Осталось всего 2 штуки иtouches
На этот раз их еще троеchangedTouches
Есть только один (потому что только палец 3 меняется).
Затем отпускаем все пальцы от экрана, тогда в это время срабатывает событие touchend,touches
Осталось всего 0 шт.,targetTouches
осталось 0 шт.,changedTouches
Там 3 пункта (потому что 3 пальца менялись).
Что ж, понимание этих концепций помогает нам понять, когда менять значение, в каком сенсорном массиве в коде.
Идеи и практика
tap
TAP можно понимать как событие щелчка, а щелчок отличается, событие щелчка мобильного устройства имеет задержку примерно 300 мс, потому что браузер должен определить, следует ли дважды щелкнуть событие.
идеи
- touchstart: запишите момент времени и координаты x и y точки касания
- touchend: рассчитать разницу во времени между этим временем и началом, а также смещение по горизонтали и вертикали
Описание: разница во времени используется для оценки продолжительности касания пользователя. Если время превышает указанное время, событие касания является недействительным; смещение используется для определения наличия каких-либо следов движения в событии касания пользователя. Здесь допускаем небольшое смещение, потому что пальцы могут трясти корпус
выполнить
const tapDefaults = {
time: 250,
offset: 10
}
export default function tap (node, a, b) {
let st, sx, sy
let opts, callback
if (typeof a === 'function') {
callback = a
opts = Object.assign({}, tapDefaults, b)
} else {
callback = b
opts = Object.assign({}, tapDefaults, a)
}
node.addEventListener('touchstart', (e) => {
e.preventDefault() // 组织浏览器默认行为,防止触摸过程页面滚动
const touch = e.targetTouches[0]
st = e.timeStamp
sx = touch.pageX
sy = touch.pageY
}, false)
node.addEventListener('touchend', (e) => {
const touch = e.changedTouches[0]
if (
// 若为长按,则将时间判定条件更改
e.timeStamp - st <= opts.time &&
Math.abs(touch.pageX - sx) <= opts.offset &&
Math.abs(touch.pageY - sy) <= opts.offset
) {
callback && callback()
}
}, false)
}
doubletap
То есть событие двойного клика, если временной интервал между двумя кликами не превышает заданного времени, считается действительным.
идеи
- Первый действительный щелчок, запишите состояние, в противном случае сбросить состояние
- Второй действительный щелчок запускает событие и сбрасывает состояние
- Если временной интервал между двумя временами слишком велик, сбросьте состояние
выполнить
const tapDefaults = {
time: 250,
offset: 10
}
function handler (node, inject) {
let st, sx, sy
node.addEventListener('touchstart', (e) => {
e.preventDefault()
const touch = e.targetTouches[0]
st = e.timeStamp
sx = touch.pageX
sy = touch.pageY
}, false)
node.addEventListener('touchend', (e) => {
const touch = e.changedTouches[0]
inject({
time: e.timeStamp - st,
offsetX: Math.abs(touch.pageX - sx),
offsetY: Math.abs(touch.pageY - sy)
})
}, false)
}
export function doubletap (node, a, b) {
let opts, callback
let status = 0
if (typeof a === 'function') {
callback = a
opts = Object.assign({}, tapDefaults, b)
} else {
callback = b
opts = Object.assign({}, tapDefaults, a)
}
handler(node, (info) => {
if (
info.time <= opts.time &&
info.offsetX <= opts.offset &&
info.offsetY <= opts.offset
) {
if (status === 0) {
status = 1
// 时间间隔太长则重置状态
setTimeout(() => {
status = 0
}, opts.time)
} else if (status === 1) {
callback && callback()
status = 0
}
} else {
status = 0
}
})
}
longtap
То есть длительное нажатие, палец удерживается более указанного времени как действительное, и срабатывает, когда палец убирается.
идеи
- Идея аналогична событию касания, за исключением того, что изменено условие оценки времени, и оно изменено на то, сколько времени требуется для срабатывания.
выполнить
const longtapDefaults = {
time: 350,
offset: 10
}
// 这里代码逻辑和tap事件一样
// 更改时间判定为:
// e.timeStamp - st > opts.time
press
То есть событие нажатия, которое автоматически запускается нажатием и удержанием в течение более заданного времени, обратите внимание иlongtap
Разница в том, что при длительном касании нужно ждать, пока палец не уйдет, чтобы сработать, в то время какpress
Когда время нажатия достигает заданного значения, оно срабатывает автоматически, а палец в это время все еще находится на экране.
идеи
- touchstart: запишите координаты x и y в это время и запустите таймер для выполнения обратного вызова после указанного времени, по умолчанию 350 мс
- touchmove: отслеживать процесс перемещения и отменять таймер, если смещение слишком велико до того, как событие будет запущено
- touchend: отменить таймер
Анализ: в соответствии с приведенными выше идеями, если время нажатия короткое, таймер будет отменен, когда палец уйдет, и обратный вызов не будет запущен.
выполнить
const pressDefaults = {
time: 350,
offset: 10
}
export default function press (node, a, b) {
let opts, callback, sx, sy
let timer = null
if (typeof a === 'function') {
callback = a
opts = Object.assign({}, pressDefaults, b)
} else {
callback = b
opts = Object.assign({}, pressDefaults, a)
}
node.addEventListener('touchstart', (e) => {
e.preventDefault()
const touch = e.targetTouches[0]
sx = touch.pageX
sy = touch.pageY
timer = setTimeout(() => {
callback && callback()
}, opts.time)
}, false)
node.addEventListener('touchmove', (e) => {
const touch = e.targetTouches[0]
if (
Math.abs(touch.pageX - sx) > opts.offset ||
Math.abs(touch.pageY - sy) > opts.offset
) {
clearTimeout(timer)
}
}, false)
node.addEventListener('touchend', () => {
clearTimeout(timer)
}, false)
}
swipe
То есть событие скольжения пальца, сценарии приложения, такие как: карусельное изображение, скользящее влево и вправо, скользящая страница на весь экран и т. д., являются одним из наиболее распространенных жестов на мобильном терминале.
идеи
- touchstart: запись точки времени и положения точки касания
- touchmove: оценка скользящего смещения в режиме реального времени
- touchend: рассчитать скорость и направление скольжения и условно определить, следует ли запускать событие
Анализ: Учитывая, что некоторые анимационные эффекты и другие операции должны выполняться во время процесса скольжения, мы предоставляем события в скольжении для пользовательской настройки.Стоит отметить, что если вы хотите изменить положение ползунка в режиме реального времени, это лучше всего не перехватывать ток или предотвращать его тряску.Перехват вызовет явление застревания скольжения, а защита от сотрясений задержит синхронную операцию скольжения, кроме того, скорость скольжения также была обработана.В принципе, расстояние скольжения пользователя превышает указанное значение, оно считается действительным, но для лучшего взаимодействия с пользователем мы определяем, что если пользователь скользит очень быстро за короткий промежуток времени, это также считается эффективной операцией, и она не должна быть на большом расстоянии.
выполнить
const swipeDefaults = {
direction: 'horizontal', // vertical
speed: 200,
offset: 100,
prevent: true,
// touchmove: (offset) => {}
}
export default function swipe (node, a, b) {
let opts, callback, sTime, sTouch, eTouch
if (typeof a === 'function') {
callback = a
opts = Object.assign({}, swipeDefaults, b)
} else {
callback = b
opts = Object.assign({}, swipeDefaults, a)
}
node.addEventListener('touchstart', (e) => {
if (opts.prevent) {
e.preventDefault()
}
sTime = e.timeStamp
sTouch = eTouch = e.targetTouches[0]
}, false)
if (typeof opts.touchmove === 'function') {
node.addEventListener('touchmove', (e) => {
eTouch = e.targetTouches[0]
if (opts.direction === 'horizontal') {
opts.touchmove(eTouch.pageX - sTouch.pageX)
} else {
opts.touchmove(eTouch.pageY - sTouch.pageY)
}
}, false)
}
node.addEventListener('touchend', (e) => {
eTouch = e.changedTouches[0]
let time = e.timeStamp - sTime
let offset, direction
if (opts.direction === 'horizontal') {
offset = eTouch.pageX - sTouch.pageX
direction = offset > 0 ? 'right' : 'left'
} else {
offset = eTouch.pageY - sTouch.pageY
direction = offset > 0 ? 'down' : 'up'
}
if (
Math.abs(offset) >= opts.offset ||
Math.abs(offset) / time * 1000 >= opts.speed
) {
callback && callback(direction)
}
}, false)
}
заключительные замечания
Благодаря приведенным выше идеям и реализации кода мы создали библиотеку жестов для мобильного терминала одним касанием. Не терпится увидеть ее и испытать на себе.
Наконец, исходный код и документы, предоставленные на этот раз, прилагаются:GitHub.com/Энсон Вонг/...