canvas實現地圖放大縮小拖拽

效果圖

如圖,代碼在最下面
在這裏插入圖片描述

前言

實現目標:使用canvas畫出地圖,且可以縮放,拖拽
個人理解:其實canvas動畫就是一次次的擦除重繪,就像是小時候看的動畫片,每一幀播的很快,因爲視覺停留,看起來像是連續動畫而已。

canvas 基礎

1.drawImage
drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)  //下面的變量名以此爲準

後面8個參數比較重要了:前 4 個是定義圖像源的切片位置和大小,後 4 個則是定義切片的目標顯示位置和大小。
貼個菜鳥鏈接 大概回顧下canvas drawImage
或者直接看下圖在這裏插入圖片描述在這裏插入圖片描述

拖拽功能

三個事件:onmousedown,onmousemove,onmouseup
大概講下思路,後面上代碼

  • onmousedown 記錄鼠標按下的地址,且移動flag=true
  • onmousemove 當移動flag爲true時,計算出drawImage需要的參數,並重繪
  • onmouseup 將移動flag置爲false

邊界條件

當拖拽的時候不能無限的拖動,否則會超出畫布,所以需要限制一下 dx, dy, 兩個參數

放大與縮小

監聽滾輪事件:onmousewheel

  • 縮放時以鼠標所在的點爲中心進行縮放,所以需要計算具體的drawImage的8個參數
  • 每次選取圖像時就從(0,0)點選取全圖,sWidth, sHeight 即爲畫布大小不需要改變
  • 假設需要變大N倍,那麼dWidth=NsWidth, dHeight=NsHeight
  • 然後放置切片位置,dx,dy應始終爲負數,將圖像顯示到畫布中

在這裏插入圖片描述
如圖:紅色框是畫布, 畫布是不動的,我們根據這個看圖片怎麼放大。

  • 圖1:原圖,
  • 圖2:以原點放大N倍,
  • 圖3:將放大的圖向左上平移,最終使放大的圖片在畫布中合適位置。
  • 圖4:最終效果
    其中最重要的就是計算左上平移,也就是dx,dy的數值。

定義變量:

  • 放大倍數:imgScale
  • 當前鼠標所在位置也就是縮放中心 pos:{x,y}
  • 當前畫布左上移動的位置,即dx,dy的數值:imgX, imgY(默認爲0)
  • 縮放後新的左上移動的位置,imgXN,imgYN

以下是公式:

 imgXN = (1-imgScale)*(pos.x-imgX)/imgScale+(pos.x-(pos.x-imgX)/imgScale);
 imgYN = (1-imgScale)*(pos.y-imgY)/imgScale+(pos.y-(pos.y-imgY)/imgScale);

上面公式沒有簡化🙃,其實如下

 var newPos = {x:((pos.x-imgX)/imgScale).toFixed(2) , y:((pos.y-imgY)/imgScale).toFixed(2)};
 imgXN = (1-imgScale)*newPos.x+(pos.x-newPos.x);
 imgYN = (1-imgScale)*newPos.y+(pos.y-newPos.y);

以上公式不詳解了,大意就是:先把鼠標位置相對原圖的位置計算出來,然後以該點爲中心重新進行縮放(不知道有沒有更好的思路呢😊)

代碼

以下源碼:

<canvas id="scaleDragCanvas" width="878" height="547" style="border: thin solid #aaaaaa;"></canvas>
var canvas, context;
var img, imgX = 0, imgY = 0, imgScale = 1;
var MINIMUM_SCALE = 1.0 ,pos={},posl={},dragging = false;


(function int() {
    canvas = document.getElementById('scaleDragCanvas'); //畫布對象
    context = canvas.getContext('2d');//畫布顯示二維圖片
    loadImg();
    canvasEventsInit();

})();

function loadImg() {
    img = new Image();
    img.onload = function () {
        drawImage();
    }
    img.src = 'https://static.zhihu.com/liukanshan/images/comics/bg-89c9bdc3.jpg';//劉看山
}

function drawImage() {
    context.clearRect(0, 0, canvas.width, canvas.height);
    // 保證  imgX  在  [img.width*(1-imgScale),0]   區間內
    if(imgX<img.width*(1-imgScale)) {
        imgX = img.width*(1-imgScale);
    }else if(imgX>0) {
        imgX=0
    }
    // 保證  imgY   在  [img.height*(1-imgScale),0]   區間內
    if(imgY<img.height*(1-imgScale)) {
        imgY = img.height*(1-imgScale);
    }else if(imgY>0) {
        imgY=0
    }


    context.drawImage(
        img, //規定要使用的圖像、畫布或視頻。
        0, 0, //開始剪切的 x 座標位置。
        img.width, img.height,  //被剪切圖像的高度。
        imgX, imgY,//在畫布上放置圖像的 x 、y座標位置。
        img.width * imgScale, img.height * imgScale  //要使用的圖像的寬度、高度
    );
}

/*事件註冊*/
function canvasEventsInit() {
    canvas.onmousedown = function (event) {
        dragging = true;
         pos = windowToCanvas(event.clientX, event.clientY);  //座標轉換,將窗口座標轉換成canvas的座標

    };
    canvas.onmousemove = function (evt) {  //移動
        if(dragging){
        posl = windowToCanvas(evt.clientX, evt.clientY);
        var x = posl.x - pos.x, y = posl.y - pos.y;
        imgX  += x;
        imgY  += y;
            pos = JSON.parse(JSON.stringify(posl));
        drawImage();  //重新繪製圖片
    }

    };
    canvas.onmouseup = function () {
        dragging = false;
    };
    canvas.onmousewheel = canvas.onwheel = function (event) {    //滾輪放大縮小
        var pos = windowToCanvas (event.clientX, event.clientY);
        event.wheelDelta = event.wheelDelta ? event.wheelDelta : (event.deltalY * (-40));  //獲取當前鼠標的滾動情況
        var newPos = {x:((pos.x-imgX)/imgScale).toFixed(2) , y:((pos.y-imgY)/imgScale).toFixed(2)};
        if (event.wheelDelta > 0) {// 放大
                imgScale +=0.1;
                imgX = (1-imgScale)*newPos.x+(pos.x-newPos.x);
                imgY = (1-imgScale)*newPos.y+(pos.y-newPos.y);
        } else {//  縮小
            imgScale -=0.1;
            if(imgScale<MINIMUM_SCALE) {//最小縮放1
                imgScale = MINIMUM_SCALE;
            }
            imgX = (1-imgScale)*newPos.x+(pos.x-newPos.x);
            imgY = (1-imgScale)*newPos.y+(pos.y-newPos.y);
            console.log(imgX,imgY);
            }
        drawImage();   //重新繪製圖片

    };
}


/*座標轉換*/
function windowToCanvas(x,y) {
    var box = canvas.getBoundingClientRect();  //這個方法返回一個矩形對象,包含四個屬性:left、top、right和bottom。分別表示元素各邊與頁面上邊和左邊的距離
    return {
        x: x - box.left - (box.width - canvas.width) / 2,
        y: y - box.top - (box.height - canvas.height) / 2
    };
}


立個flag,有空封裝一下

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章