ซูมเข้าที่จุดหนึ่ง (ใช้มาตราส่วนและการแปล)


156

ฉันต้องการที่จะสามารถซูมเข้าในประเด็นภายใต้เมาส์ใน HTML 5 ผ้าใบเช่นซูมบนGoogle Maps ฉันจะบรรลุสิ่งนั้นได้อย่างไร


2
ฉันใช้สิ่งนี้เพื่อซูมผืนผ้าใบของฉันและใช้งานได้ดี! สิ่งเดียวที่ฉันต้องเพิ่มคือการคำนวณจำนวนซูมไม่ได้อย่างที่คุณคาดไว้ "var zoom = 1 + wheel / 2;" นั่นคือผลลัพธ์เป็น 1.5 สำหรับการซูมเข้าและ 0.5 สำหรับการซูมออก ฉันแก้ไขมันในรุ่นของฉันเพื่อให้ฉันมี 1.5 สำหรับการซูมเข้าและ 1 / 1.5 สำหรับการซูมออกซึ่งทำให้ปริมาณการซูมเข้าและซูมออกเท่ากัน ดังนั้นหากคุณซูมเข้าครั้งเดียวและซูมกลับคุณจะมีภาพเหมือนก่อนซูม
คริส

2
โปรดทราบว่านี้ไม่ได้ทำงานใน Firefox แต่วิธีการที่สามารถนำไปใช้กับjQuery เมาส์วีปลั๊กอิน ขอบคุณสำหรับการแบ่งปัน!
johndodo

2
var zoom = Math.pow (1.5f, ล้อ); // ใช้เพื่อคำนวณการซูม มันมีข้อดีที่การซูมด้วยวงล้อ = 2 เหมือนกับการซูมสองครั้งด้วยวงล้อ = 1 นอกจากนี้การซูมเข้า +2 และออกโดย +2 จะคืนค่ามาตราส่วนดั้งเดิม
Matt

คำตอบ:


126

ทางออกที่ดีกว่าคือการย้ายตำแหน่งวิวพอร์ตเพียงแค่เปลี่ยนการซูม จุดซูมเป็นเพียงจุดในการซูมเก่าและการซูมใหม่ที่คุณต้องการจะยังคงเหมือนเดิม ซึ่งจะบอกว่าวิวพอร์ตที่ซูมไว้ล่วงหน้าและวิวพอร์ตหลังซูมนั้นมีจุดซูมเดียวกันเมื่อเทียบกับวิวพอร์ต เนื่องจากเรากำลังปรับเทียบกับที่มา คุณสามารถปรับตำแหน่งวิวพอร์ตได้:

scalechange = newscale - oldscale;
offsetX = -(zoomPointX * scalechange);
offsetY = -(zoomPointY * scalechange);

ดังนั้นจริงๆคุณสามารถเลื่อนลงและไปทางขวาเมื่อคุณซูมโดยปัจจัยที่คุณซูมเข้าเทียบกับจุดที่คุณซูม

ป้อนคำอธิบายรูปภาพที่นี่


2
มีค่ามากกว่ารหัสตัดและวางคือคำอธิบายว่าทางออกที่ดีที่สุดคืออะไรและทำไมมันถึงทำงานโดยไม่มีสัมภาระโดยเฉพาะอย่างยิ่งถ้ามันยาวสามบรรทัด
Tatarize

2
scalechange = newscale / oldscale?
Tejesh Alimilli

4
นอกจากนี้ฉันต้องการเพิ่มสำหรับผู้ที่ต้องการบรรลุแผนที่เช่นองค์ประกอบแพนซูมที่เมาส์ X, Y ควรจะเป็น (mousePosRelativeToContainer - currentTransform) / currentScale มิฉะนั้นมันจะรักษาตำแหน่งเมาส์ปัจจุบันเป็นที่สัมพันธ์กับภาชนะ
Gilad

1
ใช่คณิตศาสตร์นี้อนุมานว่าการซูมและการแพนอยู่ใน coords ที่เกี่ยวข้องกับจุดกำเนิด หากสัมพันธ์กับวิวพอร์ตคุณต้องปรับเปลี่ยนให้เหมาะสม แม้ว่าฉันจะสมมติว่าคณิตศาสตร์ที่ถูกต้องคือ zoomPoint = (mousePosRelativeToContainer + currentTranslation) คณิตศาสตร์นี้ยังถือว่าจุดกำเนิดโดยทั่วไปอยู่ที่ด้านบนซ้ายของสนาม แต่การปรับสำหรับสถานการณ์ที่ผิดปกติเล็กน้อยนั้นง่ายกว่ามากเนื่องจากความเรียบง่าย
Tatarize

1
@ C.Finke วิธีที่สองที่สามารถทำได้คือการใช้การแปลภายใน ctx คุณวาดทุกอย่างที่มีขนาดเท่ากันและอยู่ในตำแหน่งเดียวกัน แต่คุณเพียงแค่ใช้การคูณเมทริกซ์ภายในผืนผ้าใบจาวาสคริปต์เพื่อตั้งค่าการแพนและสเกล (ซูม) ของบริบท ดังนั้นแทนที่จะวาดรูปร่างทั้งหมดในตำแหน่งอื่น คุณวาดพวกมันในที่เดียวกันและย้ายวิวพอร์ตภายในจาวาสคริปต์ วิธีนี้ยังต้องการให้คุณใช้เหตุการณ์เมาส์และแปลกลับไปด้านบน ดังนั้นคุณจะลบกะทะแล้วย้อนกลับด้วยการซูม
Tatarize

68

ในที่สุดก็แก้ไขมัน:

var zoomIntensity = 0.2;

var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var width = 600;
var height = 200;

var scale = 1;
var originx = 0;
var originy = 0;
var visibleWidth = width;
var visibleHeight = height;


function draw(){
    // Clear screen to white.
    context.fillStyle = "white";
    context.fillRect(originx,originy,800/scale,600/scale);
    // Draw the black square.
    context.fillStyle = "black";
    context.fillRect(50,50,100,100);
}
// Draw loop at 60FPS.
setInterval(draw, 1000/60);

canvas.onwheel = function (event){
    event.preventDefault();
    // Get mouse offset.
    var mousex = event.clientX - canvas.offsetLeft;
    var mousey = event.clientY - canvas.offsetTop;
    // Normalize wheel to +1 or -1.
    var wheel = event.deltaY < 0 ? 1 : -1;

    // Compute zoom factor.
    var zoom = Math.exp(wheel*zoomIntensity);
    
    // Translate so the visible origin is at the context's origin.
    context.translate(originx, originy);
  
    // Compute the new visible origin. Originally the mouse is at a
    // distance mouse/scale from the corner, we want the point under
    // the mouse to remain in the same place after the zoom, but this
    // is at mouse/new_scale away from the corner. Therefore we need to
    // shift the origin (coordinates of the corner) to account for this.
    originx -= mousex/(scale*zoom) - mousex/scale;
    originy -= mousey/(scale*zoom) - mousey/scale;
    
    // Scale it (centered around the origin due to the trasnslate above).
    context.scale(zoom, zoom);
    // Offset the visible origin to it's proper position.
    context.translate(-originx, -originy);

    // Update scale and others.
    scale *= zoom;
    visibleWidth = width / scale;
    visibleHeight = height / scale;
}
<canvas id="canvas" width="600" height="200"></canvas>

กุญแจดังที่@Tatarize ชี้ให้เห็นคือการคำนวณตำแหน่งแกนเพื่อให้จุดซูม (ตัวชี้เมาส์) ยังคงอยู่ในตำแหน่งเดียวกันหลังจากซูม

เดิมทีเมาส์นั้นอยู่ห่างmouse/scaleจากมุมเราต้องการให้จุดใต้เมาส์อยู่ในตำแหน่งเดียวกันหลังจากการซูม แต่อยู่mouse/new_scaleห่างจากมุม ดังนั้นเราต้องเปลี่ยนorigin(พิกัดของมุม) เป็นบัญชีสำหรับสิ่งนี้

originx -= mousex/(scale*zoom) - mousex/scale;
originy -= mousey/(scale*zoom) - mousey/scale;
scale *= zoom

จากนั้นโค้ดที่เหลือจะต้องใช้การปรับขนาดและแปลเป็นบริบทการวาดดังนั้นจึงเกิดขึ้นพร้อมกับมุมผ้าใบ


ขอบคุณเพื่อนเกือบหายไป 2 วันก่อนที่จะพบรหัสของคุณ
Velaro

เฮ้ฉันแค่กำลังมองหาอะไรแบบนี้และแค่อยากจะบอกว่าคุณเอามันออกไป!
chrisallick

26

จริงๆแล้วนี่เป็นปัญหาที่ยากมาก (ทางคณิตศาสตร์) และฉันกำลังทำงานในสิ่งเดียวกันเกือบ ฉันถามคำถามที่คล้ายกันใน Stackoverflow แต่ไม่มีการตอบสนอง แต่โพสต์ใน DocType (StackOverflow สำหรับ HTML / CSS) และได้รับคำตอบ ลองดูที่http://doctype.com/javascript-image-zoom-css3-transforms-calculate-origin-example

ฉันกำลังสร้างปลั๊กอิน jQuery ที่ทำสิ่งนี้ (ซูมแบบ Google แผนที่โดยใช้การแปลง CSS3) ฉันได้เคอร์เซอร์ซูมไปที่เมาส์ทำงานได้ดี แต่ยังคงพยายามหาวิธีอนุญาตให้ผู้ใช้ลากภาพไปมาอย่างที่คุณสามารถทำได้ใน Google แผนที่ เมื่อฉันใช้งานได้ฉันจะโพสต์โค้ดที่นี่ แต่ลองดูลิงค์ด้านบนสำหรับส่วนที่เมาส์ซูมไปยังจุด

ฉันไม่ทราบว่ามีขนาดและวิธีการแปลในบริบท Canvas คุณสามารถบรรลุสิ่งเดียวกันโดยใช้ CSS3 เช่น ใช้ jQuery:

$('div.canvasContainer > canvas')
    .css('-moz-transform', 'scale(1) translate(0px, 0px)')
    .css('-webkit-transform', 'scale(1) translate(0px, 0px)')
    .css('-o-transform', 'scale(1) translate(0px, 0px)')
    .css('transform', 'scale(1) translate(0px, 0px)');

ตรวจสอบให้แน่ใจว่าคุณตั้งค่า CSS3 transform-origin เป็น 0, 0 (-moz-transform-origin: 0 0) การใช้การแปลง CSS3 ช่วยให้คุณสามารถซูมเข้ากับสิ่งใด ๆ ได้เพียงตรวจสอบให้แน่ใจว่าคอนเทนเนอร์ DIV ถูกตั้งค่าเป็นล้น: ซ่อนเพื่อหยุดขอบซูมที่ทะลักออกมาจากด้านข้าง

ไม่ว่าคุณจะใช้การแปลง CSS3 หรือระดับของตัวเองและวิธีการแปลนั้นขึ้นอยู่กับคุณ แต่ให้ตรวจสอบลิงค์ด้านบนเพื่อคำนวณ


อัพเดท: Meh! ฉันจะโพสต์รหัสที่นี่แทนที่จะให้คุณติดตามลิงก์:

$(document).ready(function()
{
    var scale = 1;  // scale of the image
    var xLast = 0;  // last x location on the screen
    var yLast = 0;  // last y location on the screen
    var xImage = 0; // last x location on the image
    var yImage = 0; // last y location on the image

    // if mousewheel is moved
    $("#mosaicContainer").mousewheel(function(e, delta)
    {
        // find current location on screen 
        var xScreen = e.pageX - $(this).offset().left;
        var yScreen = e.pageY - $(this).offset().top;

        // find current location on the image at the current scale
        xImage = xImage + ((xScreen - xLast) / scale);
        yImage = yImage + ((yScreen - yLast) / scale);

        // determine the new scale
        if (delta > 0)
        {
            scale *= 2;
        }
        else
        {
            scale /= 2;
        }
        scale = scale < 1 ? 1 : (scale > 64 ? 64 : scale);

        // determine the location on the screen at the new scale
        var xNew = (xScreen - xImage) / scale;
        var yNew = (yScreen - yImage) / scale;

        // save the current screen location
        xLast = xScreen;
        yLast = yScreen;

        // redraw
        $(this).find('div').css('-moz-transform', 'scale(' + scale + ')' + 'translate(' + xNew + 'px, ' + yNew + 'px' + ')')
                           .css('-moz-transform-origin', xImage + 'px ' + yImage + 'px')
        return false;
    });
});

แน่นอนว่าคุณจะต้องปรับให้เข้ากับขนาดผ้าใบและวิธีการแปล


อัปเดต 2:เพิ่งสังเกตว่าฉันกำลังใช้การแปลงแหล่งที่มาพร้อมกับการแปล ฉันจัดการเพื่อใช้เวอร์ชันที่ใช้สเกลและแปลด้วยตนเองตรวจสอบได้ที่นี่http://www.dominicpettifer.co.uk/Files/Mosaic/MosaicTest.htmlรอภาพที่จะดาวน์โหลดจากนั้นใช้ ล้อเลื่อนของเมาส์เพื่อซูมและยังรองรับการเลื่อนด้วยการลากภาพไปมา ใช้การแปลง CSS3 แต่คุณควรใช้การคำนวณเดียวกันสำหรับ Canvas ของคุณ


ในที่สุดฉันก็แก้มันได้ฉันใช้เวลา 3 นาทีหลังจากนั้นประมาณ 2 สัปดาห์ในการทำอย่างอื่น
csiz

@Synday ลิงก์ Ironfootman ในการอัปเดตของเขาไม่ทำงาน ลิงค์นี้: dominicpettifer.co.uk/Files/Mosaic/MosaicTest.html ฉันต้องการความรู้สึกแบบนี้ คุณช่วยโพสต์โค้ดได้ไหม ขอบคุณ
Bogz

2
ณ วันนี้ (sept.2014) ลิงก์ไปยัง MosaicTest.html นั้นตายแล้ว
คริส

การสาธิตโมเสกหายไป ฉันใช้วานิลลา js ปกติและไม่ใช่ jQuery $ (นี่คืออะไร) หมายถึงอะไร document.body.offsetTop? ฉันต้องการดูการสาธิตโมเสกโครงการ foreverscape.com ของฉันจริงๆสามารถได้รับประโยชน์จากมัน
FlavourScape

2
หน้าตัวอย่าง mosaic ถูกบันทึกไว้ใน archive.org: web.archive.org/web/20130126152008/http://…
Chris

9

ฉันพบปัญหานี้โดยใช้ c ++ ซึ่งฉันอาจไม่ควรใช้การฝึกอบรม OpenGL เพื่อเริ่มต้นด้วย ... อย่างไรก็ตามถ้าคุณใช้ตัวควบคุมที่มีต้นกำเนิดอยู่ที่มุมซ้ายบนและคุณต้องการแพน / ซูม เช่นเดียวกับ google maps นี่คือเค้าโครง (ใช้ allegro เป็นตัวจัดการกิจกรรมของฉัน):

// initialize
double originx = 0; // or whatever its base offset is
double originy = 0; // or whatever its base offset is
double zoom = 1;

.
.
.

main(){

    // ...set up your window with whatever
    //  tool you want, load resources, etc

    .
    .
    .
    while (running){
        /* Pan */
        /* Left button scrolls. */
        if (mouse == 1) {
            // get the translation (in window coordinates)
            double scroll_x = event.mouse.dx; // (x2-x1) 
            double scroll_y = event.mouse.dy; // (y2-y1) 

            // Translate the origin of the element (in window coordinates)      
            originx += scroll_x;
            originy += scroll_y;
        }

        /* Zoom */ 
        /* Mouse wheel zooms */
        if (event.mouse.dz!=0){    
            // Get the position of the mouse with respect to 
            //  the origin of the map (or image or whatever).
            // Let us call these the map coordinates
            double mouse_x = event.mouse.x - originx;
            double mouse_y = event.mouse.y - originy;

            lastzoom = zoom;

            // your zoom function 
            zoom += event.mouse.dz * 0.3 * zoom;

            // Get the position of the mouse
            // in map coordinates after scaling
            double newx = mouse_x * (zoom/lastzoom);
            double newy = mouse_y * (zoom/lastzoom);

            // reverse the translation caused by scaling
            originx += mouse_x - newx;
            originy += mouse_y - newy;
        }
    }
}  

.
.
.

draw(originx,originy,zoom){
    // NOTE:The following is pseudocode
    //          the point is that this method applies so long as
    //          your object scales around its top-left corner
    //          when you multiply it by zoom without applying a translation.

    // draw your object by first scaling...
    object.width = object.width * zoom;
    object.height = object.height * zoom;

    //  then translating...
    object.X = originx;
    object.Y = originy; 
}

9

ฉันชอบคำตอบของ Tatarizeแต่ฉันจะให้ทางเลือกอื่น นี่เป็นปัญหาพีชคณิตเชิงเส้นเล็กน้อยและวิธีที่ฉันนำเสนอทำงานได้ดีกับแพนซูมย่อภาพ ฯลฯ นั่นคือมันทำงานได้ดีถ้าภาพของคุณเปลี่ยนไปแล้ว

เมื่อปรับขนาดเมทริกซ์สเกลจะอยู่ที่จุด (0, 0) ดังนั้นหากคุณมีภาพและปรับขนาดเป็น 2 เท่าจุดล่างขวาจะเป็นสองเท่าในทิศทาง x และ y (โดยใช้แบบแผนที่ [0, 0] เป็นมุมบนซ้ายของภาพ)

หากคุณต้องการซูมภาพเกี่ยวกับจุดศูนย์กลางแทนวิธีแก้ไขจะเป็นดังนี้: (1) แปลภาพโดยที่จุดศูนย์กลางอยู่ที่ (0, 0); (2) ปรับขนาดภาพตามปัจจัย x และ y (3) แปลภาพกลับ กล่าวคือ

myMatrix
  .translate(image.width / 2, image.height / 2)    // 3
  .scale(xFactor, yFactor)                         // 2
  .translate(-image.width / 2, -image.height / 2); // 1

ยิ่งกว่าเดิมกลยุทธ์เดียวกันใช้ได้กับทุกจุด ตัวอย่างเช่นถ้าคุณต้องการปรับขนาดภาพที่จุด P:

myMatrix
  .translate(P.x, P.y)
  .scale(xFactor, yFactor)
  .translate(-P.x, -P.y);

และสุดท้ายหากภาพถูกแปลงไปแล้วในบางลักษณะ (ตัวอย่างเช่นถ้าหมุนหมุนเอียงแปลหรือปรับขนาด) แล้วการแปลงปัจจุบันจำเป็นต้องรักษาไว้ โดยเฉพาะการแปลงที่กำหนดไว้ข้างต้นจะต้องมีการโพสต์ทวีคูณ (หรือคูณขวา) โดยการแปลงปัจจุบัน

myMatrix
  .translate(P.x, P.y)
  .scale(xFactor, yFactor)
  .translate(-P.x, -P.y)
  .multiply(myMatrix);

ที่นั่นคุณมีมัน นี่คือเสียงอึกทึกครึกโครมที่แสดงสิ่งนี้ในการกระทำ เลื่อนเมาส์วีลไปตามจุดต่างๆและคุณจะเห็นว่าพวกมันอยู่ติดกันตลอดเวลา (ทดสอบใน Chrome เท่านั้น) http://plnkr.co/edit/3aqsWHPLlSXJ9JCcJzgH?p=preview


1
ฉันต้องบอกว่าถ้าคุณมีเมทริกซ์การแปลงเลียนแบบให้คุณใช้ด้วยความกระตือรือร้น เมทริกซ์การแปลงจำนวนมากจะมีฟังก์ชั่นซูม (sx, sy, x, y) ที่ทำเช่นนั้น มันเกือบจะคุ้มค่าหากคุณไม่ได้ใช้
Tatarize

ในความเป็นจริงฉันสารภาพว่าในรหัสที่ฉันใช้วิธีนี้ได้ถูกแทนที่ด้วยคลาสเมทริกซ์ และฉันทำสิ่งนี้แน่นอนหลายครั้งและปรุงคลาสเมทริกซ์ให้สุกไม่น้อยกว่าสองเท่า ( github.com/EmbroidePy/pyembroidery/blob/master/pyembroidery/ ...... ), ( github.com/EmbroidePy/EmbroidePy/blob/master/embroidepy/ ...... ) หากคุณต้องการอะไรที่ซับซ้อนกว่าการดำเนินการเหล่านี้เมทริกซ์ก็เป็นคำตอบที่ถูกต้องและเมื่อคุณได้รับการจัดการกับพีชคณิตเชิงเส้นคุณจะรู้ว่าคำตอบนี้เป็นคำตอบที่ดีที่สุด
Tatarize

6

นี่คือวิธีแก้ปัญหาของฉันสำหรับภาพกึ่งกลาง:

var MIN_SCALE = 1;
var MAX_SCALE = 5;
var scale = MIN_SCALE;

var offsetX = 0;
var offsetY = 0;

var $image     = $('#myImage');
var $container = $('#container');

var areaWidth  = $container.width();
var areaHeight = $container.height();

$container.on('wheel', function(event) {
    event.preventDefault();
    var clientX = event.originalEvent.pageX - $container.offset().left;
    var clientY = event.originalEvent.pageY - $container.offset().top;

    var nextScale = Math.min(MAX_SCALE, Math.max(MIN_SCALE, scale - event.originalEvent.deltaY / 100));

    var percentXInCurrentBox = clientX / areaWidth;
    var percentYInCurrentBox = clientY / areaHeight;

    var currentBoxWidth  = areaWidth / scale;
    var currentBoxHeight = areaHeight / scale;

    var nextBoxWidth  = areaWidth / nextScale;
    var nextBoxHeight = areaHeight / nextScale;

    var deltaX = (nextBoxWidth - currentBoxWidth) * (percentXInCurrentBox - 0.5);
    var deltaY = (nextBoxHeight - currentBoxHeight) * (percentYInCurrentBox - 0.5);

    var nextOffsetX = offsetX - deltaX;
    var nextOffsetY = offsetY - deltaY;

    $image.css({
        transform : 'scale(' + nextScale + ')',
        left      : -1 * nextOffsetX * nextScale,
        right     : nextOffsetX * nextScale,
        top       : -1 * nextOffsetY * nextScale,
        bottom    : nextOffsetY * nextScale
    });

    offsetX = nextOffsetX;
    offsetY = nextOffsetY;
    scale   = nextScale;
});
body {
    background-color: orange;
}
#container {
    margin: 30px;
    width: 500px;
    height: 500px;
    background-color: white;
    position: relative;
    overflow: hidden;
}
img {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    max-width: 100%;
    max-height: 100%;
    margin: auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

<div id="container">
    <img id="myImage" src="http://s18.postimg.org/eplac6dbd/mountain.jpg">
</div>


4

นี่คือวิธีอื่นในการทำเช่นนั้นโดยใช้ setTransform () แทนที่จะเป็น scale () และ translate () ทุกอย่างถูกเก็บไว้ในวัตถุเดียวกัน ผ้าใบจะถือว่าอยู่ที่ 0,0 ในหน้ามิฉะนั้นคุณจะต้องลบตำแหน่งออกจากหน้าประสาน

this.zoomIn = function (pageX, pageY) {
    var zoomFactor = 1.1;
    this.scale = this.scale * zoomFactor;
    this.lastTranslation = {
        x: pageX - (pageX - this.lastTranslation.x) * zoomFactor,
        y: pageY - (pageY - this.lastTranslation.y) * zoomFactor
    };
    this.canvasContext.setTransform(this.scale, 0, 0, this.scale,
                                    this.lastTranslation.x,
                                    this.lastTranslation.y);
};
this.zoomOut = function (pageX, pageY) {
    var zoomFactor = 1.1;
    this.scale = this.scale / zoomFactor;
    this.lastTranslation = {
        x: pageX - (pageX - this.lastTranslation.x) / zoomFactor,
        y: pageY - (pageY - this.lastTranslation.y) / zoomFactor
    };
    this.canvasContext.setTransform(this.scale, 0, 0, this.scale,
                                    this.lastTranslation.x,
                                    this.lastTranslation.y);
};

รหัสที่มาพร้อมกับการจัดการปรากฎว่า:

this.startPan = function (pageX, pageY) {
    this.startTranslation = {
        x: pageX - this.lastTranslation.x,
        y: pageY - this.lastTranslation.y
    };
};
this.continuePan = function (pageX, pageY) {
    var newTranslation = {x: pageX - this.startTranslation.x,
                          y: pageY - this.startTranslation.y};
    this.canvasContext.setTransform(this.scale, 0, 0, this.scale,
                                    newTranslation.x, newTranslation.y);
};
this.endPan = function (pageX, pageY) {
    this.lastTranslation = {
        x: pageX - this.startTranslation.x,
        y: pageY - this.startTranslation.y
    };
};

หากต้องการคำตอบด้วยตัวเองให้พิจารณาว่าพิกัดหน้าเดียวกันนั้นต้องตรงกับพิกัดผืนผ้าใบเดียวกันก่อนและหลังการซูม จากนั้นคุณสามารถทำพีชคณิตเริ่มต้นจากสมการนี้:

(pageCoords - การแปล) / scale = canvasCoords


3

ฉันต้องการที่จะใส่ข้อมูลที่นี่สำหรับผู้ที่แยกการวาดภาพและย้าย -zooming

สิ่งนี้อาจมีประโยชน์เมื่อคุณต้องการจัดเก็บการซูมและตำแหน่งวิวพอร์ต

นี่คือลิ้นชัก:

function redraw_ctx(){
   self.ctx.clearRect(0,0,canvas_width, canvas_height)
   self.ctx.save()
   self.ctx.scale(self.data.zoom, self.data.zoom) // 
   self.ctx.translate(self.data.position.left, self.data.position.top) // position second
   // Here We draw useful scene My task - image:
   self.ctx.drawImage(self.img ,0,0) // position 0,0 - we already prepared
   self.ctx.restore(); // Restore!!!
}

แจ้งให้ทราบล่วงหน้าขนาดต้องเป็นคนแรก

และนี่คือโปรแกรมซูม:

function zoom(zf, px, py){
    // zf - is a zoom factor, which in my case was one of (0.1, -0.1)
    // px, py coordinates - is point within canvas 
    // eg. px = evt.clientX - canvas.offset().left
    // py = evt.clientY - canvas.offset().top
    var z = self.data.zoom;
    var x = self.data.position.left;
    var y = self.data.position.top;

    var nz = z + zf; // getting new zoom
    var K = (z*z + z*zf) // putting some magic

    var nx = x - ( (px*zf) / K ); 
    var ny = y - ( (py*zf) / K);

    self.data.position.left = nx; // renew positions
    self.data.position.top = ny;   
    self.data.zoom = nz; // ... and zoom
    self.redraw_ctx(); // redraw context
    }

และแน่นอนเราจะต้องมี dragger:

this.my_cont.mousemove(function(evt){
    if (is_drag){
        var cur_pos = {x: evt.clientX - off.left,
                       y: evt.clientY - off.top}
        var diff = {x: cur_pos.x - old_pos.x,
                    y: cur_pos.y - old_pos.y}

        self.data.position.left += (diff.x / self.data.zoom);  // we want to move the point of cursor strictly
        self.data.position.top += (diff.y / self.data.zoom);

        old_pos = cur_pos;
        self.redraw_ctx();

    }


})

3
if(wheel > 0) {
    this.scale *= 1.1; 
    this.offsetX -= (mouseX - this.offsetX) * (1.1 - 1);
    this.offsetY -= (mouseY - this.offsetY) * (1.1 - 1);
}
else {
    this.scale *= 1/1.1; 
    this.offsetX -= (mouseX - this.offsetX) * (1/1.1 - 1);
    this.offsetY -= (mouseY - this.offsetY) * (1/1.1 - 1);
}

2

นี่คือการติดตั้งโค้ดของคำตอบของ @ tatarize โดยใช้ PIXI.js. ฉันมีวิวพอร์ตเป็นส่วนหนึ่งของภาพที่มีขนาดใหญ่มาก (เช่นสไตล์ของ Google แผนที่)

$canvasContainer.on('wheel', function (ev) {

    var scaleDelta = 0.02;
    var currentScale = imageContainer.scale.x;
    var nextScale = currentScale + scaleDelta;

    var offsetX = -(mousePosOnImage.x * scaleDelta);
    var offsetY = -(mousePosOnImage.y * scaleDelta);

    imageContainer.position.x += offsetX;
    imageContainer.position.y += offsetY;

    imageContainer.scale.set(nextScale);

    renderer.render(stage);
});
  • $canvasContainer เป็นคอนเทนเนอร์ html ของฉัน
  • imageContainer เป็นคอนเทนเนอร์ PIXI ของฉันที่มีภาพอยู่
  • mousePosOnImage คือตำแหน่งของเมาส์ที่สัมพันธ์กับรูปภาพทั้งหมด (ไม่ใช่แค่พอร์ตมุมมอง)

นี่คือวิธีที่ฉันได้ตำแหน่งของเมาส์:

  imageContainer.on('mousemove', _.bind(function(ev) {
    mousePosOnImage = ev.data.getLocalPosition(imageContainer);
    mousePosOnViewport.x = ev.data.originalEvent.offsetX;
    mousePosOnViewport.y = ev.data.originalEvent.offsetY;
  },self));

0

คุณต้องได้รับจุดในพื้นที่โลก (ตรงข้ามกับพื้นที่หน้าจอ) ก่อนและหลังการซูมแล้วแปลโดยเดลต้า

mouse_world_position = to_world_position(mouse_screen_position);
zoom();
mouse_world_position_new = to_world_position(mouse_screen_position);
translation += mouse_world_position_new - mouse_world_position;

ตำแหน่งของเมาส์อยู่ในพื้นที่หน้าจอดังนั้นคุณต้องเปลี่ยนเป็นพื้นที่ว่างในโลก การแปลงอย่างง่ายควรคล้ายกับสิ่งนี้:

world_position = screen_position / scale - translation

0

คุณสามารถใช้ฟังก์ชั่น scrollto (x, y) เพื่อจัดการตำแหน่งของแถบเลื่อนไปทางขวาจนถึงจุดที่คุณต้องแสดงหลังการซูมเพื่อค้นหาตำแหน่งของการใช้เมาส์ event.clientX และ event.clientY สิ่งนี้จะช่วยคุณ


0

สิ่งหนึ่งที่สำคัญ ... หากคุณมีสิ่งที่ชอบ:

body {
  zoom: 0.9;
}

คุณต้องทำให้สิ่งที่เท่าเทียมกันบนผืนผ้าใบ:

canvas {
  zoom: 1.1;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.