1. Описание проблемы
- на мобильном
H5страница, с которой мы часто сталкиваемся点击按钮-->弹窗-->选择选项такая сцена. Когда полоса прокрутки появляется, когда вариантов слишком много, прокрутите полосу прокрутки до нижней или верхней части контейнера. Когда вы перетаскиваете полосу прокрутки вверх или вниз, действие прокрутки проникает внутрь, а нижняя частьbodyтакже будут прокручиваться вместе. - Краткое изложение проблемы: когда содержимое прокручивается вверх или вниз контейнера, оно перемещается вверх или вниз.принудительно прокрутить, то происходит прокатка
- Лучший пример: всплывающее окно с белой полосой Jingdong
2. Исследование решения
Ссылка много онлайн-решений, его можно разделить на три метода. После тщательного рассмотрения и анализа физических лиц приведены ниже:
использоватьjsконтролировать и изменятьcss
-
появляется всплывающее окно
1.1. Запишите место, где всплывающая кнопка появляется при нажатииscrollTop
1.2. Дайтеbodyстиль{'overflow': 'hidden'} -
Всплывающее окно закрывается
2.1. Аннулированиеbodyстиль{'overflow': 'hidden'}
2.2. Дайтеbodyстиль{'top': scrollTop}преимущество: Простое и быстрое внедрение
недостаток: В течение периода времени, когда всплывающее окно открывается и закрывается, если всплывающее окно не закрыто всем окном, вы увидитеbodyмерцание
использоватьjsУправление событием прокрутки по умолчанию для всплывающей области содержимого
-
появляется всплывающее окно
1.1 Прослушивание контейнеров контентаlayoutBoxизtouchstartа такжеtouchmoveмероприятие
1.2. МониторингtouchstartСобытие, узнайте начальную позицию пальца, чтобы начать прокрутку области содержимогоtargetY
1.3 МониторингtouchmoveСобытие, знать положение изменения в процессе прокрутки области содержимогоnewTargetY
1.4. Получите расстояние, на которое контент прокручивается до верхней части контейнераscrollTop / 内容可滚动的高度 scrollHeight / 当前容器的高度 clientHeight
1.5 Предотвращение поведения контейнеров с контентом по умолчанию при прокрутке вверх и вниз. (ключевой момент) -
Всплывающее окно закрывается нормально
преимущество: Начиная с источника проблемы проникновения качения, решить проблему,
jsРеализации не существуетiosПроблемы совместимости
недостаток: проверка электронной машины, проблемы с совместимостью у отдельных брендов.
Область содержимого всплывающего окна запрещена к прокрутке, используйтеjsИмитация полос прокрутки
-
появляется всплывающее окно
1.1. Мониторингtouchmoveсобытие, которое предотвращает поведение по умолчанию во всем
1.2. Мониторингtouchstartа такжеtouchmoveСобытие записывает расстояние, пройденное пальцем, используяtransform: translate3d()Атрибут для реализации прокрутки контента -
Всплывающее окно закрывается нормально
преимущество:
jsРеализации не существуетiosПроблемы совместимости
недостаток:iosПружинный опыт, который потерял родную полосу прокрутки на
Второе решение, принятое арендодателем на этот раз, продолжим
3. ОбзорscrollTop / scrollHeight / clientHeight
Сначала мы идем к mdn, чтобы запросить определения этих трех атрибутов.
-
scrollTop
-
scrollHeight
-
clientHeight
- Сочетание трех
图片摘自 MDN ,
链接:https://developer.mozilla.org/zh-CN/docs/Web/API/Element/clientHeight
https://developer.mozilla.org/zh-CN/docs/Web/API/Element/scrollHeight
https://developer.mozilla.org/zh-CN/docs/Web/API/Element/scrollTop
侵权则立即删除
В-четвертых, проблемы, с которыми столкнулись
touchmoveНевозможно предотвратить поведение по умолчанию, черезMDNНайдите информацию, чтобы найти:
На мобильных устройствах touchmove не может напрямую предотвратить поведение по умолчанию по следующим причинам:Улучшена производительность прокрутки с пассивным
Резюме:
Очки знаний:
EventTarget.addEventListener(type, listener, options)
optionsвнутреннийpassiveполе
Наша цель — заблокировать прокрутку страницы, чтоoptionsвнутреннийpassiveУстановить какfalse.
V. Предварительная реализация
написано какmixin
/**
* @author cunhang_wei
* @description 解决弹窗内容区滚动穿透到body的问题
* @param $refs.layoutBox 需要事先指定 内容容器
*/
export default {
data () {
return {
targetY: 0
}
},
mounted () {
if (this.$refs.layoutBox) {
this.$el.addEventListener('touchstart', this.handleTouchstart)
this.$el.addEventListener('touchmove', this.handleTouchmove, {
passive: false
})
}
},
methods: {
handleTouchstart (e) {
this.targetY = Math.floor(e.targetTouches[0].clientY) // 手指起始触摸位置
console.log('handleTouchstart', this.targetY)
},
handleTouchmove (e) {
let layoutBox = this.$refs.layoutBox // 内容容器
let newTargetY = Math.floor(e.targetTouches[0].clientY) // 手指滑动中触摸位置
let sTop = layoutBox.scrollTop // 内容滚动到容器顶部的高度
let sHeight = layoutBox.scrollHeight // 内容的可滚动高度
let cliHeight = layoutBox.clientHeight // 当前内容容器的高度
if (sTop <= 0 && newTargetY - this.targetY > 0 && e.cancelable) {
console.log('下拉到页面顶部')
e.preventDefault()
} else if (sTop >= sHeight - cliHeight && newTargetY - this.targetY < 0 && e.cancelable) {
console.log('上翻到页面底部')
e.preventDefault()
}
}
},
beforeDestroy () {
if (this.$refs.layoutBox) {
this.$el.removeEventListener('touchstart', this.handleTouchstart)
this.$el.removeEventListener('touchmove', this.handleTouchmove)
}
}
}
После написания я обнаружил, что этот файл миксина нужно вводить каждый раз, чтобы контролировать проникновение прокрутки всплывающего окна, что немного громоздко Я проверил официальную документацию Vue и нашел лучший способ, то естьглобальная директива
Шесть, оптимизация письма
написано как глобальная директиваno-through
/**
* @author cunhang_wei
* @description 解决弹窗内容区滚动穿透到body的问题(覆盖率90%)
* @description 用法
* <ul v-no-through>
* <li></li>
* <li></li>
* </ul>
**/
// 全局变量 targetY
var targetY = 0
export default {
name: 'no-through',
bind: function (el, binding) {
// 在binding这个对象里添加属性,是为了在unbind的时候可以取到
binding.handleTouchstart = function (event) {
targetY = Math.floor(event.targetTouches[0].clientY) // 手指起始触摸位置
}
binding.handleTouchmove = function (event) {
let newTargetY = Math.floor(event.targetTouches[0].clientY) // 手指滑动中触摸位置
let sTop = el.scrollTop // 内容滚动到容器顶部的高度
let sHeight = el.scrollHeight // 内容的可滚动高度
let cliHeight = el.clientHeight // 当前内容容器的高度
if (sTop <= 0 && newTargetY - targetY > 0 && event.cancelable) {
console.log('下拉到页面顶部')
event.preventDefault()
} else if (sTop >= sHeight - cliHeight && newTargetY - targetY < 0 && event.cancelable) {
console.log('上翻到页面底部')
event.preventDefault()
}
}
el.addEventListener('touchstart', binding.handleTouchstart)
el.addEventListener('touchmove', binding.handleTouchmove, {
passive: false
})
},
unbind: function (el, binding) {
// 重置全局变量 targetY
targetY = 0
el.removeEventListener('touchstart', binding.handleTouchstart)
el.removeEventListener('touchmove', binding.handleTouchmove, {
passive: false
})
}
}
// 最后再去 main.js 注册为全局指令,即可使用。
Семь, реальный тест машины
- ios тест пройден ios13
- Телефоны Xiaomi и Redmi прошли тест на Android 10
- OnePlus протестирован с Android 10
- Тест мобильного телефона Huawei прошел emui11 Android 10
- Существует проблема совместимости с Samsung S8 (предварительная оценка связана с базовой реализацией Samsung webView)
Восемь, резюме и размышление
ключ к решению проблем
- Знайте, при каких обстоятельствах происходит проникновение валков
- Знать
scrollTop / scrollHeight / clientHeightЭти три свойства, как их комбинировать, чтобы судить о прокрутке вверх или вниз - Мобильный телефон H5
touchmoveСобытия не поддерживаются по умолчаниюpreventDefault(), вам нужно вручную включить его - Знания по захвату событий и всплыванию событий:
Оптимизация письма
- мы используем
VueКогда , для многократно используемой логики js рекомендуется писать ее как миксинmixins - И для использования
DOMПри использовании узла рекомендуется записывать его в виде инструкцииdirectives
Наконец, учиться хорошо не плохо! Я люблю тебя, летчик, не забудь поставить палец вверх после прочтения
Последующие обновления (25.06.2021)
Недавно я просматривал github и обнаружил, что есть более совершенное решение:body-scroll-lock, может решить эту проблему очень хорошо
Пробовала локально, эффект тоже очень хороший, можно попробовать