版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Zckguiying/article/details/85309720
功能主要包括:下载图片、等比缩放、旋转、全屏拖拽
用法:
import ImgPreview from '@/components/ImgPreview'
{/* 图片预览组件 */}
<ImgPreview
visible={previewVisible} // 是否可见
onClose={this.closePreview} // 关闭事件
src={licenceUrl} // 图片url
picKey={currentKey} // 下载需要的key,根据自己需要决定
isAlwaysCenterZoom={false} // 是否总是中心缩放,默认false,若为true,每次缩放图片都先将图片重置回屏幕中间
isAlwaysShowRatioTips={false} // 是否总提示缩放倍数信息,默认false,只在点击按钮时提示,若为true,每次缩放图片都会提示
/>
ImgPreview.js
// message缩放倍数提示,基于antd实现
import './style.less'
import React from 'react'
import config from '@/config'
import {message} from 'antd'
export default class ImgPreview extends React.Component {
constructor(props) {
super(props)
this.state = {
screenHeight: 0,
screenWidth: 0,
ratio: 1,
angle: 0,
defaultWidth: 'auto',
defaultHeight: 'auto',
imgSrc: '',
posTop: 0,
posLeft: 0,
isAlwaysCenterZoom: false, // 是否总是中心缩放
isAlwaysShowRatioTips: false, // 是否总是显示缩放倍数信息,默认点击按钮缩放时才显示
flags: false,
isDraged: false,
position: {
x: 0,
y: 0
},
nx: '',
ny: '',
dx: '',
dy: '',
xPum: '',
yPum: ''
}
this.percent = 100
}
componentDidMount() {
this.setState({
screenWidth: window.screen.availWidth,
screenHeight: window.screen.availHeight,
ratio: 1,
angle: 0
}, () => {
this.getImgSize()
})
}
componentWillReceiveProps (nextProps) {
this.setState({
imgSrc: nextProps.src,
isAlwaysCenterZoom: nextProps.isAlwaysCenterZoom,
isAlwaysShowRatioTips: nextProps.isAlwaysShowRatioTips
}, () => {
this.getImgSize()
})
}
// 获取预览图片的默认宽高和位置
getImgSize = () => {
let {ratio, isDraged, isAlwaysCenterZoom} = this.state
let posTop = 0
let posLeft = 0
// 图片原始宽高
let originWidth = this.originImgEl.width
let originHeight = this.originImgEl.height
// 默认最大宽高
let maxDefaultWidth = 540
let maxDefaultHeight = 320
// 默认展示宽高
let defaultWidth = 0
let defaultHeight = 0
if (originWidth > maxDefaultWidth || originHeight > maxDefaultHeight) {
if (originWidth / originHeight > maxDefaultWidth / maxDefaultHeight) {
defaultWidth = maxDefaultWidth
defaultHeight = Math.round(originHeight * (maxDefaultHeight / maxDefaultWidth))
posTop = (defaultHeight * ratio / 2) * -1
posLeft = (defaultWidth * ratio / 2) * -1
} else {
defaultWidth = Math.round(maxDefaultHeight * (originWidth / originHeight))
defaultHeight = maxDefaultHeight
posTop = (defaultHeight * ratio / 2) * -1
posLeft = (defaultWidth * ratio / 2) * -1
}
} else {
defaultWidth = originWidth
defaultHeight = originHeight
posTop = (defaultWidth * ratio / 2) * -1
posLeft = (defaultHeight * ratio / 2) * -1
}
if (isAlwaysCenterZoom) {
this.setState({
posTop: posTop,
posLeft: posLeft,
defaultWidth: defaultWidth * ratio,
defaultHeight: defaultHeight * ratio
})
} else {
// 若拖拽改变过位置,则在缩放操作时不改变当前位置
if (isDraged) {
this.setState({
defaultWidth: defaultWidth * ratio,
defaultHeight: defaultHeight * ratio
})
} else {
this.setState({
posTop: posTop,
posLeft: posLeft,
defaultWidth: defaultWidth * ratio,
defaultHeight: defaultHeight * ratio
})
}
}
}
// 下载
download = () => {
window.open(config.apiHost + '/downloadFromOss?key=' + this.props.picKey)
}
// 放大
scaleBig = (type = 'click') => {
let {ratio, isAlwaysShowRatioTips} = this.state
ratio += 0.15
this.percent += 15
this.setState({
ratio: ratio
}, () => {
this.getImgSize()
})
if (isAlwaysShowRatioTips) {
message.info(`缩放比例:${this.percent}%`, 0.2)
} else {
if (type === 'click') {
message.info(`缩放比例:${this.percent}%`, 0.2)
}
}
}
// 缩小
scaleSmall = (type = 'click') => {
let {ratio, isAlwaysShowRatioTips} = this.state
ratio -= 0.15
if (ratio <= 0.1) {
ratio = 0.1
}
if (this.percent - 15 > 0) {
this.percent -= 15
}
this.setState({
ratio: ratio
}, () => {
this.getImgSize()
})
if (isAlwaysShowRatioTips) {
message.info(`缩放比例:${this.percent}%`, 0.2)
} else {
if (type === 'click') {
message.info(`缩放比例:${this.percent}%`, 0.2)
}
}
}
// 滚轮缩放
wheelScale = (e) => {
e.preventDefault()
if (e.deltaY > 0) {
this.scaleBig('wheel')
} else {
this.scaleSmall('wheel')
}
}
// 旋转
retate = () => {
let {angle} = this.state
angle += 90
this.setState({
angle: angle
})
}
// 按下获取当前数据
mouseDown = (event) => {
let touch
if (event.touches) {
touch = event.touches[0]
} else {
touch = event
}
let position = {
x: touch.clientX,
y: touch.clientY
}
this.setState({
flags: true,
position: position,
dx: this.imgEl.offsetLeft,
dy: this.imgEl.offsetTop
})
}
mouseMove = (event) => {
let {dx, dy, position, flags} = this.state
if (flags) {
event.preventDefault()
let touch
if (event.touches) {
touch = event.touches[0]
} else {
touch = event
}
this.setState({
isDraged: true,
nx: touch.clientX - position.x,
ny: touch.clientY - position.y,
xPum: dx + touch.clientX - position.x,
yPum: dy + touch.clientY - position.y
}, () => {
this.imgEl.style.left = this.state.xPum + 'px'
this.imgEl.style.top = this.state.yPum + 'px'
})
}
}
mouseUp = () => {
this.setState({
flags: false
})
}
mouseOut = () => {
this.setState({
flags: false
})
}
// 关闭预览
closePreview = () => {
let {onClose} = this.props
this.setState({
ratio: 1,
angle: 0,
defaultWidth: 'auto',
defaultHeight: 'auto',
imgSrc: '',
posTop: 0,
posLeft: 0,
flags: false,
isDraged: false,
position: {
x: 0,
y: 0
},
nx: '',
ny: '',
dx: '',
dy: '',
xPum: '',
yPum: ''
}, () => {
this.getImgSize()
this.percent = 100
onClose()
})
}
render() {
let {screenWidth, screenHeight, posLeft, posTop, angle, imgSrc} = this.state
let {visible} = this.props
return (
<div className={'preview-wrapper' + (visible ? ' show' : ' hide')} style={{width: screenWidth, height: screenHeight}}>
<i onClick={() => {this.closePreview()}} className='iconfont icon-icon-test31'></i>
<div className='img-container'>
<img className='image'
width={this.state.defaultWidth}
height={this.state.defaultHeight}
onWheel={this.wheelScale}
style={{transform: `rotate(${angle}deg)`, top: posTop, left: posLeft}}
onMouseDown={this.mouseDown}
onMouseMove={this.mouseMove}
onMouseUp={this.mouseUp}
onMouseOut={this.mouseOut}
draggable='false'
src={imgSrc} ref={(img) => {this.imgEl = img}} alt="预览图片"/>
</div>
<img className='origin-image' src={imgSrc} ref={(originImg) => {this.originImgEl = originImg}} alt="预览图片"/>
<div className='operate-con'>
<div onClick={this.download} className='operate-btn'>
<i className='iconfont icon-icon-test10'></i>
<span>下载</span>
</div>
<div onClick={() => {this.scaleBig('click')}} className='operate-btn'>
<i className='iconfont icon-icon-test33'></i>
<span>放大</span>
</div>
<div onClick={() => {this.scaleSmall('click')}} className='operate-btn'>
<i className='iconfont icon-icon-test35'></i>
<span>缩小</span>
</div>
<div onClick={this.retate} className='operate-btn'>
<i className='iconfont icon-icon-test34'></i>
<span>旋转</span>
</div>
</div>
</div>
)
}
}
style.less
.preview-wrapper{
position: fixed;
top: 0;
left: 0;
background: rgba(0,0,0,0.6);
z-index: 999;
display: flex;
flex-direction: column;
align-items: center;
.icon-icon-test31{
position: fixed;
top: 60px;
right: 60px;
color: #ffffff;
transform:rotate(45deg);
font-size: 40px;
margin-right: 0;
}
.img-container{
width: 1px;
height: 1px;
position: fixed;
top: 50%;
left: 50%;
.image{
position: absolute;
cursor: pointer;
}
}
.origin-image{
position: relative;
z-index: -1;
visibility: hidden;
}
.operate-con{
position: fixed;
bottom:10%;
left:0;
right: 0;
margin: 0 auto;
width: 400px;
height: 70px;
background-color: #37474f;
border-radius: 100px;
opacity: 0.8;
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px 55px;
box-sizing: border-box;
.operate-btn{
width: 40px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
.iconfont{
color: #ffffff;
font-size: 15px;
margin-right: 0;
}
span{
color: white;
font-size: 12px;
margin-top: 15px;
}
}
}
}
.preview-wrapper.show{
display: flex;
}
.preview-wrapper.hide{
display: none;
}