根据业务做了一个简易图片浏览组件,分享一下
<template>
<div class="content-box">
<p class="switch-button switch-pre" @click="pre"><i class="el-icon-arrow-left"></i></p>
<p class="switch-button switch-next" @click="next"><i class="el-icon-arrow-right"></i></p>
<ul v-if="imgUrl" class="operation-box">
<li class="operation-button operation-grow" @click="grow">
<i class="el-icon-zoom-in"></i>
</li>
<li class="operation-button operation-shrink" @click="shrink">
<i class="el-icon-zoom-out"></i>
</li>
<li class="operation-button operation-tranform" @click="clockwise">
<i class="el-icon-refresh-right"></i>
</li>
<li class="operation-button operation-tranform" @click="contrarotate">
<i class="el-icon-refresh-left"></i>
</li>
</ul>
<ul class="switch-dot-box">
<li v-for="(item,i) in this.imgs" :class="['switch-dot',{'switch-dot_now':index === i}]" :key="i+'img-preview'"></li>
</ul>
<div class="images-box" ref="imgBox">
<!-- draggable="false" -->
<img v-if="imgUrl" class="images-box_img" :src="imgUrl" ref="img" ondragstart="return false;" @mousedown.stop="mouseDown">
<div v-else class="img-error">
<i class="el-icon-picture-outline"></i>
</div>
</div>
</div>
</template>
<script>
import { calculator } from '@/util'
const getStyle = (obj, attr) => {
if (obj.currentStyle) {
return obj.currentStyle[attr]
} else {
return getComputedStyle(obj, null)[attr]
}
}
export default {
name: 'img-preview',
props: {
imgs: {
type: Array,
require: true
}
},
data () {
return {
// 图片伸缩值
flexValue: 1,
// 图片伸缩最小值
flexMin: 0.5,
// 图片伸缩最大值
flexMax: 3,
// 图片伸缩比例
flexProp: 0.2,
// z轴旋转值
rotateZ: 0,
imgUrl: '',
index: 0,
isPress: false,
// 鼠标移动的距离
disX: 0,
disY: 0,
// 鼠标坐标
clientX: 0,
clientY: 0
}
},
methods: {
contrarotate () {
this.flexValue = 1
if (this.rotateZ === 0) this.rotateZ = 270
else this.rotateZ = calculator.sub(this.rotateZ, 90)
this.transform()
this.$refs.img.style.setProperty('left', '0px')
this.$refs.img.style.setProperty('top', '0px')
},
clockwise () {
this.flexValue = 1
if (this.rotateZ === 270) this.rotateZ = 0
else this.rotateZ = calculator.add(this.rotateZ, 90)
this.transform()
this.$refs.img.style.setProperty('left', '0px')
this.$refs.img.style.setProperty('top', '0px')
},
grow () {
if (this.flexValue < this.flexMax) {
this.flexValue = calculator.add(this.flexValue, this.flexProp)
this.transform()
}
},
shrink () {
if (this.flexValue > this.flexMin) {
this.flexValue = calculator.sub(this.flexValue, this.flexProp)
this.transform()
}
},
transform () {
this.$refs.img.style.setProperty('transform', `rotateZ(${this.rotateZ}deg) scale(${this.flexValue})`)
},
pre () {
if (this.index === 0) {
this.index = this.imgs.length - 1
} else {
this.index = this.index - 1
}
this.flexValue = 1
this.imgUrl = this.imgs[this.index]
},
next () {
if (this.index === this.imgs.length - 1) {
this.index = 0
} else {
this.index = this.index + 1
}
this.flexValue = 1
this.imgUrl = this.imgs[this.index]
},
mouseDown (e) {
const that = this
const width = this.$refs.imgBox.clientWidth
const height = this.$refs.imgBox.clientHeight
const initX = parseInt(getStyle(this.$refs.img, 'left'))
const initY = parseInt(getStyle(this.$refs.img, 'top'))
const disX = e.clientX
const disY = e.clientY
document.onmousemove = (env) => {
// 鼠标移动的距离
let x = env.clientX - disX + initX
let y = env.clientY - disY + initY
// 设置边界 图片放大最多3倍
if (x < calculator.sub(100, calculator.mul(width, that.flexValue))) x = calculator.sub(100, calculator.mul(width, that.flexValue))
if (x > calculator.sub(calculator.mul(width, that.flexValue), 100)) x = calculator.sub(calculator.mul(width, that.flexValue), 100)
if (y < calculator.sub(100, calculator.mul(height, that.flexValue))) y = calculator.sub(100, calculator.mul(height, that.flexValue))
if (y > calculator.sub(calculator.mul(height, that.flexValue), 100)) y = calculator.sub(calculator.mul(height, that.flexValue), 100)
this.$refs.img.style.setProperty('left', `${x}px`)
this.$refs.img.style.setProperty('top', `${y}px`)
}
// 图形移出父盒子取消移动事件,防止移动过快触发鼠标移出事件,导致鼠标弹起事件失效
this.$refs.imgBox.onmouseleave = () => {
document.onmousemove = null
document.onmouseup = null
}
// 鼠标弹起后停止移动
document.onmouseup = () => {
document.onmousemove = null
document.onmouseup = null
}
// 阻止冒泡
return false
}
},
created () {
this.imgUrl = this.imgs[this.index]
},
watch: {
index: {
handler () {
if (this.imgUrl) {
this.flexValue = 1
this.rotateZ = 0
this.transform()
this.$refs.img.style.setProperty('top', `${0}px`)
this.$refs.img.style.setProperty('left', `${0}px`)
}
},
deep: true
}
}
}
</script>
<style lang="scss" scoped>
.content-box {
width: 100%;
height: 100%;
position: relative;
border: 1px solid coral;
}
.switch-button {
position: absolute;
top: 50%;
font-size: 30px;
transform: translateY(-50%);
cursor: pointer;
z-index: 1000;
font-size: 50px;
opacity: 0.5;
}
.switch-pre {
left: 3px;
}
.switch-next {
right: 3px;
}
.operation-box {
position: absolute;
top: 20px;
left: 30px;
width: 150px;
background: rgba(255, 255, 255, 0.5);
display: flex;
justify-content: space-around;
}
.operation-button {
font-size: 30px;
font-weight: 700;
padding: 1px;
border-radius: 50%;
cursor: pointer;
z-index: 1000;
color: #000;
}
.images-box {
box-sizing: content-box;
margin: 1px;
width: calc(100% - 2px);
height: calc(100% - 2px);
overflow: hidden;
position: relative;
background: #f4f5f6;
}
.images-box_img {
transform-origin: center center;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
cursor: move;
}
.img-error {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.switch-dot-box {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
padding: 5px;
display: flex;
z-index: 1000;
}
.switch-dot {
width: 10px;
height: 10px;
background: cyan;
border-color: cyan;
border-radius: 50%;
margin: 0 10px;
z-index: 1000;
transition-duration: 0.8s;
}
.switch-dot.switch-dot_now {
transform: scale(1.5);
}
</style>
效果图
calculator 是一个封装的加减乘除方法
const calculator = {
add (num1 = 0, num2 = 0, d) {
const s1 = num1.toString()
const s2 = num2.toString()
const s1Arr = s1.split('.')
const s2Arr = s2.split('.')
// eslint-disable-next-line eqeqeq
const d1 = s1Arr.length == 2 ? s1Arr[1] : ''
// eslint-disable-next-line eqeqeq
const d2 = s2Arr.length == 2 ? s2Arr[1] : ''
const maxLen = Math.max(d1.length, d2.length)
const m = Math.pow(10, maxLen)
const result = Number(tofixed(((num1 * m + num2 * m) / m), maxLen))
return typeof d === 'number' ? Number(tofixed(result, d)) : result
},
sub (num1, num2, d) {
return calculator.add(num1, -Number(num2), d)
},
mul (num1 = 0, num2 = 0, d) {
const s1 = num1.toString()
const s2 = num2.toString()
const m =
(s1.split('.')[1] ? s1.split('.')[1].length : 0) +
(s2.split('.')[1] ? s2.split('.')[1].length : 0)
const resultVal =
(Number(s1.replace('.', '')) * Number(s2.replace('.', ''))) /
Math.pow(10, m)
return typeof d !== 'number'
? Number(resultVal)
: Number(tofixed(resultVal, parseInt(d)))
},
div (num1, num2, d) {
const s1 = num1.toString()
const s2 = num2.toString()
const m =
(s2.split('.')[1] ? s2.split('.')[1].length : 0) -
(s1.split('.')[1] ? s1.split('.')[1].length : 0)
let resultVal =
(Number(s1.replace('.', '')) / Number(s2.replace('.', ''))) *
Math.pow(10, m)
resultVal = resultVal || 0
return typeof d !== 'number'
? Number(resultVal)
: Number(tofixed(resultVal, parseInt(d)))
}
}