在生活中我们时常能看到这种图片裁剪的效果, 那么他实际上底层的原理是怎么实现的呢? 让我们深入的解剖一下.
原理分析 首先我们先将视图一分为三, 理解为三个层级叠加在一起的仰视图.
最上面是可拖动的选择窗口 中间待剪辑的可视窗口 底层是一张opacity: .5
的背景图片 基础结构 我们这里主要讲JavaScript, HTML与css就简要的过一下.
首先HTML
基本结构是两张相同的结构, 两张图片分别是调整过透明度的底图和一张被裁剪过了的中间层.mainBox
包裹着选择的小方块square
,相对定位于image在最上面一层.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <body > <div id ="box" > <img src ="images/Konachan.com - 239917 sample.jpg" alt ="img" id ="image1" > <img src ="images/Konachan.com - 239917 sample.jpg" alt ="img" id ="image2" > <div id ="mainBox" class ="main" > <div class ="square left-up" > </div > <div class ="square up" > </div > <div class ="square right-up" > </div > <div class ="square right" > </div > <div class ="square right-down" > </div > <div class ="square down" > </div > <div class ="square left-down" > </div > <div class ="square left" > </div > </div > </div > <script src ="js/main.js" > </script > </body >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 body {background : #333 ;}#box {position : absolute;top : 100px ; left : 200px ; width : 460px ; height : 360px ;}#box img {width : 460px ;}#box #image1 {opacity : .5 ;position : absolute; top : 0 ;left : 0 ;}#box #image2 {position : absolute; top : 0 ;left : 0 ; clip : rect (0 , 200px , 200px , 0 ) }#box .main {position : absolute;border : 1px solid #fff ; width : 200px ; height : 200px ;box-sizing : border-box;}#box .main .square {position : absolute; width : 8px ;height : 8px ; background : #fff }#box .main .left-up {left : -4px ;top : -4px ;cursor : nw-resize;}#box .main .up {left : 50% ;top : -4px ;margin-left : -4px ;cursor : n-resize}#box .main .right-up {right : -4px ;top : -4px ;cursor : ne-resize}#box .main .right {right : -4px ;top : 50% ;margin-top : -4px ;cursor : e-resize}#box .main .right-down {right : -4px ;bottom : -4px ;cursor : se-resize}#box .main .down {left : 50% ;bottom : -4px ;margin-left : -4px ;cursor : s-resize}#box .main .left-down {left : -4px ;bottom : -4px ;cursor : sw-resize}#box .main .left {top : 50% ;left : -4px ;margin-top : -4px ;cursor : w-resize}#box { -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; }
JavaScript实现 ESMAScript 并没有提供可拖动的API, 那我们先来思考一下, 该如何实现拖动的功能~ 最上层有9个小方块(Square) , 分别代表着不同方向的边界, 拖动这个边界, 无非需要实现这下面的底层步骤.
鼠标落下(MouseDown) => 鼠标拖动 => 松开鼠标(MouseUp) .
先创建一个clipImage
函数作为入口函数, 主要获取目标元素和生成DOM节点(后面讲), 绑定事件 . 先给小方块绑定一个鼠标落下事件(mousedown) , 当触发事件(MouseEvent)时, 监听器调用onMousedown函数
.onMousedown
函数中, 接受四个参数e
事件, box
目标元素, ctrl
小方块的方向, type
属性, 主要是用来记录数据并暴露给全局变量进行通讯.
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 ;(function ( ) { var util = { $: function (dom ) { return document .querySelector(dom); } }; var _MainBox, _MainCtrl, _MainType; var moving = 0 ; clipImage('mainBox' ); function clipImage (id ) { var boxMain = document .getElementById(id); var right = util.$('#box .main .right' ); up.addEventListener('mousedown' , function (e ) { onMousedown(e, box, up, 'up' ); }); function onMousedown (e, box, ctrl, type ) { var e = e || window .event; _MainBox = box; _MainCtrl = ctrl; _MainType = type; } })();
紧接着来计算拖动的距离, 在onMouseDown
函数上将moving
拖动标记设为1
(true也行).
判断拖动的标记是否启动, 创建getPosition
函数获取元素相对于页面左/上边的偏移量用于计算拖动的偏移量. 如下图.
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 var moving = 0 ;function onMousedown (e, box, ctrl, type ) { var e = e || window .event; _MainBox = box; _MainCtrl = ctrl; _MainType = type; moving = 1 ; } function getPosition (node ) { var left = node.offsetLeft; var top = node.offsetTop; var parent = node.offsetParent; while (parent) { left += parent.offsetLeft; top += parent.offsetTop; parent = parent.offsetParent; } return { "left" : left, "top" : top}; } document .onmousemove = function (e ) { if (moving) { var e = e || window .event; var addWidth, addHeight; var width = _MainBox.offsetWidth; var boxX = getPosition(_MainBox).left; switch (_MainType) { case "right" : addWidth = e.clientX - boxX - width; _MainBox.style.width = width + addWidth + 'px' ; break ; } } };
紧接着我们会发现虽然实现了拖动的效果, 但是松开鼠标box宽度
还是会随着鼠标变化. 这是因为还没有重置标记. 随即监听鼠标松开事件(MouseUp)
. 单边拖动就完成啦~
1 2 3 4 5 document .onmouseup = function ( ) { moving = 0 ; };
整理归纳 我们将switch
里的代码整理出来. 装进函数里去调用. 相续的将各个方向也加上, 原理也是同理. 值得注意的是将右面和下面 要加上box
的宽高才能计算出来. 剩下四个边角, 如左上什么的其实就是同时调用正方位的两个函数实现的实现起来. 然后拖动功能就大功告成啦~
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 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 ;(function ( ) { var util = { $: function (dom ) { return document .querySelector(dom); } }; var _MainBox, _MainCtrl, _MainType; var moving = 0 ; clipImage('mainBox' ); function clipImage (id ) { var boxMain = document .getElementById(id); var up = util.$('#box .main .up' ); var down = util.$('#box .main .down' ); var right = util.$('#box .main .right' ); var rightUp = util.$('#box .main .right-up' ); var rightDown = util.$('#box .main .right-down' ); var left = util.$('#box .main .left' ); var leftUp = util.$('#box .main .left-up' ); var leftDown = util.$('#box .main .left-down' ); right.addEventListener('mousedown' , function (e ) { onMousedown(e, boxMain, right, 'right' ); }); up.addEventListener('mousedown' , function (e ) { onMousedown(e, boxMain, up, 'up' ); }); down.addEventListener('mousedown' , function (e ) { onMousedown(e, boxMain, down, 'down' ); }); left.addEventListener('mousedown' , function (e ) { onMousedown(e, boxMain, left, 'left' ); }); leftUp.addEventListener('mousedown' , function (e ) { onMousedown(e, boxMain, leftUp, 'leftUp' ); }); leftDown.addEventListener('mousedown' , function (e ) { onMousedown(e, boxMain, leftDown, 'leftDown' ); }); rightUp.addEventListener('mousedown' , function (e ) { onMousedown(e, boxMain, rightUp, 'rightUp' ); }); rightDown.addEventListener('mousedown' , function (e ) { onMousedown(e, boxMain, rightDown, 'rightDown' ); }); } function onMousedown (e, box, ctrl, type ) { _MainBox = box; _MainCtrl = ctrl; _MainType = type; moving = 1 ; } function getPosition (node ) { var left = node.offsetLeft; var top = node.offsetTop; var parent = node.offsetParent; while (parent) { left += parent.offsetLeft; top += parent.offsetTop; parent = parent.offsetParent; } return { "left" : left, "top" : top}; } document .onmousemove = function (e ) { if (moving) { var e = e || window .event; var height = _MainBox.offsetHeight; var boxY = getPosition(_MainBox).top; switch (_MainType) { case "right" : right(e); break ; case "up" : up(e); break ; case "down" : down(e); break ; case "left" : left(e); break ; case "leftUp" : leftUp(e); break ; case "leftDown" : leftDown(e); break ; case "rightUp" : rightUp(e); break ; case "rightDown" : rightDown(e); break ; } } }; document .onmouseup = function ( ) { moving = 0 ; }; function right (e ) { var width = _MainBox.offsetWidth; var boxX = getPosition(_MainBox).left; var addWidth = e.clientX - boxX - width; _MainBox.style.width = width + addWidth + 'px' ; } function up (e ) { var height = _MainBox.offsetHeight; var boxY = getPosition(_MainBox).top; var addHeight = boxY - e.clientY; _MainBox.style.height = height + addHeight + 'px' ; _MainBox.style.top = _MainBox.offsetTop - addHeight + "px" ; } function down (e ) { var height = _MainBox.offsetHeight; var boxY = getPosition(_MainBox).top; var addHeight = e.clientY - boxY - height; _MainBox.style.height = height + addHeight + 'px' ; } function left (e ) { var width = _MainBox.offsetWidth; var boxX = getPosition(_MainBox).left; var addWidth = boxX - e.clientX; _MainBox.style.width = width + addWidth + 'px' ; _MainBox.style.left = _MainBox.offsetLeft - addWidth + 'px' ; } function leftUp (e ) { var width = _MainBox.offsetWidth; var height = _MainBox.offsetHeight; var boxX = getPosition(_MainBox).left; var boxY = getPosition(_MainBox).top; var addWidth = boxX - e.clientX; var addHeight = boxY - e.clientY; _MainBox.style.width = width + addWidth + 'px' ; _MainBox.style.height = height + addHeight + 'px' ; _MainBox.style.top = _MainBox.offsetTop - addHeight + "px" ; _MainBox.style.left = _MainBox.offsetLeft - addWidth + 'px' ; } function leftDown (e ) { var width = _MainBox.offsetWidth; var height = _MainBox.offsetHeight; var boxX = getPosition(_MainBox).left; var boxY = getPosition(_MainBox).top; var addWidth = boxX - e.clientX; var addHeight = e.clientY - boxY - height; _MainBox.style.height = height + addHeight + 'px' ; _MainBox.style.width = width + addWidth + 'px' ; _MainBox.style.left = _MainBox.offsetLeft - addWidth + 'px' ; } function rightUp (e ) { var width = _MainBox.offsetWidth; var height = _MainBox.offsetHeight; var boxX = getPosition(_MainBox).left; var boxY = getPosition(_MainBox).top; var addWidth = e.clientX - boxX - width; var addHeight = boxY - e.clientY; _MainBox.style.height = height + addHeight + "px" ; _MainBox.style.top = _MainBox.offsetTop - addHeight + "px" ; _MainBox.style.width = width + addWidth + "px" ; } function rightDown (e ) { var width = _MainBox.offsetWidth; var height = _MainBox.offsetHeight; var boxX = getPosition(_MainBox).left; var boxY = getPosition(_MainBox).top; var addWidth = e.clientX - boxX - width; var addHeight = e.clientY - boxY - height; _MainBox.style.height = height + addHeight + "px" ; _MainBox.style.width = width + addWidth + "px" ; } })();
** <– 努力填坑中~ –> **