Fork me on GitHub

图片拖动组件

来源:用房管理系统
作用:大图片小窗口查看时可以用左右上下拖动
框架:React
UI 库:ant design

客户需要展示楼层图,但是楼层图的大小不一,为了页面布局,展示时是适应的宽高,图片是变形的,且太小。
本来寻找了一个放大镜插件,向淘宝的图片展示一样的。

组件

React-image-magnify

使用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import ReactImageMagnify from 'React-image-magnify';

<ReactImageMagnify {...{
smallImage: {
alt: 'Wristwatch by Ted Baker London',
isFluidWidth: true,
src: watchImg300
},
largeImage: {
src: watchImg1200,
width: 1200,
height: 1800
}
}} />

优点:效果很好,交互性好
缺点:smallImage 宽高必须写死,或者 isFluidWidth: true,展示的效果其实都不太好。
具体可看开源项目

调好大图小图的宽高,展示效果还好,结果客户不满意,还是要弹框展示楼层原图,并且要拖动图片查看。
然后我百度到一堆原生 js 的方法,根据坐标的变化,改变图的位置的方法,写成了一个 React 的组件。
组件直接用的 antd 的 modal 弹框
该组件也用到了原生的方法获取元素设置位置,这个是我作为菜鸟想到的唯一方法

主要代码

1
2
3
4
5
6
7
<img
id='img'
style={{cursor:'pointer',position:'absolute'}} src={img}
onMouseDown={this.handlerMouseDown}
onMouseUp={this.handlerMouseUp}
onMouseMove={this.handlerMouseMove}
/>

 主要为图片绑定了三个事件,
onMouseDown:按下鼠标
onMouseUp:松开按住鼠标
onMouseMove:按住并移动鼠标

字面意思构够详细了吧

鼠标按下时的方法

offsetLeft:是当前元素左边框相对于 body 的偏移量,
clientLeft:clientleft=offsetleft-该对象的 border 值。

目前没弄太懂这俩的区别,看了好多介绍文章, 可能空间想象能力不太好,还是没看懂。

按照上面的额  概念,如果元素没有 border 两个是相等的,但是这里使用 client, 每次  选中图片 ,图片不会在上次移动后的位置基础上移动,而是复原到初始状态再移动。。

1
2
3
4
5
6
7
8
9
10
handlerMouseDown=(e)=>{
this.setState({mouseDownFlag:true, mouseDownX:e.pageX,mouseDownY:e.pageY })
//1、状态值变为true,表示图片可以拖动了
//2、记录当前鼠标按下的相对页面的位置
const img=document.getElementById('img')
const initX=img.offsetLeft;
const initY=img.offsetTop;
this.setState({initX,initY})
//3、记录图片当前相对body的位置
}

松开按住鼠标的  方法

1
2
3
4
handlerMouseUp=()=>{
this.setState({mouseDownFlag:false})
//2、鼠标松开。状态值变为false,表示图片不再拖动了
}

鼠标移动时的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
handlerMouseMove=(e)=>{
e.preventDefault && e.preventDefault();
//去掉鼠标的默认拖动事件
const img=document.getElementById('img')
const { mouseDownX, mouseDownY,initX,initY,width,height}=this.state
//鼠标拖动后的相对页面的位置
const moveX=e.pageX;
const moveY=e.pageY;

if(this.state.mouseDownFlag){
//计算鼠标移动了的距离+图片原来相对body的位置
let left=parseInt(moveX)-parseInt(mouseDownX)+parseInt(initX)
let top=parseInt(moveY)-parseInt(mouseDownY)+parseInt(initY)
//这里的限制是为了避免图片被完全移开,页面出现过多的空白。保证整个弹框里面展示的都是部分图片
left=left>0?0:
left<900-width?900-width
:parseInt(moveX)-parseInt(mouseDownX)+parseInt(initX)
+'px'
top=top >0?0:
top<650-height?650-height:top
+'px'
img.style.left=left;
img.style.top=top
}
}

下面是整个组件的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import React ,{ Component } from 'React'
import { Modal } from 'antd';


class Index extends Component {
constructor(props){
super(props);
this.state={
width:0,
height:0,
show:false,
mouseDownFlag:false,
mouseDownX:null,
mouseDownY:null,
initX:null,
initY:null,
}
}

componentWillUpdate(props){
const img=new Image();
img.src=props.img
img.onload=()=>{
let width=img.width;
let height=img.height;
this.setState({height,width})
}
}

handlerMouseUp=()=>{
this.setState({mouseDownFlag:false},()=>{})

}
handlerMouseDown=(e)=>{
this.setState({mouseDownFlag:true, mouseDownX:e.pageX,mouseDownY:e.pageY })
const img=document.getElementById('img')
const initX=img.offsetLeft;
const initY=img.offsetTop;
// const initX=img.clientLeft;
// const initY=img.clientTop;
this.setState({initX,initY})
}
handlerMouseMove=(e)=>{
e.preventDefault && e.preventDefault();

const img=document.getElementById('img')
const { mouseDownX, mouseDownY,initX,initY,width,height}=this.state
const moveX=e.pageX;
const moveY=e.pageY;
console.log(1,initX,initY)
console.log(2,mouseDownX,mouseDownY)
console.log(3,moveX,moveY)

if(this.state.mouseDownFlag){
let left=parseInt(moveX)-parseInt(mouseDownX)+parseInt(initX)
let top=parseInt(moveY)-parseInt(mouseDownY)+parseInt(initY)
left=left>0?0:
left<900-width?900-width
:parseInt(moveX)-parseInt(mouseDownX)+parseInt(initX)
+'px'
top=top >0?0:
top<650-height?650-height:top
+'px'
console.log('222',left,top)
img.style.left=left;
img.style.top=top
}
}

render(){
const { img ,name,children}=this.props
const {show}=this.state
return(
<span>
<span onClick={()=>{this.setState({show:true})}}>
{children}
</span>
<Modal
title={<div>
<span style={{
display: 'inline-block',
width:6,
height:16,
marginRight:10,
verticalAlign:'center',
backgroundColor: '#4A9478',verticalAlign:'center'
}}>
</span>{name}</div>}
visible={show}
closable
onCancel={()=>{ this.setState({show:false})}}
footer={null}
width={900}
bodyStyle={{backgroundColor:'#F3F2F6',borderBottomRightRadius:4,borderBottomLeftRadius:4,
height:650,overflow:'scroll',position:'relative',padding:0}}
destroyOnClose
maskClosable
>
<img
id='img'
style={{cursor:'pointer',position:'absolute'}} src={img}
onMouseDown={this.handlerMouseDown}
onMouseUp={this.handlerMouseUp}
onMouseMove={this.handlerMouseMove}
/>


</Modal>
</span>

)
}
}

export default Index

使用

1
2
3
< DragImg name="图片名称" img={Img}>
<img style={{width:500,height:600}} src={Img}/>
</DragImg>

img 传入的是图片信息,可以是路径或是处理好的 base64 编码的图片信息


-------------本文结束感谢阅读-------------