欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

vue中pc移動滾動穿透問題及解決

 更新時間:2022年07月27日 11:20:19   作者:weixin_41655541  
這篇文章主要介紹了vue中pc移動滾動穿透問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

vue pc移動滾動穿透問題

上層無滾動(很簡單直接@touchmove.prevent)

<div @touchmove.prevent>
我是里面的內容
</div>

上層有滾動

如果上層需要滾動的話,那么固定的時候先獲取 body 的滑動距離,然后用 fixed 固定,用 top 模擬滾動距離;不固定的時候用獲取 top 的值,然后讓 body 滾動到之前的地方即可。

示例如下:

    watch:{
        statusShow(val){
            if(val) {
                this.lockBody();
            } else {
                this.resetBody();
            }
        },
        calendarShow(val){
            if(val) {
                this.lockBody();
            } else {
                this.resetBody();
            }
        }
    },
 
    methods: {
        lockBody() {
            const { body } = document;
            const scrollTop = document.body.scrollTop ||                                 
            document.documentElement.scrollTop;
            body.style.position = 'fixed';
            body.style.width = '100%';
            body.style.top = `-${scrollTop}px`;
        },
        resetBody() {
            const { body } = document;
            const { top } = body.style;
            body.style.position = '';
            body.style.width = '';
            body.style.top = '';
            document.body.scrollTop = -parseInt(top, 10);
            document.documentElement.scrollTop = -parseInt(top, 10);
        },
}

body是DOM對象里的body子節(jié)點,即 標簽;

documentElement 是整個節(jié)點樹的根節(jié)點root,即 標簽;

不同瀏覽器中,有的能識別document.body.scrollTop,有的能識別document.documentElement.scrollTop,有兼容性問題需要解決。

滑動穿透終極解決方案

問題描述

滑動穿透:浮層上的觸控會導致底層元素滑動。

問題探究

1、給body加overflow:hidden,pc端可以鎖scroll,移動端無效

pc端可以直接overflow:hidden解決

2、給body加overflow:hidden及絕對定位,背景會定位到頂部,如果是單屏頁面可以,長頁面不適用

如果彈出浮層時背景本來就沒有滾動距離,可以overflow:hidden加絕對定位解決

3、禁用touchmove事件,如@touchmove.prevent,對于彈層不需要的滑動的元素來說非常好用,因為scroll是touchmove觸發(fā)的,直接禁用就不會滑動穿透了,其實是直接就沒有系統(tǒng)滑動事件了。但是顯然不適合彈層需要滑動的情況

如果彈層時不需要滾動的,可以直接禁用touchmove就可以了

4、專門解決滑動穿透的第三方,存在巨大的兼容性問題。比如tua-body-scroll-lock,android可以完美解決,ios整個屏幕都不能滑動了。高星的body-scroll-lock據(jù)說android全掛,就沒有試了。

第三方有兼容性問題,可以自己判斷ua選用

5、終極解決方案:vant的popup

合理完美的解決方案,不存在兼容問題,適用于任何情況的popup。如果你不想為了鎖背景引入一個根本用不到的庫,可以一起來研究下popup的實現(xiàn)原理。

原理探究

如果不想看源碼想直接知道結論的話可以看這里:

因為常見會滑動穿透的場景都是:

  • 子元素本來就不可滾動,在子元素上滑動引起背景滾動,
  • 子元素可以滾動,但已經滾動到頂部或者底部,繼續(xù)滑動的話就會滑動穿透

所以如果子元素本身不可滾動,或者子元素氪滾動,但已經滾動到頂部或者底部時直接對touchmove進行默認事件阻止就可以阻止滑動穿透了。因為scroll事件是通過touchmove觸發(fā)的,禁止掉就不會觸發(fā)系統(tǒng)的scroll事件了。這樣就可以完美解決可滾動元素可以滾動但其背景在滑動時不為所動的效果了。

如果你想看看popup到底時如何做的可以來看看下面的源碼:

源碼分析:

src/popup/index.js文件中主要是參數(shù)及界面顯示的處理。

// src/popup/index.js
import { createNamespace, isDef } from '../utils';
import { PopupMixin } from '../mixins/popup';
import Icon from '../icon';
const [createComponent, bem] = createNamespace('popup');
export default createComponent({
? // 穿透處理的代碼在這里混入
? mixins: [PopupMixin],
? props: {
? ? round: Boolean,
? ? duration: Number,
? ? closeable: Boolean,
? ? transition: String,
? ? safeAreaInsetBottom: Boolean,
? ? closeIcon: {
? ? ? type: String,
? ? ? default: 'cross'
? ? },
? ? closeIconPosition: {
? ? ? type: String,
? ? ? default: 'top-right'
? ? },
? ? position: {
? ? ? type: String,
? ? ? default: 'center'
? ? },
? ? overlay: {
? ? ? type: Boolean,
? ? ? default: true
? ? },
? ? closeOnClickOverlay: {
? ? ? type: Boolean,
? ? ? default: true
? ? }
? },
? beforeCreate() {
? ? const createEmitter = eventName => event => this.$emit(eventName, event);
? ? this.onClick = createEmitter('click');
? ? this.onOpened = createEmitter('opened');
? ? this.onClosed = createEmitter('closed');
? },
? render() {
? ? if (!this.shouldRender) {
? ? ? return;
? ? }
? ? const { round, position, duration } = this;
? ? const transitionName =
? ? ? this.transition ||
? ? ? (position === 'center' ? 'van-fade' : `van-popup-slide-${position}`);
? ? const style = {};
? ? if (isDef(duration)) {
? ? ? style.transitionDuration = `${duration}s`;
? ? }
? ? return (
? ? ? <transition
? ? ? ? name={transitionName}
? ? ? ? onAfterEnter={this.onOpened}
? ? ? ? onAfterLeave={this.onClosed}
? ? ? >
? ? ? ? <div
? ? ? ? ? vShow={this.value}
? ? ? ? ? style={style}
? ? ? ? ? class={bem({
? ? ? ? ? ? round,
? ? ? ? ? ? [position]: position,
? ? ? ? ? ? 'safe-area-inset-bottom': this.safeAreaInsetBottom
? ? ? ? ? })}
? ? ? ? ? onClick={this.onClick}
? ? ? ? >
? ? ? ? ? {this.slots()}
? ? ? ? ? {this.closeable && (
? ? ? ? ? ? <Icon
? ? ? ? ? ? ? role="button"
? ? ? ? ? ? ? tabindex="0"
? ? ? ? ? ? ? name={this.closeIcon}
? ? ? ? ? ? ? class={bem('close-icon', this.closeIconPosition)}
? ? ? ? ? ? ? onClick={this.close}
? ? ? ? ? ? />
? ? ? ? ? )}
? ? ? ? </div>
? ? ? </transition>
? ? );
? }
});

根據(jù)mixins混入,可以看到核心部分應該在src/mixins/popup中,在這里針對lockscroll做出了兩種處理,綁定touchmove及touchstart并綁定class:van-overflow-hidden

// src/mixins/popup/index.js
import { context } from './context';
import { TouchMixin } from '../touch';
import { PortalMixin } from '../portal';
import { on, off, preventDefault } from '../../utils/dom/event';
import { openOverlay, closeOverlay, updateOverlay } from './overlay';
import { getScrollEventTarget } from '../../utils/dom/scroll';
export const PopupMixin = {
? mixins: [
? ? TouchMixin,
? ? PortalMixin({
? ? ? afterPortal() {
? ? ? ? if (this.overlay) {
? ? ? ? ? updateOverlay();
? ? ? ? }
? ? ? }
? ? })
? ],
? props: {
? ? // whether to show popup
? ? value: Boolean,
? ? // whether to show overlay
? ? overlay: Boolean,
? ? // overlay custom style
? ? overlayStyle: Object,
? ? // overlay custom class name
? ? overlayClass: String,
? ? // whether to close popup when click overlay
? ? closeOnClickOverlay: Boolean,
? ? // z-index
? ? zIndex: [Number, String],
? ? // prevent body scroll
? ? lockScroll: {
? ? ? type: Boolean,
? ? ? default: true
? ? },
? ? // whether to lazy render
? ? lazyRender: {
? ? ? type: Boolean,
? ? ? default: true
? ? }
? },
? data() {
? ? return {
? ? ? inited: this.value
? ? };
? },
? computed: {
? ? shouldRender() {
? ? ? return this.inited || !this.lazyRender;
? ? }
? },
? watch: {
? ? value(val) {
? ? ? const type = val ? 'open' : 'close';
? ? ? this.inited = this.inited || this.value;
? ? ? this[type]();
? ? ? this.$emit(type);
? ? },
? ? overlay: 'renderOverlay'
? },
? mounted() {
? ? if (this.value) {
? ? ? this.open();
? ? }
? },
? /* istanbul ignore next */
? activated() {
? ? if (this.value) {
? ? ? this.open();
? ? }
? },
? beforeDestroy() {
? ? this.close();
? ? if (this.getContainer && this.$parent && this.$parent.$el) {
? ? ? this.$parent.$el.appendChild(this.$el);
? ? }
? },
? /* istanbul ignore next */
? deactivated() {
? ? this.close();
? },
? methods: {
? ? open() {
? ? ? /* istanbul ignore next */
? ? ? if (this.$isServer || this.opened) {
? ? ? ? return;
? ? ? }
? ? ? // cover default zIndex
? ? ? if (this.zIndex !== undefined) {
? ? ? ? context.zIndex = this.zIndex;
? ? ? }
? ? ? this.opened = true;
? ? ? this.renderOverlay();
? ? ? // 穿透處理的核心部分
? ? ? if (this.lockScroll) {
? ? ? ? // 給touchstart及touchmove上綁定代碼
? ? ? ? // 關于touchStart及ontouchmove的代碼在TouchMixin的引入中
? ? ? ? on(document, 'touchstart', this.touchStart);
? ? ? ? on(document, 'touchmove', this.onTouchMove);
? ? ? ? if (!context.lockCount) {
? ? ? ? ? document.body.classList.add('van-overflow-hidden');
? ? ? ? }
? ? ? ? context.lockCount++;
? ? ? }
? ? },
? ? close() {
? ? ? if (!this.opened) {
? ? ? ? return;
? ? ? }
? ? ? if (this.lockScroll) {
? ? ? ? context.lockCount--;
? ? ? ? off(document, 'touchstart', this.touchStart);
? ? ? ? off(document, 'touchmove', this.onTouchMove);
? ? ? ? if (!context.lockCount) {
? ? ? ? ? document.body.classList.remove('van-overflow-hidden');
? ? ? ? }
? ? ? }
? ? ? this.opened = false;
? ? ? closeOverlay(this);
? ? ? this.$emit('input', false);
? ? },
? ? onTouchMove(event) {
? ? ? // 這個方法是touch文件中引入得,一會會看到
? ? ? // 主要計算滑動得方向及距離
? ? ? this.touchMove(event);
? ? ? // 方向計算
? ? ? const direction = this.deltaY > 0 ? '10' : '01';
? ? ? // 獲取滾動目標對象
? ? ? const el = getScrollEventTarget(event.target, this.$el);
? ? ? // 滾動元素相關屬性賦值
? ? ? const { scrollHeight, offsetHeight, scrollTop } = el;
? ? ? let status = '11';
? ? ? /* istanbul ignore next */
? ? ? if (scrollTop === 0) {
? ? ? ? // 沒有滾動的情況下,判定是否有滾動條
? ? ? ? status = offsetHeight >= scrollHeight ? '00' : '01';
? ? ? } else if (scrollTop + offsetHeight >= scrollHeight) {
? ? ? ? // 有滾動距離且滾動到底部
? ? ? ? status = '10';
? ? ? }
? ? ? /* istanbul ignore next */
? ? ? if (
? ? ? ? status !== '11' &&
? ? ? ? this.direction === 'vertical' &&
? ? ? ? !(parseInt(status, 2) & parseInt(direction, 2))
? ? ? ) {
? ? ? ? // 有滾動條且有滾動距離且方向為垂直時,阻止默認事件,即阻止頁面滾動
? ? ? ? // 所以原理其實是在可能會引起背景滑動穿透時禁止掉scroll事件
? ? ? ? // 因為常見會滑動穿透的場景都是子元素不滾動引起背景滾動,或者子元素已經滾動到頂部或者底部,繼續(xù)滑動的話就會滑動穿透,如果發(fā)現(xiàn)已經滾動到頂部或者底部時直接禁止掉touchmove就可以阻止滑動穿透了
? ? ? ? preventDefault(event, true);
? ? ? }
? ? },
? ? renderOverlay() {
? ? ? if (this.$isServer || !this.value) {
? ? ? ? return;
? ? ? }
? ? ? this.$nextTick(() => {
? ? ? ? this.updateZIndex(this.overlay ? 1 : 0);
? ? ? ? if (this.overlay) {
? ? ? ? ? openOverlay(this, {
? ? ? ? ? ? zIndex: context.zIndex++,
? ? ? ? ? ? duration: this.duration,
? ? ? ? ? ? className: this.overlayClass,
? ? ? ? ? ? customStyle: this.overlayStyle
? ? ? ? ? });
? ? ? ? } else {
? ? ? ? ? closeOverlay(this);
? ? ? ? }
? ? ? });
? ? },
? ? updateZIndex(value = 0) {
? ? ? this.$el.style.zIndex = ++context.zIndex + value;
? ? }
? }
};

來看看touch的處理,可以看到給touchstart及touchmove綁定了滑動方向及距離得計算,touchmove這個方法會在ontouchmove中被調用,注意名稱,不要混淆。

import Vue from 'vue';
const MIN_DISTANCE = 10;
function getDirection(x: number, y: number) {
? if (x > y && x > MIN_DISTANCE) {
? ? return 'horizontal';
? }
? if (y > x && y > MIN_DISTANCE) {
? ? return 'vertical';
? }
? return '';
}
type TouchMixinData = {
? startX: number;
? startY: number;
? deltaX: number;
? deltaY: number;
? offsetX: number;
? offsetY: number;
? direction: string;
};
export const TouchMixin = Vue.extend({
? data() {
? ? return { direction: '' } as TouchMixinData;
? },
? methods: {
? ? // touchstart獲取起始位置
? ? touchStart(event: TouchEvent) {
? ? ? this.resetTouchStatus();
? ? ? this.startX = event.touches[0].clientX;
? ? ? this.startY = event.touches[0].clientY;
? ? },
? ? // touchmove算得移動后得位移差,用來計算方向和偏移量
? ? touchMove(event: TouchEvent) {
? ? ? const touch = event.touches[0];
? ? ? this.deltaX = touch.clientX - this.startX;
? ? ? this.deltaY = touch.clientY - this.startY;
? ? ? this.offsetX = Math.abs(this.deltaX);
? ? ? this.offsetY = Math.abs(this.deltaY);
? ? ? this.direction = this.direction || getDirection(this.offsetX, this.offsetY);
? ? },
? ? resetTouchStatus() {
? ? ? this.direction = '';
? ? ? this.deltaX = 0;
? ? ? this.deltaY = 0;
? ? ? this.offsetX = 0;
? ? ? this.offsetY = 0;
? ? }
? }
});

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。 

相關文章

  • vue3.0實現(xiàn)考勤日歷組件使用詳解

    vue3.0實現(xiàn)考勤日歷組件使用詳解

    這篇文章主要為大家詳細介紹了vue3.0實現(xiàn)考勤日歷組件使用,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • vuejs+element-ui+laravel5.4上傳文件的示例代碼

    vuejs+element-ui+laravel5.4上傳文件的示例代碼

    本篇文章主要介紹了vuejs+element-ui+laravel5.4上傳文件的示例代碼,具有一定的參考價值,有興趣的可以了解一下
    2017-08-08
  • 在vue中實現(xiàn)給每個頁面頂部設置title

    在vue中實現(xiàn)給每個頁面頂部設置title

    這篇文章主要介紹了在vue中實現(xiàn)給每個頁面頂部設置title,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-07-07
  • Vue 自定義組件 v-model 使用詳解

    Vue 自定義組件 v-model 使用詳解

    這篇文章主要介紹了Vue 自定義組件 v-model 使用介紹,包括vue2中使用和vue3中使用,本文通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-08-08
  • vue項目如何解決數(shù)字計算精度問題

    vue項目如何解決數(shù)字計算精度問題

    這篇文章主要介紹了vue項目如何解決數(shù)字計算精度問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • Axios在vue項目中的封裝步驟

    Axios在vue項目中的封裝步驟

    Axios?是一個基于?promise?的網(wǎng)絡請求庫,可以用于瀏覽器和?node.js,是一個第三方插件,第三方異步請求工具庫,這篇文章主要介紹了Axios在vue項目中的封裝方法,需要的朋友可以參考下
    2022-10-10
  • Axios學習筆記之使用方法教程

    Axios學習筆記之使用方法教程

    axios是用來做數(shù)據(jù)交互的插件,最近正在學習axios,所以想著整理成筆記方便大家和自己參考學習,下面這篇文章主要跟大家介紹了關于Axios使用方法的相關資料,需要的朋友們下面來一起看看吧。
    2017-07-07
  • vue-awesome-swiper 基于vue實現(xiàn)h5滑動翻頁效果【推薦】

    vue-awesome-swiper 基于vue實現(xiàn)h5滑動翻頁效果【推薦】

    說到h5的翻頁,很定第一時間想到的是swiper。但是我當時想到的卻是,vue里邊怎么用swiper。這篇文章主要介紹了vue-awesome-swiper - 基于vue實現(xiàn)h5滑動翻頁效果 ,需要的朋友可以參考下
    2018-11-11
  • 學習 Vue.js 遇到的那些坑

    學習 Vue.js 遇到的那些坑

    這篇文章主要介紹了學習 Vue.js 遇到的那些坑,幫助大家更好的理解和使用vue框架,感興趣的朋友可以了解下
    2021-02-02
  • 如何使用vite搭建vue3項目詳解

    如何使用vite搭建vue3項目詳解

    Vite 是一個面向現(xiàn)代瀏覽器的更輕,更快的web應用開發(fā)工具,下面這篇文章主要給大家介紹了關于如何使用vite搭建vue3項目的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-07-07

最新評論