Skip to content

Commit a4f38a6

Browse files
author
evinma
committed
feat: 0.1.0
1 parent 26f50ab commit a4f38a6

12 files changed

+567
-1
lines changed

.babelrc

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"presets": [
3+
[
4+
"@babel/preset-env",
5+
{ "modules": false },
6+
],
7+
],
8+
"plugins": [
9+
"@babel/plugin-proposal-function-bind"
10+
]
11+
}

.editorconfig

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
root = true
2+
3+
[*]
4+
charset = utf-8
5+
indent_style = space
6+
indent_size = 4
7+
end_of_line = lf
8+
insert_final_newline = true
9+
trim_trailing_whitespace = true

.eslintrc.js

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module.exports = {
2+
extends: 'standard',
3+
parser: 'babel-eslint',
4+
rules: {
5+
'indent': [2, 4],
6+
'promise/param-names': 0,
7+
'comma-dangle': [2, 'always-multiline'],
8+
},
9+
}

.gitignore

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
lib
2+
*.log
3+
coverage
4+
.DS_Store
5+
node_modules
6+
docs/.vuepress/dist/

README.md

+44-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,45 @@
11
# tua-body-scroll-lock
2-
Body scroll locking that just works with everything
2+
3+
## 介绍
4+
顾名思义 `tua-body-scroll-lock` 是用来锁住 `body` 滚动的包。并且针对`PC端`和移动端 `ios``android` 做了不同的处理,保证在各个端都可以完美使用。
5+
6+
## 安装
7+
8+
```bash
9+
$ npm i -S tua-body-scroll-lock
10+
# OR
11+
$ yarn add tua-body-scroll-lock
12+
```
13+
14+
## 使用
15+
16+
### 移动端
17+
18+
```js
19+
import { lock, unlock } from 'tua-body-scroll-lock'
20+
21+
// 禁止滑动后还需要内部可以滚动的元素(针对移动端ios处理)
22+
const targetElement = document.querySelector("#someElementId");
23+
24+
lock(targetElement)
25+
unlock(targetElement)
26+
```
27+
### PC端
28+
29+
> tips: PC端不需要targetElement; 不传targetElement也不想要控制台提示可以传`null`
30+
31+
```js
32+
import { lock, unlock } from 'tua-body-scroll-lock'
33+
34+
lock()
35+
unlock()
36+
```
37+
38+
## 测试
39+
[测试链接](https://sports.qq.com/mviptest/bodyscrolllock.htm)
40+
41+
![bodyScrollLock](http://img1.gtimg.com/sports/pics/hv1/49/61/2305/149898229.png)
42+
43+
44+
45+

commitlint.config.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module.exports = {
2+
// https://www.npmjs.com/package/@commitlint/config-conventional
3+
extends: ['@commitlint/config-conventional'],
4+
rules: {
5+
'subject-case': [0],
6+
},
7+
}

package.json

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
{
2+
"name": "tua-body-scroll-lock",
3+
"version": "0.1.0",
4+
"description": "Body scroll locking that just works with everything",
5+
"main": "lib/tua-bsl.umd.js",
6+
"module": "lib/tua-bsl.esm.js",
7+
"typings": "src/index.d.ts",
8+
"scripts": {
9+
"start": "rollup -c -w",
10+
"build": "rollup -c",
11+
"lint": "eslint --fix ./"
12+
},
13+
"eslintIgnore": [
14+
"lib/"
15+
],
16+
"husky": {
17+
"hooks": {
18+
"pre-push": "npm run build",
19+
"pre-commit": "lint-staged",
20+
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
21+
}
22+
},
23+
"lint-staged": {
24+
"{src,test}/**/*.js": [
25+
"eslint --fix",
26+
"git add"
27+
]
28+
},
29+
"devDependencies": {
30+
"@babel/core": "^7.3.3",
31+
"@babel/plugin-proposal-function-bind": "^7.2.0",
32+
"@babel/preset-env": "^7.3.1",
33+
"@commitlint/cli": "^7.5.2",
34+
"@commitlint/config-conventional": "^7.5.0",
35+
"babel-eslint": "^10.0.1",
36+
"eslint-config-standard": "^12.0.0",
37+
"eslint-plugin-import": "^2.16.0",
38+
"eslint-plugin-node": "^8.0.1",
39+
"eslint-plugin-promise": "^4.0.1",
40+
"eslint-plugin-standard": "^4.0.0",
41+
"husky": "^1.3.1",
42+
"lint-staged": "^8.1.4",
43+
"rollup": "^1.2.2",
44+
"rollup-plugin-babel": "^4.3.2",
45+
"rollup-plugin-commonjs": "^9.2.0",
46+
"rollup-plugin-eslint": "^5.0.0",
47+
"rollup-plugin-json": "^3.1.0",
48+
"rollup-plugin-node-resolve": "^4.0.0",
49+
"rollup-plugin-replace": "^2.1.0",
50+
"rollup-plugin-uglify": "^6.0.2"
51+
},
52+
"repository": {
53+
"type": "git",
54+
"url": "https://github.com/tuateam/tua-body-scroll-lock.git"
55+
},
56+
"keywords": [
57+
"body scroll",
58+
"body scroll lock",
59+
"react scroll lock",
60+
"react scroll",
61+
"scroll",
62+
"lock",
63+
"freeze",
64+
"toggle",
65+
"disable",
66+
"overflow",
67+
"modal",
68+
"lightbox",
69+
"react",
70+
"vanilla-js",
71+
"angular",
72+
"vue",
73+
"ios",
74+
"mobile",
75+
"desktop",
76+
"tablet",
77+
"bsl"
78+
],
79+
"author": "evinma",
80+
"license": "MIT"
81+
}

rollup.config.js

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// import json from 'rollup-plugin-json'
2+
import babel from 'rollup-plugin-babel'
3+
import replace from 'rollup-plugin-replace'
4+
import { eslint } from 'rollup-plugin-eslint'
5+
import commonjs from 'rollup-plugin-commonjs'
6+
import { uglify } from 'rollup-plugin-uglify'
7+
import nodeResolve from 'rollup-plugin-node-resolve'
8+
9+
const pkg = require('./package.json')
10+
11+
const plugins = [
12+
// json(),
13+
eslint(),
14+
babel(),
15+
commonjs(),
16+
nodeResolve({ jsnext: true, main: true, browser: true }),
17+
]
18+
19+
export default [
20+
{
21+
input: 'src/index.js',
22+
output: [
23+
{
24+
file: pkg.module,
25+
// exports: 'named',
26+
format: 'es',
27+
},
28+
],
29+
plugins,
30+
},
31+
{
32+
input: 'src/index.js',
33+
output: [
34+
{
35+
file: pkg.main,
36+
format: 'umd',
37+
exports: 'named',
38+
name: 'bodyScrollLock',
39+
},
40+
],
41+
plugins: [
42+
...plugins,
43+
replace({
44+
'process.env.NODE_ENV': true,
45+
}),
46+
],
47+
},
48+
{
49+
input: 'src/index.js',
50+
output: {
51+
file: `lib/tua-bsl.umd.min.js`,
52+
format: 'umd',
53+
exports: 'named',
54+
name: 'bodyScrollLock',
55+
},
56+
plugins: [
57+
...plugins,
58+
replace({
59+
'process.env.NODE_ENV': false,
60+
}),
61+
uglify(),
62+
],
63+
}
64+
]

src/index.d.ts

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export function lock (targetElement?: HTMLElement): void
2+
export function unlock (targetElement?: HTMLElement): void

src/index.js

+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
let locksElement = []
2+
let lockedNum = 0
3+
let initialClientY
4+
let unLockCallback
5+
let documentListenerAdded
6+
7+
let hasPassiveEvents = false
8+
if (typeof window !== 'undefined') {
9+
const passiveTestOptions = {
10+
get passive () {
11+
hasPassiveEvents = true
12+
return undefined
13+
},
14+
}
15+
window.addEventListener('testPassive', null, passiveTestOptions)
16+
window.removeEventListener('testPassive', null, passiveTestOptions)
17+
}
18+
19+
const detectOS = () => {
20+
const ua = navigator.userAgent
21+
const android = /(Android);?[\s/]+([\d.]+)?/.test(ua)
22+
const ipad = /(iPad).*OS\s([\d_]+)/.test(ua)
23+
const iphone = !ipad && /(iPhone\sOS)\s([\d_]+)/.test(ua)
24+
const ios = iphone || ipad
25+
26+
return {
27+
os: android ? 'android' : 'ios',
28+
ios,
29+
ipad,
30+
iphone,
31+
android,
32+
}
33+
}
34+
35+
const setOverflowHiddenPc = () => {
36+
const $ = document::document.querySelector
37+
const $body = $('body')
38+
const bodyStyle = { ...$body.style }
39+
const scrollBarWidth = window.innerWidth - document.body.clientWidth
40+
$body.style.overflow = 'hidden'
41+
$body.style.paddingRight = scrollBarWidth + 'px'
42+
$body.style.boxSizing = 'border-box'
43+
44+
return () => {
45+
$body.style.overflow = bodyStyle.overflow || ''
46+
$body.style.paddingRight = bodyStyle.paddingRight || ''
47+
$body.style.boxSizing = bodyStyle.boxSizing || ''
48+
}
49+
}
50+
51+
const setOverflowHiddenMobile = () => {
52+
const $ = document::document.querySelector
53+
const $html = $('html')
54+
const $body = $('body')
55+
56+
const htmlStyle = { ...$html.style }
57+
const bodyStyle = { ...$body.style }
58+
59+
const scrollTop = $html.scrollTop || $body.scrollTop
60+
61+
$html.style.overflow = 'hidden'
62+
$html.style.height = '100%'
63+
64+
$body.style.overflow = 'hidden'
65+
$body.style.top = `-${scrollTop}px`
66+
$body.style.width = '100%'
67+
$body.style.position = 'fixed'
68+
69+
return () => {
70+
$html.style.overflow = htmlStyle.overflow || ''
71+
$html.style.height = htmlStyle.height || ''
72+
73+
$body.style.overflow = bodyStyle.overflow || ''
74+
$body.style.height = bodyStyle.height || ''
75+
$body.style.width = bodyStyle.width || ''
76+
$body.style.position = ''
77+
$body.style.top = ''
78+
window.scrollTo(0, scrollTop)
79+
}
80+
}
81+
82+
const preventDefault = event => {
83+
if (event.cancelable) {
84+
event.preventDefault()
85+
}
86+
}
87+
88+
const handleScroll = (event, targetElement) => {
89+
const clientY = event.targetTouches[0].clientY - initialClientY
90+
91+
if (targetElement && targetElement.scrollTop === 0 && clientY > 0) {
92+
return preventDefault(event)
93+
}
94+
95+
if (targetElement && (targetElement.scrollHeight - 1 - targetElement.scrollTop <= targetElement.clientHeight) && clientY < 0) {
96+
return preventDefault(event)
97+
}
98+
99+
event.stopPropagation()
100+
return true
101+
}
102+
103+
const checkTargetElement = (targetElement) => {
104+
if (!targetElement && targetElement !== null && process.env.NODE_ENV !== 'production') {
105+
console.warn('If scrolling is also required in the floating layer, the target element must be provided')
106+
}
107+
}
108+
109+
export const lock = (targetElement) => {
110+
checkTargetElement(targetElement)
111+
112+
if (detectOS().ios) {
113+
if (targetElement && locksElement.indexOf(targetElement) < 0) {
114+
targetElement.ontouchstart = event => {
115+
initialClientY = event.targetTouches[0].clientY
116+
}
117+
targetElement.ontouchmove = event => {
118+
if (event.targetTouches.length === 1) {
119+
handleScroll(event, targetElement)
120+
}
121+
}
122+
locksElement.push(targetElement)
123+
}
124+
if (!documentListenerAdded) {
125+
document.addEventListener('touchmove', preventDefault, hasPassiveEvents ? { passive: false } : undefined)
126+
documentListenerAdded = true
127+
}
128+
} else {
129+
unLockCallback = detectOS().android ? setOverflowHiddenMobile() : setOverflowHiddenPc()
130+
}
131+
lockedNum += 1
132+
}
133+
134+
export const unlock = (targetElement) => {
135+
checkTargetElement(targetElement)
136+
137+
lockedNum -= 1
138+
if (lockedNum > 0 && !targetElement) return
139+
if (detectOS().ios) {
140+
const targetElementIndex = locksElement.indexOf(targetElement)
141+
if (targetElementIndex > -1) {
142+
targetElement.ontouchstart = null
143+
targetElement.ontouchmove = null
144+
locksElement.splice(targetElementIndex, 1)
145+
}
146+
if (documentListenerAdded && lockedNum <= 0) {
147+
document.removeEventListener('touchmove', preventDefault, hasPassiveEvents ? { passive: false } : undefined)
148+
149+
documentListenerAdded = false
150+
}
151+
} else {
152+
lockedNum <= 0 && unLockCallback()
153+
}
154+
}

0 commit comments

Comments
 (0)