有了上一篇图片放大镜的铺垫,今天的这个例子是缩小镜,因为裁剪的原图往往很大,不能在工作区域看到全部图片,所以,要有缩小镜来显示,当前裁剪的区域是原图的个部分.按照惯例首先看下效果图:
功能一:载入默认图片
功能二:选择本地图片
功能三:拖拽(鼠标直接拖动工作区视窗)
功能四:放大,缩小(点击按钮放大/缩小0.5倍,鼠标滚轮图片缩放)
功能五:利用canvas绘图裁剪图片
以下是源码,分享下,防止忘记:
html:
1 <script src="./javascript/jquery.min.js"></script> 2 <div id="workplace"> 3 <div id="workplacewrap"> 4 <div id="operateplace"> 5 <div class="showinfo"> 6 <span class="mouseposition"> 7 坐标(px): 8 x:<span class="mouseposition-x">0</span> 9 y:<span class="mouseposition-y">0</span> 10 </span> 11 </div> 12 <div id="sourceImage"> 13 <img src="./images/show-window/1-1.jpg" alt="" draggable="false"> 14 </div> 15 <div id="clipregion"></div> 16 <div class="operation"> 17 <input id="fileselector" type="file" hidden style=""> 18 <label class="btn" for="fileselector">选择文件</label> 19 <span id="btn-ResizeUp" class="btn">放大</span> 20 <span id="btn-ResizeDown" class="btn">缩小</span> 21 <span id="btn-Clip" class="btn">裁剪</span> 22 </div> 23 </div> 24 <div id="showwindow"> 25 <div class="showinfo"> 26 <span class="mouseposition"> 27 缩放比例: 28 <span class="enlargeratio"></span>% 29 </span> 30 </div> 31 <div style="position: absolute;"> 32 <div id="pologen"></div> 33 </div> 34 <div id="previewwindow"> 35 <img src="" alt="" draggable="false"> 36 </div> 37 <div class="operation"> 38 <span class="btn">保存</span> 39 </div> 40 41 </div> 42 </div>
css:
1 * { 2 font-size: 14px; 3 letter-spacing: 1px; 4 color: #000; 5 -ms-text-justify: auto; 6 text-justify: auto; 7 font-size: 12px; 8 } 9 10 #workplace { 11 width: 100%; 12 height: auto; 13 background: #f4f4f4; 14 15 } 16 17 #workplacewrap { 18 margin: 0px auto; 19 width: 1200px; 20 display: flex; 21 flex-direction: row; 22 align-items: flex-start; 23 justify-content: flex-start; 24 border-right: 1px solid #f0f0f0; 25 border-left: 1px solid #f0f0f0; 26 background: #f4f4f4; 27 font-family: "微软雅黑 Light"; 28 padding: 5px; 29 } 30 31 #operateplace { 32 padding: 4px; 33 border: 1px solid #bababa; 34 35 } 36 37 #sourceImage { 38 width: 500px; 39 height: 300px; 40 border: 1px solid #bababa; 41 background: none; 42 margin-bottom: -1px; 43 overflow: hidden; 44 } 45 46 #sourceImage img { 47 position: relative; 48 top: 0; 49 left: 0; 50 } 51 52 .btn { 53 background: #aa0000; 54 border: #F8F8F8; 55 color: white; 56 padding: 5px; 57 } 58 59 .btn:hover { 60 cursor: pointer; 61 background: #cc1234; 62 } 63 64 .operation { 65 display: flex; 66 flex-direction: row; 67 align-items: center; 68 justify-content: space-around; 69 height: 50px; 70 } 71 72 .showinfo { 73 74 margin-bottom: -1px; 75 font-size: 12px; 76 } 77 78 #showwindow { 79 justify-self: flex-end; 80 align-items: flex-end; 81 width: 500px; 82 height: auto; 83 border: #baabba 1px solid; 84 margin-left: 5px; 85 padding: 5px; 86 overflow: hidden; 87 position:relative; 88 } 89 90 #previewwindow { 91 border: 1px solid #bababa; 92 width: 500px; 93 height: 300px; 94 } 95 #pologen 96 { 97 border:1px solid red; 98 position:relative; 99 display:none; 100 }
javascript:
1 //代码重新修订 2 class ClipTools { 3 constructor() { 4 this.workview = $('#sourceImage'); 5 this.showview = $('#previewwindow'); 6 7 this.workviewimg=$('#sourceImage>img'); 8 this.showviewimg = $('#previewwindow>img'); 9 //工作区实际尺寸,由于图片有缩放,工作区尺寸实际是有workvimg尺寸决定的,而工作区视窗的是尺寸是固定的,本例中是500*300,这个尺寸默认就是最终截图的尺寸 10 //后面扩展,可以对工作视图的尺寸进行调整,截出不同尺寸的图片 11 this.workvwidth = 0; 12 this.workvheight = 0; 13 //同样展示框的尺寸,实际是右侧展示框图片缩放后的尺寸,本例中图片一定要全部放入展示视窗,所以,这个尺寸是小于等于展示视窗的尺寸的 14 this.showvwidth = $('#sourceImage').width(); 15 this.showvheight = $('#sourceImage').height(); 16 17 //右侧显示区图片缩放后的高度 18 this.showvimgheight=0; 19 //右侧显示区图片缩放后的宽度 20 this.showvimgwidth=0; 21 this.resizeRate = 0.5; 22 this.isScrollMouseResize = false; 23 //后面扩展,比如绘制裁剪区域时用,本例中没有实际意义 24 this.isClip=false; 25 26 this.isMouseDown=false; 27 //标识鼠标在工作区的坐标,offsetX 和offsetY(相对于视窗) 28 this.workpos_x = 0; 29 this.workpos_y = 0; 30 31 //图片顶点位置 32 this.sourceimg_l=0; 33 this.sourceimg_t=0; 34 //图片在计算拖动距离的时候,需要记录鼠标的起始位置,在鼠标的down->move中,起始位置以鼠标的down点为基准 35 this.lastMouse_x=0; 36 this.lastMouse_y=0; 37 //记录图片的原始尺寸 38 this.sourceimg_w = 0; 39 this.sourceimg_h = 0; 40 //记录图片缩放的比例 41 this.enlargeratio = 1.0; 42 43 //显示区图片和显示区域的比例 44 this.showToSourceWidthRatio=0.1; 45 this.showToSouceHeightRatio=0.1; 46 47 48 this.Init = this.Init.bind(this); 49 this.ImgInputFileChanged = this.ImgInputFileChanged.bind(this); 50 this.ResizeUp = this.ResizeUp.bind(this); 51 this.ResizeDown = this.ResizeDown.bind(this); 52 this.ShowWorkPos = this.ShowWorkPos.bind(this); 53 this.DrawClipRegionTosShow=this.DrawClipRegionTosShow.bind(this); 54 } 55 ShowWorkPos(x, y) { 56 $('.mouseposition-x').text(this.workpos_x); 57 $('.mouseposition-y').text(this.workpos_y); 58 $(".enlargeratio").text(($("#pologen").height()/this.showvimgwidth)*100); 59 60 this.DrawClipRegionTosShow(); 61 } 62 63 Init() { 64 //如果工作区域有默认图片 65 this.sourceimg_h = this.workviewimg.height(); 66 this.sourceimg_w = this.workviewimg.width(); 67 68 this.workvwidth = this.sourceimg_w; 69 this.workvheight = this.sourceimg_h; 70 71 this.showToSouceHeightRatio=this.showvheight/this.sourceimg_h; 72 this.showToSourceWidthRatio=this.showvwidth/this.sourceimg_w; 73 74 //右侧展示区域,展示当前工作区处于图片的区域 75 //按照最小比例对图片进行缩放 76 77 if (this.showToSouceHeightRatio<this.showToSourceWidthRatio) 78 { 79 this.showvimgheight=this.showvheight; 80 this.showvimgwidth=this.showToSouceHeightRatio*this.sourceimg_w; 81 } 82 else 83 { 84 this.showvimgheight=this.sourceimg_h*this.showToSourceWidthRatio; 85 this.showvimgwidth=this.showvwidth; 86 } 87 this.showviewimg.attr('src',this.workviewimg.attr('src')).width(this.showvimgwidth).height(this.showvimgheight); 88 89 $('#btn-Clip').click(()=>{ 90 let _cropCanvas = document.createElement('canvas'); 91 // 计算截取时从原图片的原始长度的坐标 92 //图片有缩放等,所以要利用原始数据进行计算 93 let _sy =-this.sourceimg_t/ (this.workvheight/this.sourceimg_h); 94 let _sx=-this.sourceimg_l/(this.workvwidth/this.sourceimg_w); 95 let _swidth=this.workview.width()/(this.workvwidth/this.sourceimg_w); 96 let _sheight=this.workview.height()/(this.workvheight/this.sourceimg_h); 97 let width=this.workview.width(); 98 let height=this.workview.height(); 99 //工作区域视窗就是图片的大小 100 _cropCanvas.width = width; 101 _cropCanvas.height = height; 102 // 绘制图片 103 _cropCanvas.getContext('2d').drawImage(this.workviewimg[0], _sx, _sy, _swidth, _sheight, 0, 0, width, height); 104 // 保存图片信息 105 let _lastImageData = _cropCanvas.toDataURL('image/png'); 106 // 将裁剪出来的信息展示 107 this.showviewimg.attr({"src": _lastImageData}).css({"width":width+"px","height":height+"px"}); 108 $('#pologen').css('display',"none"); 109 alert("裁剪成功"); 110 }); 111 $('#fileselector').on("change", this.ImgInputFileChanged); 112 $("#btn-ResizeUp").click(this.ResizeUp); 113 $("#btn-ResizeDown").click(this.ResizeDown); 114 //this.DrawClipRegionTosShow(); 115 this.workviewimg.mousemove((e) => { 116 117 if(this.isMouseDown) 118 { 119 if (this.workviewimg) 120 var left_s= e.offsetX- this.lastMouse_x; 121 var top_s=e.offsetY-this.lastMouse_y; 122 123 this.sourceimg_l+=left_s; 124 this.sourceimg_t+=top_s; 125 this.workviewimg.css({"left":this.sourceimg_l+"px","top":this.sourceimg_t+"px"}); 126 } 127 this.workpos_x = e.offsetX+this.sourceimg_l; 128 this.workpos_y = e.offsetY+this.sourceimg_t; 129 this.ShowWorkPos(e.offsetX,e.offsetY); 130 return false; 131 }); 132 //工作区滚轮事件,调整图片缩放,相当于是微调 133 this.workviewimg.on("mousewheel DOMMouseScroll",(e)=> { 134 this.isScrollMouseResize=true; 135 var delta = (e.originalEvent.wheelDelta && (e.originalEvent.wheelDelta > 0 ? 1 : -1)) || // chrome & ie 136 (e.originalEvent.detail && (e.originalEvent.detail > 0 ? -1 : 1)); // firefox 137 138 if(delta>0)//向上滚 139 { 140 if(this.enlargeratio>=1) 141 { 142 return; 143 } 144 this.workvwidth += 1; 145 this.workvheight +=this.sourceimg_h/this.sourceimg_w; 146 } 147 else if(delta<0) 148 { 149 this.workvwidth -= 1; 150 this.workvheight -=this.sourceimg_h/this.sourceimg_w; 151 } 152 this.workviewimg.css({"width":this.workvwidth+"px","height":this.workvheight+"px"}); 153 this.workviewimg.height(this.workvheight); 154 //this.DrawClipRegionTosShow(); 155 }); 156 157 //工作区图片拖拽 158 this.workviewimg.mousedown((e)=>{ 159 this.isMouseDown=true; 160 if(!this.isClip) 161 { 162 this.workviewimg.css({"cursor":"move"}); 163 } 164 this.lastMouse_x=e.offsetX; 165 this.lastMouse_y=e.offsetY; 166 }); 167 //鼠标未松开,移出工作区 168 this.workviewimg.mouseleave((e)=>{ 169 this.isMouseDown=false; 170 if(!this.isClip) 171 { 172 this.workviewimg.css({"cursor":"default"}); 173 } 174 }); 175 //防止鼠标松开的事件,遗漏,所以拖拽的终事件,放在body中监听 176 $('body').mouseup((e)=>{ 177 this.isMouseDown=false; 178 if(!this.isClip) 179 { 180 this.workviewimg.css({"cursor":"default"}); 181 } 182 }); 183 } 184 //在展示区域,标识出被截图的范围 185 DrawClipRegionTosShow() 186 { 187 //1.首先将工作区左上角的位置还原到原图的位置,因为有滚轮的放大和缩小,所以原本定义在放大或者缩小按钮上的缩放比例不能再使用,要重新计算 188 //不管是放大缩小按钮,还是滚轮缩放的比例在工作区都是一样的 189 //计算在右侧展示区,缩放后投影裁剪区域的位置 190 this.showToSouceHeightRatio=this.showvheight/this.workvheight; 191 this.showToSourceWidthRatio=this.showvwidth/this.workvwidth; 192 let showLeft=0; 193 let showTop=0; 194 let showWidth=0; 195 let showHeight=0; 196 if (this.showToSouceHeightRatio<this.showToSourceWidthRatio) 197 { 198 showWidth=this.workview.width()*this.showToSouceHeightRatio; 199 showHeight=this.workview.height()*this.showToSouceHeightRatio; 200 showLeft=-this.sourceimg_l*this.showToSouceHeightRatio; 201 showTop=-this.sourceimg_t*this.showToSouceHeightRatio; 202 } 203 else 204 { 205 showWidth=this.workview.width()*this.showToSourceWidthRatio; 206 showHeight=this.workview.height()*this.showToSourceWidthRatio; 207 showLeft=-this.sourceimg_l*this.showToSourceWidthRatio; 208 showTop=-this.sourceimg_t*this.showToSourceWidthRatio; 209 } 210 $('#pologen').css({"display":"block","top":showTop+"px","left":showLeft+"px","width":showWidth+"px","height":showHeight+"px"}); 211 212 } 213 ResizeDown(e) { 214 this.sourceimg_t=0; 215 this.sourceimg_l=0; 216 this.workviewimg.css({"left":this.sourceimg_l+"px","top":this.sourceimg_t+"px"}); 217 //鼠标滑轮调整过尺寸,则将图复原 218 if (this.isScrollMouseResize) { 219 this.enlargeratio = 1.0; 220 this.workvwidth=this.sourceimg_w; 221 this.workvheight=this.sourceimg_h; 222 this.workviewimg.css({"width":this.sourceimg_w+"px","height":this.sourceimg_h+"px"}); 223 } 224 this.isScrollMouseResize = false; 225 //不允许对完全在工作区展示的图片,再缩小 226 if (this.workvheight < this.showvheight && this.workvwidth < this.showvwidth) { 227 return; 228 } 229 else { 230 this.enlargeratio *= this.resizeRate; 231 this.workvwidth = this.sourceimg_w * this.enlargeratio; 232 this.workvheight = this.sourceimg_h * this.enlargeratio; 233 this.workviewimg.css({"width":this.workvwidth+"px","height":this.workvheight+"px"}); 234 this.DrawClipRegionTosShow(); 235 } 236 } 237 238 ResizeUp(e) { 239 this.sourceimg_t=0; 240 this.sourceimg_l=0; 241 this.workviewimg.css({"left":this.sourceimg_l+"px","top":this.sourceimg_t+"px"}); 242 //鼠标滑轮调整过尺寸,则将图复原 243 if (this.isScrollMouseResize) { 244 this.enlargeratio = 1.0; 245 this.workvwidth=this.sourceimg_w; 246 this.workvheight=this.sourceimg_h; 247 this.workviewimg.css({"width":this.sourceimg_w+"px","height":this.sourceimg_h+"px"}); 248 249 } 250 this.isScrollMouseResize = false; 251 //不允许,对原样展示的图片再进行放大 252 if (this.enlargeratio >= 1) { 253 return; 254 } 255 else { 256 this.enlargeratio /= this.resizeRate; 257 this.workvwidth = this.sourceimg_w * this.enlargeratio; 258 this.workvheight = this.sourceimg_h * this.enlargeratio; 259 this.workviewimg.css({"width":this.workvwidth+"px","height":this.workvheight+"px"}); 260 } 261 this.DrawClipRegionTosShow(); 262 } 263 ImgInputFileChanged(e) { 264 let file = e.target.files[0]; 265 let reader = new FileReader(); 266 reader.readAsDataURL(file); 267 //读取完成时 268 reader.onloadend = (e) => { 269 this.workviewimg.attr('src', e.target.result); 270 271 this.sourceimg_h = this.workviewimg.height(); 272 this.sourceimg_w = this.workviewimg.width(); 273 274 275 this.showToSouceHeightRatio=this.showvheight/this.sourceimg_h; 276 this.showToSourceWidthRatio=this.showvwidth/this.sourceimg_w; 277 278 //右侧展示区域,展示当前工作区处于图片的区域 279 //按照短边的比例对图片进行缩放 280 if (this.showToSouceHeightRatio<this.showToSourceWidthRatio) 281 { 282 this.showvimgheight=this.showvheight; 283 this.showvimgwidth=this.showToSouceHeightRatio*this.sourceimg_w; 284 } 285 else 286 { 287 this.showvimgheight=this.sourceimg_h*this.showToSourceWidthRatio; 288 this.showvimgwidth=this.showvwidth; 289 } 290 this.showviewimg.attr('src',this.workviewimg.attr('src')).css({"width":this.showvimgwidth+"px","height":this.showvimgheight+"px"}); 291 292 this.DrawClipRegionTosShow(); 293 } 294 295 296 } 297 } 298 299 $(function () { 300 let clip = new ClipTools(); 301 clip.Init(); 302 })
还有个文件保存的功能,由于裁剪之后,展示区域的图片路径是base64,所以可以直接发送给服务器,或者用于本地图直接展示
有问题,希望指正,本人刚接触前端不久,谢谢!