Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: 滚轮控制缩放,拖动图片查看 #44

Open
wants to merge 14 commits into
base: dev
Choose a base branch
from
9 changes: 8 additions & 1 deletion docs/basic.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@
<template>
<div class="basic">
<img width="100px" v-for="(url, index) in list" :key="index" :src="url" @click="preview(url)">
<img-preview v-model="previewUrl"></img-preview>
<img-preview
v-model="previewUrl"
:enableScale="enableScale"
:enableGrab="enableGrab"
:maxScale="3">
</img-preview>
</div>
</template>
<script>
Expand All @@ -13,6 +18,8 @@ export default {
data() {
return {
previewUrl: '',
enableScale: true,
enableGrab: true,
list: [
'\/\/deepexi-moby.oss-cn-shenzhen.aliyuncs.com/undefinedWechatIMG7-1544429373120.jpeg',
'https:\/\/deepexi.oss-cn-shenzhen.aliyuncs.com/deepexi-services/logo_Deepexi_640x640.jpg'
Expand Down
148 changes: 122 additions & 26 deletions src/img-preview.vue
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
<template>
<div class="img-preview">
<div class="dialog-mask" v-if="url"></div>
<div class="dialog-mask" @click="handleClose" v-if="url"></div>
<div class="button-close" @click="handleClose" v-if="url && !isMobile">+</div>
<transition name="dialog-fade">
<div class="dialog-box" v-if="url" @click="handleClose">
<div
ref="imgContainer"
class="dialog-img-box"
:style="{width: `${size.width}px`, height: `${size.height}px`, transform: `translate(-50%, -50%) scale(${size.scale})`}"
></div>
</div>
<div v-if="url"
ref="imgContainer"
class="dialog-img-box"
:class="[moving ? 'grabbing' : '', enableGrab ? 'grab' : '']"
@wheel="enableScale && handleImgScale($event)"
@mousedown="enableGrab && handleMouseDown($event)"
@mousemove="enableGrab && handleMouseMove($event)"
@mouseup="enableGrab && handleMouseUp($event)"
@click="shouldHandleImgBoxClick && handleClose()"
:style="{
width: `${size.width}px`,
height: `${size.height}px`,
transform: `translate(${-50 + distanceX / 10}%, ${-50 + distanceY / 10}%) scale(${size.scale})`,}"
></div>
</transition>
</div>
</template>
<script>
import computedSize from './utils'
import {computedSize, isMobile} from './utils'

export default {
name: 'ImgPreview',
Expand All @@ -27,6 +35,27 @@ export default {
url: {
type: String,
default: ''
},
/**
* 是否开启滚轮缩放
*/
enableScale: {
type: Boolean,
default: false
},
/**
* 是否开启图片拖拽
*/
enableGrab: {
type: Boolean,
default: false
},
/**
* 滚轮最大放倍率,当 enableScale = true 时才有效
*/
maxScale: {
type: Number,
default: 2
}
},
mounted() {
Expand All @@ -35,6 +64,17 @@ export default {
destroyed() {
document.removeEventListener('keyup', this.handelKeyUp)
},
computed: {
shouldHandleImgBoxClick() {
if (this.isMobile) {
return true
}
if (!this.enableGrab) {
return true
}
return false
}
},
watch: {
url(url) {
if (!url) return
Expand All @@ -52,6 +92,8 @@ export default {
const img = new Image()
img.className = 'dialog-img'
img.src = url
img.draggable = 'false'
img.ondragstart = this.stopDrag
return new Promise(resolve => {
img.onload = () => resolve(computedSize(img))
}).then(size => {
Expand All @@ -68,18 +110,60 @@ export default {
},
data() {
return {
isMobile: isMobile(),
size: {},
cache: {}
cache: {},
moving: false,
startX: 0,
startY: 0,
distanceX: 0,
distanceY: 0
}
},
methods: {
// 阻止默认拖拽事件,兼容火狐
stopDrag(e) {
e.preventDefault()
return false
},
initPosition() {
this.moving = false
this.startX = 0
this.startY = 0
this.distanceX = 0
this.distanceY = 0
},
handleMouseDown(e) {
this.moving = true
this.startX = e.clientX - this.distanceX
this.startY = e.clientY - this.distanceY
},
handleMouseUp(e) {
this.moving = false
},
handleMouseMove(e) {
if (!this.moving) return
this.distanceX = e.clientX - this.startX
this.distanceY = e.clientY - this.startY
},
handleImgScale(e) {
e.preventDefault()
if (e.deltaY < 0) {
if (this.size.scale >= this.maxScale) return
this.size.scale += 0.1
DeepenLau marked this conversation as resolved.
Show resolved Hide resolved
} else {
if (this.size.scale <= 0.5) return
this.size.scale -= 0.1
}
},
handleClose() {
this.$emit('input', '')
/**
* 预览窗口关闭事件
* @event close
*/
this.$emit('close')
this.initPosition()
},
handelKeyUp(event) {
if (event.key === 'Escape' && this.url) {
Expand All @@ -92,6 +176,18 @@ export default {

<style lang="stylus">
.img-preview {
height: 0;
.button-close {
position: fixed;
top: 20px;
right: 40px;
transform: rotate(45deg);
color: #D0CFD0;
cursor: pointer;
z-index: 2300;
line-height: 1;
font-size: 40px;
}
.dialog-mask {
position: fixed;
top: 0;
Expand All @@ -101,25 +197,23 @@ export default {
background-color: rgba(0,0,0,.65);
height: 100%;
z-index: 2100;
}
.dialog-box {
position: fixed;
overflow: hidden;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 2200;
outline: 0;
cursor: zoom-out;
}
.dialog-img-box {
position: relative;
position: fixed;
z-index: 2200;
top: 50%;
left: 50%;
border-radius: 4px;
background-color: #fff;
box-shadow: 0 4px 12px rgba(0,0,0,.15);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
cursor: zoom-out;
&.grab {
cursor: grab;
}
&.grabbing {
cursor: grabbing;
}
.dialog-img {
width: 100%;
}
Expand All @@ -128,20 +222,22 @@ export default {
@keyframes fade-in {
0% {
opacity: 0;
transform: scale(0.6);
transform: translate(-50%, -50%) scale(0.6);
}
100% {
opacity: 1;
transform: scale(1);
// 弹出时,终点的 scale 应当是动态计算后的 scale, 不能写死
// transform: scale(1);
}
}
@keyframes fade-out {
0% {
transform: scale(1);
// 弹窗消失时,起点的 scale 应当是当前的 scale ,不能写死
// transform: scale(1);
}
100% {
opacity: 0;
transform: scale(0.6);
transform: translate(-50%, -50%) scale(0.6);
}
}
.dialog-fade-enter-active,
Expand Down
6 changes: 5 additions & 1 deletion src/utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export default function computedSize(img) {
export function computedSize(img) {
let w = window,
d = document,
e = d.documentElement,
Expand All @@ -25,3 +25,7 @@ export default function computedSize(img) {
}
return size
}

export function isMobile() {
return /mobile/gi.test(navigator.userAgent)
}