Skip to content

Commit

Permalink
feat: 增加 prop: urlMapper & 优化锁定 body 滚动
Browse files Browse the repository at this point in the history
  • Loading branch information
zhuwenhan committed Feb 15, 2020
1 parent d4759fb commit fafd0ac
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 53 deletions.
37 changes: 19 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ export default {

> 此模式需自定义传递图片url集合,自己控制显隐,扩展性强,适用于自定义较强的场景
template
`template`
```html
<template>
<template v-for="(img, index) in imageUrls">
Expand All @@ -156,7 +156,7 @@ template
:start-position="startPosition"></image-preview>
</template>
```
script
`script`
```js
export default {
data () {
Expand All @@ -183,7 +183,7 @@ export default {

## 3. 支持自定义底部操作栏和 loading

template
`template`
```html
<image-preview ref="imgPreview">
<img
Expand All @@ -210,7 +210,7 @@ template
</span>
</image-preview>
```
script
`script`
```js
export default {
data () {
Expand Down Expand Up @@ -239,33 +239,34 @@ export default {

## Props

| prop | 描述 | 类型 | 默认值 |
| :--- | --- | --- | --- |
| `visible.sync` | 控制显隐。常规模式可用,插槽模式无效 | `Boolean` | `false` |
| prop <div style="width: 135px"></div> | 描述 | 类型 | 默认值 |
| --- | --- | --- | --- |
| `visible.sync` | 控制显隐。常规模式可用,插槽模式无效 | `Boolean` | `false` |
| `imageUrls` | url集合。常规模式可用,插槽模式无效 | `Array` | `[ ]` |
| `maxScale` | 最大缩放比例, 5 代表 5倍、500%, 不得小于1 | `Number` | `5` |
| `minScale` | 最小缩放比例, 0.1 代表 0.1倍、10%, 不得大于1 | `Number` | `0.1` |
| `urlMapper` | 预览 `url` 转换函数, 当缩略图 `url` 和预览图 `url` 不一致时,可用此函数转换。<br> 该函数接受缩略图 `url` 和预览下标 `index` 两个参数,返回的新值则作为预览图的 `url`。 <br>仅在插槽模式有效。 | `Function` <br> `(url: string, index: number): string` | `null` |
| `maxScale` | 最大缩放比例, `5` 代表 5倍、500%, 不得小于1 | `Number` | `5` |
| `minScale` | 最小缩放比例, `0.1` 代表 0.1倍、10%, 不得大于1 | `Number` | `0.1` |
| `scaleStep` | 单次缩放的比例(放大、缩小按钮), 0.1 代表 每次变化0.1倍,变化10% | `Number` | `0.1` |
| `angle` | 单次旋转的角度,默认90度 | `Number` | `90` |
| `include-selector` | css 选择器筛选指定图片,插槽模式下有效。<br> eg: include-selector = ".my-img" 实际筛选则为 img.my-img | `String` | `''` |
| `exclude-selector` | css 选择器过滤指定图片,插槽模式下有效。<br> eg: exclude-selector = ".other-img" 实际筛选则为 img:not(.ohter-img) | `String` | `''` |
| `filter` |`Array.prototype.filter` 函数,插槽模式下有效。<br> 过滤 imageList 集合,此参数存在时,includeSelector 和 excludeSelector 无效。 | `Function` | `() => true` |
| `close-on-press-escape` | 按ESC键是否关闭弹窗 | `Boolean` | `false` |
| `include-selector` | css 选择器筛选指定图片,插槽模式下有效。<br> eg: `include-selector = ".my-img"` 实际筛选则为 `img.my-img` | `String` | `''` |
| `exclude-selector` | css 选择器过滤指定图片,插槽模式下有效。<br> eg: `exclude-selector = ".other-img"` 实际筛选则为 `img:not(.other-img)` | `String` | `''` |
| `filter` |`Array.prototype.filter`的回调函数,插槽模式下有效。<br> 过滤 `image dom` 集合,此参数存在时,`includeSelector``excludeSelector` 无效。 | `Function` <br> `(image: HTMLImageElement): boolean` | `() => true` |
| `close-on-press-escape` | `ESC` 键是否关闭弹窗 | `Boolean` | `false` |
| `loading-delay` | 在图片加载完成之前展示 loading。此参数可控制 loading 展示的延时。<br> 如果图片在短时间内就加载完成,loading 刚显示就会瞬间消失,导致视觉上的闪动,用户体验不友好。<br> 而通过此参数可以控制 loading 显示的延迟。假设图片在设置的延时时间之内就加载完成,则不会展示 loading,超过延时时间且图片未加载完,才会展示 loading。<br> 默认为 300ms,建议设置一个合理的时间,如不需要可设置为 0。 | `Number` | `300` |

## Methods
| 方法名 | 类型 | 描述 |
| :--- | --- | --- |
| `rotate` | `rotate(angle: number \| string \| Function(number):number \| string)` | 旋转至指定角度。支持传入 `Number``字符串数字`以及 `Function`(如果是函数,则该函数第一个参数为旋转前的角度), 该函数需要返回一个数字或者字符串数字,代表最终需旋转的角度。
| `zoom` | `zoom(angle: number \| string \| Function(number):number \| string)` | 缩放到指定比例。支持传入 `Number``字符串数字`以及 `Function`(如果是函数,则该函数第一个参数为缩放前的比例), 该函数需要返回一个数字或者字符串数字,代表最终缩放的比例。
| `rotate` | `rotate(angle: number \| string \| ((oldAngle: number) => number \| string)): void` | 旋转至指定角度。支持传入 `Number``字符串数字`以及 `Function` (如果是函数,则该函数第一个参数为旋转前的角度), 该函数需要返回一个数字或者字符串数字,代表最终需旋转的角度。
| `zoom` | `zoom(angle: number \| string \|((oldScale: number) => number \| string)): void` | 缩放到指定比例。支持传入 `Number``字符串数字`以及 `Function` (如果是函数,则该函数第一个参数为缩放前的比例), 该函数需要返回一个数字或者字符串数字,代表最终缩放的比例。
| `reset` | `reset():void` | 重置到原始状态

## Slots
| 插槽名 | 插槽参数 | 描述 |
| 插槽名 | 插槽参数 <div style="width: 135px"></div> | 描述 |
| :--- | --- | --- |
| `default` | - | 插槽模式下可用,可传入任意 dom 结构,会自动识别其内部 `img` 标签并添加对应事件
| `default` | - | 插槽模式下可用,可传入任意 `dom` 结构,会自动识别其内部 `img` 标签并添加对应事件
| `operate` | - | 自定义底部操作栏
| `loading` | `loading: boolean` | 自定义 loading,该插槽只能有一个根元素。
| `loading` |`loading: boolean`| 自定义 loading,该插槽只能有一个根元素。


# License
Expand Down
13 changes: 11 additions & 2 deletions examples/pages/Example.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<div>
<div class="container">
<h3 class="mb16">第一种用法,自定义传递图片url集合,自己控制显隐</h3>
<img
v-for="(img, index) in imageUrls"
Expand All @@ -19,7 +19,8 @@
min-scale="0.6"
scale-step="0.2"
include-selector=".img , .s-img"
exclude-selector=".img2 , .img3">
exclude-selector=".img2 , .img3"
:url-mapper="urlMapper">
<img
v-for="(img, index) in imageUrls"
:key="index"
Expand Down Expand Up @@ -170,12 +171,20 @@ export default {
zoom () {
// scale 为缩放前的比例
return this.$refs.imgPreview.zoom(scale => scale * 1.5)
},
urlMapper (url, index) {
console.log(url, index)
return 'https://images.unsplash.com/photo-1536420124982-bd9d18fc47ed?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=2d98a0cbfe7514bbe11cbd95ba2554f7&auto=format&fit=crop&w=701&q=80'
}
}
}
</script>

<style scoped>
.container {
height: 110vh;
}
.mb16 {
margin-top: 16px;
}
Expand Down
13 changes: 7 additions & 6 deletions examples/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ const VueLoaderPlugin = require('vue-loader/lib/plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')

const { debug } = require('yargs').argv
const aliaMap = {
const aliasMap = {
dev: {
'vue-img-viewer': path.join(__dirname, '..', 'src')
},
preview: path.join(__dirname, '..', 'dist/index.js')
preview: {
'vue-img-viewer': path.join(__dirname, '..', 'dist/index.js')
}
}

module.exports = {
Expand All @@ -25,17 +27,16 @@ module.exports = {
},

resolve: {
alias: {
...(aliaMap[debug] || {})
},
alias: aliasMap[debug] || {},
extensions: ['.js', '.vue']
},

devServer: {
contentBase: path.join(__dirname, '__build__'),
port: 9000,
hot: true,
clientLogLevel: 'none'
clientLogLevel: 'none',
open: true
},

module: {
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
"private": false,
"main": "dist/index.js",
"scripts": {
"dev": "webpack-dev-server --inline --progress --config examples/webpack.config.js",
"dev": "webpack-dev-server --debug=dev --config examples/webpack.config.js",
"preview": "webpack-dev-server --debug=preview --config examples/webpack.config.js",
"prod": "webpack-dev-server --config examples/webpack.config.js",
"build": "NODE_ENV=production webpack --config webpack.config.js"
},
"prepublishOnly": "npm run build",
Expand Down
54 changes: 28 additions & 26 deletions src/ImagePreview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
class="image"
draggable="false"
@load="handleImageLoad"
@error="hidenLoading"
@abort="hidenLoading"
@error="hideLoading"
@abort="hideLoading"
@mousedown="handleImageMouseDown"
@wheel="wheelScale">
</div>
Expand Down Expand Up @@ -57,6 +57,7 @@

<script>
import Snippet from './Snippet'
import { forbiddenBodyScroll } from './util'
const DEFAULT_MAX_SCALE = 5 // 最大放大比例
const DEFAULT_MIN_SCALE = 0.1 // 最小放大比例
Expand Down Expand Up @@ -88,6 +89,10 @@ export default {
type: Array,
default: () => []
},
urlMapper: {
type: Function,
default: null
},
// 起始位置
startPosition: {
type: Number,
Expand Down Expand Up @@ -178,7 +183,9 @@ export default {
},
// 根据不同用法生成图片列表
finallyImageList () {
return this.isSlotMode ? this.urlList : this.imageUrls
return this.isSlotMode
? this.urlMapper ? this.urlList.map(this.urlMapper) : this.urlList
: this.imageUrls
},
finallyVisible () {
return this.isSlotMode ? this.slotModeVisible : this.visible
Expand All @@ -200,26 +207,11 @@ export default {
}
},
watch: {
visible (visible) {
if (visible) {
if (!this.isFirstShow) {
this.handleFirstVisible()
}
document.body.style.overflow = 'hidden'
} else {
document.body.style.overflow = ''
}
},
slotModeVisible (visible) {
if (visible) {
if (!this.isFirstShow) {
this.handleFirstVisible()
}
document.body.style.overflow = 'hidden'
} else {
document.body.style.overflow = ''
}
visible: {
immediate: true,
handler: 'handleVisible'
},
slotModeVisible: 'handleVisible',
startPosition: function (val, old) {
this.currentPosition = val
},
Expand Down Expand Up @@ -322,8 +314,8 @@ export default {
excludeSelectorList = this.excludeSelector.split(SPLIT_REG)
}
let selectorList = includeSelectorList.map((selector) => {
let fitlerSelector = excludeSelectorList.map(exSelector => `:not(${exSelector})`)
return BASE_SELECTOR + selector + fitlerSelector.join('')
let filterSelector = excludeSelectorList.map(exSelector => `:not(${exSelector})`)
return BASE_SELECTOR + selector + filterSelector.join('')
})
return selectorList.join(', ') || BASE_SELECTOR
},
Expand Down Expand Up @@ -357,6 +349,16 @@ export default {
top: 0
}
},
handleVisible (visible) {
if (visible) {
if (!this.isFirstShow) {
this.handleFirstVisible()
}
this.restoreBody = forbiddenBodyScroll()
} else {
this.restoreBody && this.restoreBody()
}
},
handleFirstVisible () {
// dom渲染后,将其插入body中
this.isFirstShow = true
Expand All @@ -376,7 +378,7 @@ export default {
},
handleImageLoad (e) {
this.initAspectRatio(e)
this.hidenLoading()
this.hideLoading()
this.$emit('imageLoad')
},
updatePosition (next) {
Expand Down Expand Up @@ -405,7 +407,7 @@ export default {
this.loading = true
}, this.loadingDelay)
},
hidenLoading () {
hideLoading () {
clearTimeout(this.loadingTimer)
this.loading = false
},
Expand Down
30 changes: 30 additions & 0 deletions src/util.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
export function forbiddenBodyScroll () {
let scrollBarWidth = window.innerWidth - document.documentElement.offsetWidth
let scrollBarHeight = window.innerHeight - document.documentElement.offsetHeight
if (scrollBarWidth || scrollBarHeight) {
document.body.style.overflow = 'hidden'
// 给 body 添加边距防止抖动
const { style } = document.body
let { paddingRight, paddingBottom } = getComputedStyle(document.body)

let initPaddingRight = style.paddingRight
let initPaddingBottom = style.paddingBottom

if (scrollBarWidth) {
paddingRight = parseInt(paddingRight) || 0
document.body.style.paddingRight = paddingRight + scrollBarWidth + 'px'
}
if (scrollBarHeight) {
paddingBottom = parseInt(paddingBottom) || 0
document.body.style.paddingBottom = paddingBottom + scrollBarHeight + 'px'
}

return function restoreBodyScroll () {
document.body.style.overflow = ''
document.body.style.paddingRight = initPaddingRight
document.body.style.paddingBottom = initPaddingBottom
}
} else {
return function () {}
}
}

0 comments on commit fafd0ac

Please sign in to comment.