Skip to content

Commit

Permalink
滑动穿透
Browse files Browse the repository at this point in the history
  • Loading branch information
Heath1998 committed Jan 3, 2021
1 parent 2ce7845 commit 633c2b3
Show file tree
Hide file tree
Showing 6 changed files with 387 additions and 4 deletions.
3 changes: 2 additions & 1 deletion config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ module.exports = {
proxyTable: {},

// Various Dev Server settings
host: 'localhost', // can be overwritten by process.env.HOST
host: '192.168.8.209', // can be overwritten by process.env.HOST
// host: '192.168.8.209', // can be overwritten by process.env.HOST
port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
autoOpenBrowser: false,
errorOverlay: true,
Expand Down
10 changes: 7 additions & 3 deletions src/App.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<template>
<div id="app">
<div style="width:100%;height:100%">
<body-scroll></body-scroll>
<!-- <div style="width:100%;height:100%">
<v-element-scrollbar direction="both">
<div style="height:100px; background:red;"></div>
<div style="height:100px; background:blue;"></div>
Expand Down Expand Up @@ -35,14 +36,17 @@
<div style="height:100px; background:red;"></div>
<div style="height:100px; background:blue;"></div>
</v-element-scrollbar>
</div>
</div> -->
</div>
</template>

<script>
import bodyScroll from './bodyScroll.vue'
export default {
name: 'App',
components: {
bodyScroll
}
}
</script>

Expand Down
78 changes: 78 additions & 0 deletions src/bodyScroll.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<template>
<div>
<div class="aaa" id="one">
<div style="background-color: red;">1</div>
<button @click="testunlock">testunlock</button>
<button @click="test2">testlock</button>
<div>3</div>
<div>2</div>
<div>2</div>
<div>2</div>
<div>2</div>
<div>2</div>
<div style="background-color: yellow;">2</div>
</div>
<div style="height: 2000px;">
<div style="height: 20px;">1</div>
<div style="height: 20px;">12</div>
<div style="height: 20px;">13</div>
<div style="height: 20px;">14</div>
<div style="height: 20px;">15</div>
<div style="height: 20px;">1</div>
<div style="height: 20px;">17</div>
<div style="height: 20px;">18</div>
<div style="height: 20px;">19</div>
<div style="height: 20px;">10</div>
<div style="height: 20px;">155</div>
<div style="height: 20px;">144</div>
<div style="height: 20px;">1333</div>
<div style="height: 20px;">1333</div>
<div style="height: 20px;">1333</div>
<div style="height: 20px;">1333</div>
<div style="height: 20px;">1333</div>
<div style="height: 20px;">1333</div>
<div style="height: 20px;">1333</div>
<div style="height: 20px;">1333</div>
<div style="height: 20px;">1333</div>
<div style="height: 20px;">111</div>
<div style="height: 20px;">12321312</div>
</div>
</div>
</template>

<script>
import { lock, unlock} from './utils/index.js'
export default {
mounted() {
let one = document.getElementById('one');
lock(one);
},
methods: {
testunlock() {
let one = document.getElementById('one');
unlock(one);
},
test2() {
let one = document.getElementById('one');
lock(one);
}
}
}
</script>

<style>
.aaa {
width: 90%;
height: 300px;
position: fixed;
top: 300px;
overflow: auto;
}
.aaa div {
height: 80px;
background-color: blue;
}
</style>
143 changes: 143 additions & 0 deletions src/utils/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import {
isServer,
detectOS,
getEventListenerOptions
} from './utils';

let lockedNum = 0;
let initialClientY = 0;
let initialClientX = 0;
let documentListenerAdded = false
let unLockCallback;

const lockedElements = [];
const eventListenerOptions = getEventListenerOptions({ passive: false });

const preventDefault = (event) => {
if (!event.cancelable) return

event.preventDefault()
}

const setOverflowHiddenMobile = () => {
const $html = document.documentElement;
const $body = document.body;
const scrollTop = $html.scrollTop || $body.scrollTop;
const htmlStyle = { ...$html.style };
const bodyStyle = { ...$body.style };

$html.style.height = '100%';
$html.style.overflow = 'hidden';

$body.style.top = `-${scrollTop}px`;
$body.style.width = '100%';
$body.style.height = 'auto';
$body.style.position = 'fixed';
$body.style.overflow = 'hidden';

return () => {
$html.style.height = htmlStyle.height || '';
$html.style.overflow = htmlStyle.overflow || '';

['top', 'width', 'height', 'overflow', 'position'].forEach((x) => {
$body.style[x] = bodyStyle[x] || '';
})

window.scrollTo(0, scrollTop);
}
}

const handleScroll = (event, targetElement) => {
if (targetElement) {
const {
scrollTop,
scrollLeft,
scrollWidth,
scrollHeight,
clientWidth,
clientHeight,
} = targetElement;
const clientX = event.targetTouches[0].clientX - initialClientX;
const clientY = event.targetTouches[0].clientY - initialClientY;
const isVertical = Math.abs(clientY) > Math.abs(clientX);

const isOnTop = clientY > 0 && scrollTop === 0;
const isOnLeft = clientX > 0 && scrollLeft === 0;
const isOnRight = clientX < 0 && scrollLeft + clientWidth + 1 >= scrollWidth;
const isOnBottom = clientY < 0 && scrollTop + clientHeight + 1 >= scrollHeight;

if (
(isVertical && (isOnTop || isOnBottom)) ||
(!isVertical && (isOnLeft || isOnRight))
) {
return preventDefault(event);
}
}

event.stopPropagation();
return true;
}


const lock = (targetElement) => {
if (lockedNum >= 1) return;
if (isServer()) return;
if (detectOS().ios) {
// ios
if (targetElement) {
if (targetElement && lockedElements.indexOf(targetElement) === -1) {
targetElement.ontouchstart = (event) => {
initialClientX = event.targetTouches[0].clientX;
initialClientY = event.targetTouches[0].clientY;
}

targetElement.ontouchmove = (event) => {
if (event.targetTouches.length !== 1) return;

handleScroll(event, targetElement);
}

lockedElements.push(targetElement);
}
}

if (!documentListenerAdded) {
document.addEventListener('touchmove', preventDefault, eventListenerOptions)
documentListenerAdded = true
}
} else if (detectOS().android) {
unLockCallback = setOverflowHiddenMobile()
}

lockedNum += 1;
}

const unlock = (targetElement) => {
if (lockedNum <=0 ) return;
if (isServer()) return;
lockedNum -= 1;

if (
!detectOS().ios &&
typeof unLockCallback === 'function'
) {
unLockCallback();
return;
}
// IOS
if (targetElement) {
const index = lockedElements.indexOf(targetElement);
if (index !== -1) {
targetElement.ontouchstart = null;
targetElement.ontouchmove = null;
lockedElements.splice(index, 1);
}
}

if (documentListenerAdded) {
document.removeEventListener('touchmove', preventDefault, eventListenerOptions);
documentListenerAdded = false;
}
}

export { lock, unlock}
42 changes: 42 additions & 0 deletions src/utils/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
export const isServer = () => typeof window === 'undefined';

export const detectOS = (ua) => {
ua = ua || navigator.userAgent
const ipad = /(iPad).*OS\s([\d_]+)/.test(ua)
const iphone = !ipad && /(iPhone\sOS)\s([\d_]+)/.test(ua)
const android = /(Android);?[\s/]+([\d.]+)?/.test(ua)
const ios = iphone || ipad

return { ios, android }
}

export function getEventListenerOptions (options) {
/* istanbul ignore if */
if (isServer()) return false

if (!options) {
throw new Error('options must be provided')
}
let isSupportOptions = false
const listenerOptions = {
get passive () {
isSupportOptions = true
return
},
}

/* istanbul ignore next */
const noop = () => {}
const testEvent = '__TUA_BSL_TEST_PASSIVE__'
window.addEventListener(testEvent, noop, listenerOptions)
window.removeEventListener(testEvent, noop, listenerOptions)

const { capture } = options

/* istanbul ignore next */
return isSupportOptions
? options
: typeof capture !== 'undefined'
? capture
: false
}
Loading

0 comments on commit 633c2b3

Please sign in to comment.