效果如圖,圖片可以跟隨鼠標移動,旋轉,拉伸,代碼在谷歌瀏覽器和IE11驗證了,其他瀏覽器沒試過
本博客源碼:
https://github.com/shengbid/vue-demo 把這個功能放在vue項目里了, 這個項目里會把平時博客寫的一些功能的代碼都放在里面,有需要可以下載看看,有幫助的話點個star哈

我主要講一下實現思路,
首先是移動,這個比較簡單
這里我用的是鼠標事件的pageX,pageY,因為這個獲取的是鼠標距文檔左上角的坐標,不受滾動條影響
畫一張圖來演示
如圖.綠色是鼠標移動位置,pageX,pageY是鼠標按下的位置,pageX1,pageY1是鼠標移動停止時的位置

pageX是由圖片的left + 鼠標到圖片左邊的距離組成,也許還有其他的padding,margin之類的,但是不影響,統統都算成c
pageX = left + c
c = pageX - left
要求第二次的left值,只需要用pageX1 - c就可以
left1 = pageX1 - c
再把計算出的left1賦值給定位元素,top是同樣的計算
因為我的圖片是包在一個div元素里,定位給就這個div賦值
代碼實現
// 元素移動 var moveMouse = false; $('.img').mousedown(function (e) { e.preventDefault() e.stopPropagation() moveMouse = true var dis = { X: e.pageX - $('.box').position().left, Y: e.pageY - $('.box').position().top } $(document).on('mousemove', function (event) { event.preventDefault() event.stopPropagation() if (moveMouse) { var end = {} end.X = event.pageX - dis.X end.Y = event.pageY - dis.Y $('.box').css({ 'left': end.X, 'top': end.Y }) } }) }) $(document).on('mouseup', function (e) { moveMouse = false });
這樣就實現移動功能了
再來實現元素的旋轉拉伸功能
拉伸可以通過改變元素的width和height實現,也可以通過transform的scale實現
這里涉及一個問題,旋轉的時候以圖片左上角為旋轉點還是以圖片中心點為旋轉點,transform的rotate默認是以元素的中心點為旋轉點.而width和height的改變是以元素左上角為基點,這里我們要做的功能是邊旋轉邊改變圖片大小,那么統一都以圖片中心點為基點.這樣的話用width和height來實現放大縮小功能就不方便了,所以還是使用scale來實現
先看旋轉的實現,以這張圖說明,首先需要確定中心點,還有第一次鼠標按下的點pointB,以后每次旋轉都是求BA,CA的夾角,也就是cosA

代碼實現
var pointA = { // 元素中心點 元素1/2自身寬高 + 元素的定位 X: $('.box').width() / 2 + $('.box').offset().left, Y: $('.box').height() / 2 + $('.box').offset().top }; console.log(pointA) var pointB = {}; var pointC = {}; // A,B,C分別代表中心點,起始點,結束點坐標 // 這里通過鼠標的移動獲取起始點和結束點 var typeMouse = false; var allA = 0; // 存放鼠標旋轉總共的度數 var count = 0; // 元素跟隨鼠標移動旋轉 $(".rotate").on('mousedown', function (e) { e.preventDefault() e.stopPropagation() typeMouse = true; //獲取起始點坐標 if (count < 1) { // 以鼠標第一次落下的點為起點 pointB.X = e.pageX; pointB.Y = e.pageY; count++ } console.log(5, pointA, pointB) $(document).on('mousemove', function (e) { e.preventDefault() if (typeMouse) { pointC.X = e.pageX; pointC.Y = e.pageY; // 獲取結束點坐標 // 計算出旋轉角度 var AB = {}; var AC = {}; AB.X = (pointB.X - pointA.X); AB.Y = (pointB.Y - pointA.Y); AC.X = (pointC.X - pointA.X); AC.Y = (pointC.Y - pointA.Y); // 分別求出AB,AC的向量坐標表示 var direct = (AB.X * AC.Y) - (AB.Y * AC.X); // AB與AC叉乘求出逆時針還是順時針旋轉 var lengthAB = Math.sqrt(Math.pow(pointA.X - pointB.X, 2) + Math.pow(pointA.Y - pointB.Y, 2)), lengthAC = Math.sqrt(Math.pow(pointA.X - pointC.X, 2) + Math.pow(pointA.Y - pointC.Y, 2)), lengthBC = Math.sqrt(Math.pow(pointB.X - pointC.X, 2) + Math.pow(pointB.Y - pointC.Y, 2)); var cosA = (Math.pow(lengthAB, 2) + Math.pow(lengthAC, 2) - Math.pow(lengthBC, 2)) / (2 * lengthAB * lengthAC); // 余弦定理求出旋轉角 var angleA = Math.round(Math.acos(cosA) * 180 / Math.PI); if (direct < 0) { allA = -angleA; //叉乘結果為負表示逆時針旋轉, 逆時針旋轉減度數 } else { allA = angleA; //叉乘結果為正表示順時針旋轉,順時針旋轉加度數 } // console.log(allA) $('.img-box').css('transform', 'rotate('+allA+'deg)') } }); }); $(document).on('mouseup', function (e) { typeMouse = false; });
拉伸實現,如圖,其實就是AC/AB的比例,AB和AC的長度可以百度搜索已知兩點坐標求直線距離查看計算公式,這里就不展開了

代碼實現
// 元素跟隨鼠標移動拉伸 $(".rotate, .rotate1").on('mousedown', function (e) { e.preventDefault() e.stopPropagation() typeMouse = true; //獲取起始點坐標 if (count < 1) { // 以鼠標第一次落下的點為起點 pointB.X = e.pageX; pointB.Y = e.pageY; count++ } console.log(5, pointA, pointB) $(document).on('mousemove', function (e) { e.preventDefault() if (typeMouse) { pointC.X = e.pageX; pointC.Y = e.pageY; // 獲取結束點坐標 // 計算每次移動元素的半徑變化,用作拉伸 var scalX1 = pointB.X - pointA.X var scalY1 = pointB.Y - pointA.Y var scalX = pointC.X - pointA.X var scalY = pointC.Y - pointA.Y // 計算出拉伸比例 var sa = Math.sqrt(scalX1 * scalX1 + scalY1 * scalY1) var ss = Math.sqrt(scalX * scalX + scalY * scalY) var sc = ss / sa // console.log(sc) $('.img-box').css('transform', 'scale('+sc+')') } }); }); $(document).on('mouseup', function (e) { typeMouse = false; });
html代碼
<div class="container"> <div class="box"> <div class="img-box"> <div class="flat">翻轉</div> <div class="rotate">旋轉</div> <img src="https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1141259048,554497535&fm=26&gp=0.jpg" alt="" class="img"> </div> </div> </div>
HTML元素的關系,圖片需要包三層父級div. img-box包圖片,旋轉的時候旋轉img-box.因為旋轉之后left和top值會改變,
所以需要在包一層box,移動的時候改變left和top就改變box的值.
最外層的container主要是一個定位,里面的元素都是絕對定位,需要一個相對定位的父元素
然后,這三個功能都實現了,把他們組合在一起,需要注意一點問題
移動的時候,圖片的中心點也改變了,所以每次移動后需要重新計算圖片的中心點,對應的pointB點也需要對應的改變
最后,如果你需要四個角都可以旋轉,需要計算出不同旋轉角之間的角度差
如圖,如果第一次以旋轉區域1為起點,點擊旋轉區域2時就需要加上旋轉區域1與旋轉區域2直角的夾角,也就是2倍tanA
如果第二次旋轉選的是旋轉區域3,那么就需要加上180°.這是順時針方向,如果反過來逆時針方向就是減去度數差

完整代碼
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <style> html, body { margin: 0; font-size: 14px; } .container { /* padding: 20px; border: 1px solid sienna; */ position: relative; } .box { position: absolute; left: 200px; top: 100px; width: 400px; height: 400px; color: #fff; /* background-color: rosybrown; */ } .img-box { position: absolute; /* left: 30px; top: 30px; */ width: 400px; height: 400px; background-color: sandybrown; } .flat { position: absolute; right: -20px; top: -20px; width: 40px; height: 40px; background-color: seagreen; z-index: 3; line-height: 40px; text-align: center; cursor: default; } .rotate, .rotate1 { position: absolute; right: -20px; bottom: -20px; width: 40px; height: 40px; background-color: royalblue; z-index: 3; cursor: se-resize; line-height: 40px; text-align: center; } .rotate1 { left: -20px; right: auto; } .img { width: 100%; height: 100%; cursor: move; } .header { height: 50px; } </style> <body> <div class="header"> <h1>圖形編輯</h1> </div> <div class="container"> <div class="box"> <div class="img-box"> <div class="flat">翻轉</div> <div class="rotate">旋轉</div> <div class="rotate1">旋轉</div> <img src="https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1141259048,554497535&fm=26&gp=0.jpg" alt="" class="img"> </div> </div> </div> </body> <!-- <script src="http://code.jquery.com/jquery-2.1.4.min.js"></script> --> <script src="./jquery-3.1.1.min.js"></script> <script src="./common-validate/index.js"></script> <script> console.log(commonValidate) var flat = -1 // 鏡像翻轉 $(".flat").click(function() { $(".img").css("transform", "scaleX("+flat+")") flat = -flat }) var pointA = { // 元素中心點 元素1/2自身寬高 + 元素的定位 X: $('.box').width() / 2 + $('.box').offset().left, Y: $('.box').height() / 2 + $('.box').offset().top }; console.log(pointA, $('.box').position()) var pointB = {}; var pointC = {}; // A,B,C分別代表中心點,起始點,結束點坐標 // 這里通過鼠標的移動獲取起始點和結束點 var typeMouse = false; var moveMouse = false; var allA = 0; // 存放鼠標旋轉總共的度數 var count = 0; var mPointB = {} // 移動的B點距離 var init = { count: 0 } var oldTarget = { target: null, angle: 0 } // 元素跟隨鼠標移動旋轉拉伸 $(".rotate, .rotate1").on('mousedown', function (e) { e.preventDefault() e.stopPropagation() // 計算兩個旋轉方塊之間的角度 var tanA = $('.box').width() / $('.box').height() var d = Math.round(Math.atan(tanA) * 180 / Math.PI) if (oldTarget.target && oldTarget.target != e.currentTarget) { if (e.currentTarget == $('.rotate')[0]) { oldTarget.angle = 2 * d } else { oldTarget.angle = -2 * d } } else { oldTarget.angle = 0 } typeMouse = true; //獲取起始點坐標 if (count < 1) { // 以鼠標第一次落下的點為起點 pointB.X = e.pageX; pointB.Y = e.pageY; init.count = 0 oldTarget.target = e.currentTarget count++ } if (mPointB.flag) { // 如果移動后,元素的B點也需要加上平移的距離 pointB.X += mPointB.X pointB.Y += mPointB.Y mPointB.flag = false init.count = 0 } console.log('pointA', pointA, 'pointB', pointB) $(document).on('mousemove', function (e) { e.preventDefault() if (typeMouse) { pointC.X = e.pageX; pointC.Y = e.pageY; // 獲取結束點坐標 // 計算每次移動元素的半徑變化,用作拉伸 var scalX1 = pointB.X - pointA.X var scalY1 = pointB.Y - pointA.Y var scalX = pointC.X - pointA.X var scalY = pointC.Y - pointA.Y // 計算出旋轉角度 var AB = {}; var AC = {}; AB.X = (pointB.X - pointA.X); AB.Y = (pointB.Y - pointA.Y); AC.X = (pointC.X - pointA.X); AC.Y = (pointC.Y - pointA.Y); // 分別求出AB,AC的向量坐標表示 var direct = (AB.X * AC.Y) - (AB.Y * AC.X); // AB與AC叉乘求出逆時針還是順時針旋轉 var lengthAB = Math.sqrt(Math.pow(pointA.X - pointB.X, 2) + Math.pow(pointA.Y - pointB.Y, 2)), lengthAC = Math.sqrt(Math.pow(pointA.X - pointC.X, 2) + Math.pow(pointA.Y - pointC.Y, 2)), lengthBC = Math.sqrt(Math.pow(pointB.X - pointC.X, 2) + Math.pow(pointB.Y - pointC.Y, 2)); var cosA = (Math.pow(lengthAB, 2) + Math.pow(lengthAC, 2) - Math.pow(lengthBC, 2)) / (2 * lengthAB * lengthAC); // 余弦定理求出旋轉角 var angleA = Math.round(Math.acos(cosA) * 180 / Math.PI); if (direct < 0) { allA = -angleA; //叉乘結果為負表示逆時針旋轉, 逆時針旋轉減度數 } else { allA = angleA; //叉乘結果為正表示順時針旋轉,順時針旋轉加度數 } allA += oldTarget.angle // $('.img-box').css('transform', 'rotate('+allA+'deg)') // 計算出拉伸比例 var sa = Math.sqrt(scalX1 * scalX1 + scalY1 * scalY1) var ss = Math.sqrt(scalX * scalX + scalY * scalY) var sc = ss / sa // console.log(sa, ss, sc) $('.img-box').css('transform', 'rotate('+allA+'deg) scale('+sc+')') // $('.img-box').css('transform', 'scale('+sc+')') } }); }); // 元素移動 $('.img').mousedown(function (e) { e.preventDefault() e.stopPropagation() moveMouse = true if (init.count < 1) { init = { // X: e.pageX, // Y: e.pageY, X: pointA.X, Y: pointA.Y, count: 1 } } var dis = { X: e.pageX - $('.box').position().left, Y: e.pageY - $('.box').position().top } $(document).on('mousemove', function (event) { event.preventDefault() event.stopPropagation() if (moveMouse) { var end = {} end.X = event.pageX - dis.X end.Y = event.pageY - dis.Y $('.box').css({ 'left': end.X, 'top': end.Y }) // console.log($('.box').offset(), $('.box').position(), end, dis) pointA = { // 移動后,重新計算元素中心點 元素1/2自身寬高 + 元素的定位 X: $('.box').width() / 2 + $('.box').offset().left, Y: $('.box').height() / 2 + $('.box').offset().top }; if (count > 0) { // 每次移動按下的點不一致,有誤差,使用中心點來計算 // mPointB.X = event.pageX - init.X // mPointB.Y = event.pageY - init.Y mPointB.X = pointA.X - init.X mPointB.Y = pointA.Y - init.Y mPointB.flag = true } // console.log(pointA, mPointB) } }) }) $(document).on('mouseup', function (e) { typeMouse = false; moveMouse = false }); </script> </html>
補充內容:
感謝博友提出問題,在平移時,由於每次鼠標按下的位置不一樣,我是用最后一次鼠標移動的位置減去第一次鼠標按下的位置, 如果多次移動,伸縮計算就會有誤差
而元素的中心點是一直保持在元素中心,每次移動后都會改變,所以改成使用最后一次移動的元素中心點減去第一次移動時的中心點,能消除誤差
代碼里紅色的是修改后的
這個方法中pointB的計算有些麻煩,特別是四個角都以旋轉時. 之后又試了一種方法,比較簡單一些.第二種實現方法可以看這篇https://www.cnblogs.com/steamed-twisted-roll/p/14292478.html