从画布中的不同图像点缩放/缩放

时间:2017-04-15 03:49:56

标签: javascript html5 canvas zoom scale

我在html5中制作了一个画布。我用它来显示你可以移动的更大图像的部分,我想让它可以放大和缩小。但我不知道如何从一定程度上使图像缩放。当我增加图像的大小时,画布显示的部分被移动,我希望画布中心的部分在缩放完成时成为焦点,但无论我如何尝试它都会被扭曲一些怎么样。似乎取决于图像的哪个部分被显示,当缩放发生时,图像的一部分将被移动到不同的坐标。我不知道我将使用什么类型的算法,因此计算图像的移动。

这是我的意思是https://imgur.com/a/aZiVM移动的视觉示例的链接,两个图像的缩放程度相同,但取决于画布中可见的图像部分,图像需要的数量移动缩放不同。

这是我的代码,但它并没有真正起作用。

<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.js"></script>
    <style>
        body {
            padding: 0px;
            margin: 0px;
        }
    </style>
</head>
<body>
    <div id="map" style="position:absolute;top:10px;left:50px;">
        <canvas id="canvas" width="800" height="600" style="float:left;border:1px solid #000000;">Your browser doesn't support canvas</canvas>
        <div id="floorDown" onMouseDown="zoomIn()" style="width:200px;height:50px;float:left;">Zoom in</div><br>
        <div id="floorDown" onMouseDown="zoomOut()" style="width:200px;height:50px;float:left;">Zoom out</div>
    </div>

    <script>
    var canvas = document.getElementById('canvas');
    var ctx = canvas.getContext('2d');

    var canvasOffset=$("#canvas").offset();
    var offsetX=canvasOffset.left;
    var offsetY=canvasOffset.top;

    var startX;
    var startY;
    var isDown=false;

    //start position
    var imageX=0;
    var imageY=0;
    var imageWidth,imageHeight,imageRight,imageBottom;
    var draggingImage=false;
    var startX;
    var startY;

    var img=new Image();
    img.onload=function(){
        imageWidth=img.width;
        imageHeight=img.height;
        draw();
    }

    img.src='http://orig00.deviantart.net/35cb/f/2013/030/f/0/tripolar_by_zy0rg-d5t9tqh.png';

    function draw(){
        // clear the canvas
        ctx.clearRect(0,0,canvas.width,canvas.height);
        //Disable anti-aliasing
        ctx.imageSmoothingEnabled=false;
        // draw the image
        ctx.drawImage(img,0,0,img.width,img.height,imageX,imageY,imageWidth,imageHeight);
    }

    function handleMouseDown(e){
        startX=parseInt(e.clientX-offsetX);
        startY=parseInt(e.clientY-offsetY);
        draggingImage= true;
    }

    function handleMouseUp(e){
        draggingImage=false;
        draw();
    }

    function handleMouseOut(e){
        handleMouseUp(e);
    }

    function handleMouseMove(e){
        if(draggingImage){
            imageClick=false;
            mouseX=parseInt(e.clientX-offsetX);
            mouseY=parseInt(e.clientY-offsetY);
            // move the image by the amount of the latest drag
            var dx=mouseX-startX;
            var dy=mouseY-startY;
            imageX+=dx;
            imageY+=dy;
            // reset the startXY for next time
            startX=mouseX;
            startY=mouseY;
            // redraw the image with border
            draw();
        }
    }


    // TEST zoom in/out functions
    function zoomIn() {
        imageX=imageX*2;
        imageY=imageY*2;
        imageWidth=imageWidth*2;
        imageHeight=imageHeight*2;
        draw();
    }
    function zoomOut() {
        imageX=imageX/2;
        imageY=imageY/2;
        imageWidth=imageWidth/2;
        imageHeight=imageHeight/2;
        draw();
    }

    $("#canvas").mousedown(function(e){handleMouseDown(e);});
    $("#canvas").mousemove(function(e){handleMouseMove(e);});
    $("#canvas").mouseup(function(e){handleMouseUp(e);});
    $("#canvas").mouseout(function(e){handleMouseOut(e);});
    </script>
</body>

1 个答案:

答案 0 :(得分:3)

给定原点(pos)和缩放比例

var pos = {x : 0, y : 0};
var scale = 1;
function zoomAt(x,y,_scale)
    scale *= _scale
    pos.x = x - (x - pos.x) * scale;
    pos.y = y - (y - pos.y) * scale;
}

然后,您可以使用

创建转换
ctx.setTransform(scale, 0, 0, scale, pos.x, pos.y);

所以放大屏幕中心

zoomAt(canvas.width / 2, canvas.height / 2, 1.1);  // zoom in
zoomAt(canvas.width / 2, canvas.height / 2, 1 / 1.1);  // zoom out

全部放在一起

// the following globals are available
// w, h, cw, ch,  width height centerWidth centerHeight of canvas
// canvas, ctx, mouse, globalTime  
const image = new Image;
image.src = "https://upload.wikimedia.org/wikipedia/commons/thumb/6/6d/KTZ_2TE10U_Aynabulak.jpg/800px-KTZ_2TE10U_Aynabulak.jpg";

const font = {
    font : "28px Arial",
    textAlign : "center",
    textBaseline : "middle",
}

function setStyle(ctx, style){
    Object.keys(style).forEach(key => ctx[key] = style[key]);
}
// Handle all key input
const keys = {  // key input object
    ArrowLeft : false,  // only add key names you want to listen to
    ArrowRight : false,
    keyEvent (event) {
        if (keys[event.code] !== undefined) {  // are we interested in this key
            keys[event.code] = event.type === "keydown";
        }
    }
}
// add key listeners
document.addEventListener("keydown", keys.keyEvent);
document.addEventListener("keyup", keys.keyEvent);


const view = (()=>{
    const matrix = [1,0,0,1,0,0]; // current view transform
    const invMatrix = [1,0,0,1,0,0]; // current inverse view transform
    var m = matrix;  // alias
    var im = invMatrix; // alias
    var rotate = 0;  // current x axis direction in radians
    var scale = 1;   // current scale
    const pos = {      // current position of origin
        x : 0,
        y : 0,
    }
    var dirty = true;
    return {
        apply(ctx){
            if(dirty){ this.update() }
            var m = matrix;
            ctx.setTransform(m[0],m[1],m[2],m[3],m[4],m[5]);
        },
        update(){ // call to update transforms
            var xdx = Math.cos(rotate) * scale;
            var xdy = Math.sin(rotate) * scale;
            m[0] = xdx;
            m[1] = xdy;
            m[2] = -xdy;
            m[3] = xdx;
            m[4] = pos.x;
            m[5] = pos.y;
            // calculate the inverse transformation
            cross = m[0] * m[3] - m[1] * m[2];
            im[0] =  m[3] / cross;
            im[1] = -m[1] / cross;
            im[2] = -m[2] / cross;
            im[3] =  m[0] / cross;
            dirty = false;
        },
        toWorld(x,y,point = {}){  // convert screen to world coords
            var xx, yy;
            if(dirty){ this.update() }
            xx = x - matrix[4];     
            yy = y - matrix[5];     
            point.x =  xx * im[0] + yy * im[2]; 
            point.y = xx * im[1] + yy * im[3];
            return point;
        },        
        toScreen(x,y,point = {}){  // convert world coords to  coords
            if(dirty){ this.update() }
            point.x =  x * m[0] + y * m[2] + m[4]; 
            point.y = x * m[1] + y * m[3] + m[5];
            return point;
        },        
        movePos(x,y){
            pos.x += x;
            pos.y += y;
            dirty = true;
        },
        setPos(x,y){
            pos.x = x;
            pos.y = y;
            dirty = true;
        },
        setScale(sc){
            scale = sc;
            dirty = true;
        },
        scaleScale(sc){
            scale *= sc;
            dirty = true;
        },
        scaleAt(x,y,sc){
            if(dirty){ this.update() }
            scale *= sc;
            pos.x = x - (x - pos.x) * sc;
            pos.y = y - (y - pos.y) * sc;            
            dirty = true;
        }
    };
})();

function onResize(){
    setStyle(ctx,font);

}
const drag = {
    dragging : false,
    lastX : 0,
    lastY : 0,
    update(){
        var dx,dy;
        if(mouse.w){
            if(mouse.w < 0){
                mouse.w += 10;
                view.scaleAt(mouse.x,mouse.y,1/1.02);
                if(mouse.w > 0){
                    mouse.w = 0;
                }
            } else if(mouse.w > 0){
                mouse.w -= 10;
                view.scaleAt(mouse.x,mouse.y,1.02);
                if(mouse.w < 0){
                    mouse.w = 0;
                }
            }
        }
        if(mouse.buttonRaw){
            if(!this.dragging){
                this.dragging = true;
                this.lastX = mouse.x;
                this.lastY = mouse.y;
            }else{
                if(mouse.buttonRaw & 1){
                    dx = mouse.x-this.lastX;
                    dy = mouse.y-this.lastY;
                    this.lastX = mouse.x;
                    this.lastY = mouse.y;
                    view.movePos(dx,dy);
                }
            }
        }else{
            if(this.dragging){
                this.dragging = false;
            }
        }
    }
}

function display() { // call once per frame
    ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
    ctx.globalAlpha = 1; // reset alpha
    ctx.clearRect(0, 0, w, h);
    if(keys.ArrowLeft ){ mouse.w += 10 }
    if(keys.ArrowRight){ mouse.w -= 10 }
    drag.update();
    if(image.complete){
        view.apply(ctx);
        ctx.drawImage(image,0,0);
        ctx.setTransform(1, 0, 0, 1, 0, 0);
        ctx.fillText("Click drag to pan. Wheel to zoom or left/right arrow.",cw,20)
    }else{
        ctx.fillText("Loading Image...",cw,ch)
    }
}




/******************************************************************************
 The code from here down is generic full page mouse and canvas boiler plate 
 code. As I do many examples which all require the same mouse and canvas 
 functionality I have created this code to keep a consistent interface. The
 Code may or may not be part of the answer.
 This code may or may not have ES6 only sections so will require a transpiler
 such as babel.js to run on legacy browsers.
 *****************************************************************************/
// V2.0 ES6 version for Stackoverflow and Groover QuickRun 
var w, h, cw, ch, canvas, ctx, mouse, globalTime = 0;
// You can declare onResize (Note the capital R) as a callback that is also
// called once at start up. Warning on first call canvas may not be at full
// size. 
;(function(){
    const RESIZE_DEBOUNCE_TIME = 100;
    var resizeTimeoutHandle;
    var firstRun = true;
    function createCanvas () {
        var c,cs;
        cs = (c = document.createElement("canvas")).style;
        cs.position = "absolute";
        cs.top = cs.left = "0px";
        cs.zIndex = 10;
        document.body.appendChild(c);
        return c;
    }
    function resizeCanvas () {
        if (canvas === undefined) { canvas = createCanvas() }
        canvas.width = innerWidth;
        canvas.height = innerHeight;
        ctx = canvas.getContext("2d");
        if (typeof setGlobals === "function") { setGlobals() }
        if (typeof onResize === "function") {
            clearTimeout(resizeTimeoutHandle);
            if (firstRun) { onResize() }
            else { resizeTimeoutHandle = setTimeout(onResize, RESIZE_DEBOUNCE_TIME) }
            firstRun = false;
        }
    }
    function setGlobals () {
        cw = (w = canvas.width) / 2;
        ch = (h = canvas.height) / 2;
    }
    mouse = (function () {
        function preventDefault(e) { e.preventDefault() }
        var m; // alias for mouse
        var mouse = {
            x : 0, y : 0, w : 0, // mouse position and wheel
            alt : false, shift : false, ctrl : false, // mouse modifiers 
            buttonRaw : 0,
            over : false,                        // true if mouse over the element
            buttonOnMasks : [0b1, 0b10, 0b100],  // mouse button on masks
            buttonOffMasks : [0b110, 0b101, 0b011], // mouse button off masks
            active : false,
            bounds : null,
            eventNames : "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(","),
            event(e) {
                var t = e.type;
                m.bounds = m.element.getBoundingClientRect();
                m.x = e.pageX - m.bounds.left - scrollX;
                m.y = e.pageY - m.bounds.top - scrollY;
                m.alt = e.altKey;
                m.shift = e.shiftKey;
                m.ctrl = e.ctrlKey;
                if (t === "mousedown") { m.buttonRaw |= m.buttonOnMasks[e.which - 1] }
                else if (t === "mouseup") { m.buttonRaw &= m.buttonOffMasks[e.which - 1] }
                else if (t === "mouseout") { m.over = false }
                else if (t === "mouseover") { m.over = true }
                else if (t === "mousewheel") {
                    m.w = e.wheelDelta 
                    e.preventDefault();
                }
                else if (t === "DOMMouseScroll") { 
                    m.w = -e.detail 
                    e.preventDefault();
                }
            },
            start(element) {
                m.element = element === undefined ? document : element;
                m.eventNames.forEach(name =>  document.addEventListener(name, mouse.event) );
                document.addEventListener("contextmenu", preventDefault, false);
                m.active = true;
            },
        }
        m = mouse;
        return mouse;
    })();
    function update(timer) { // Main update loop
        globalTime = timer;
        display();           // call demo code
        requestAnimationFrame(update)
    }
    setTimeout(function(){
        canvas = createCanvas(); 
        mouse.start(canvas, true);
        resizeCanvas();
        window.addEventListener("resize", resizeCanvas);
        requestAnimationFrame(update);
    },0);
})();


/** SimpleFullCanvasMouse.js end **/
#imageCC {
   font-family : arial;
   font-size : 10px;
   position : absolute;
   z-index : 100;
   bottom : 3px;
   right : 10px;
   background : rgba(255,255,255,0.7);
}
<div id=imageCC>Image rights.
<a href="https://commons.wikimedia.org/wiki/User:Kabelleger">Kabelleger</a> / David Gubler (<a href="http://www.bahnbilder.ch">http://www.bahnbilder.ch</a>), <a href="https://commons.wikimedia.org/wiki/File:KTZ_2TE10U_Aynabulak.jpg">KTZ 2TE10U Aynabulak</a>, <a href="https://creativecommons.org/licenses/by-sa/3.0/legalcode">CC BY-SA 3.0</a>
</div>