programing

점 확대(척도 및 변환 사용)

muds 2023. 9. 28. 08:49
반응형

점 확대(척도 및 변환 사용)

구글 지도를 확대하는 것처럼 HTML 5 캔버스로 마우스 밑에 있는 점을 확대할 수 있기를 원합니다.어떻게 하면 그것을 이룰 수 있을까요?

더 나은 해결책은 확대/축소 변경에 따라 뷰포트의 위치를 간단히 이동하는 것입니다.확대/축소 지점은 단순히 이전 확대/축소와 새 확대/축소를 동일하게 유지하려는 지점입니다.즉, 뷰포트가 미리 줌되고 뷰포트가 후 줌되고 뷰포트가 뷰포트에 대해 동일한 줌 포인트를 갖는다는 것입니다.우리가 원점에 비해 상대적으로 규모를 조정하고 있는 것을 감안하면 말입니다.그에 따라 뷰포트 위치를 조정할 수 있습니다.

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

따라서 확대할 때 오른쪽으로 아래로 확대할 수 있습니다. 확대한 지점에 비해 확대한 양만큼 축소할 수 있습니다.

enter image description here

드디어 해결했습니다.

const zoomIntensity = 0.2;

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

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


function draw(){
    // Clear screen to white.
    context.fillStyle = "white";
    context.fillRect(originx, originy, width/scale, height/scale);
    // Draw the black square.
    context.fillStyle = "black";
    context.fillRect(50, 50, 100, 100);

    // Schedule the redraw for the next display refresh.
    window.requestAnimationFrame(draw);
}
// Begin the animation loop.
draw();

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

    // Compute zoom factor.
    const 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 translate 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(코너의 coordin) 이를 설명합니다.

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

그러면 나머지 코드는 스케일링을 적용하고 원점이 캔버스 모서리와 일치하도록 그리기 컨텍스트로 변환해야 합니다.

이것은 사실 (수학적으로) 매우 어려운 문제이고, 저는 거의 같은 일을 하고 있습니다.Stack Overflow에 대해서 비슷한 질문을 했는데 답변이 없더니 DocType(HTML/CSS용 Stack Overflow)에 글을 올려 답변이 왔습니다.http://doctype.com/javascript-image-zoom-css3-transforms-calculate-origin-example 을 확인해보세요.

나는 이것을 하는 jQuery 플러그인을 만드는 중입니다(CSS3 Transforms를 사용한 Google Maps 스타일 줌).줌 투 마우스 커서 비트가 정상적으로 작동하고 있지만, 사용자가 구글 지도에서 할 수 있는 것처럼 캔버스를 끌어다 놓을 수 있는 방법을 찾고 있습니다.작동이 되면 여기에 코드를 게시하겠지만, 위 링크에서 마우스-줌-투-포인트 부분을 확인하세요.

캔버스 컨텍스트에 스케일 및 번역 방법이 있는지 몰랐습니다. jQuery를 사용하여 CSS3를 사용하면 동일한 작업을 수행할 수 있습니다.

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

CSS3 transform-origin을 0, 0(transform-origin: 0)으로 설정해야 합니다.CSS3 변환을 사용하면 무엇이든 확대할 수 있습니다. 컨테이너 DIV가 오버플로(숨김)로 설정되어 있는지 확인하십시오. 측면에서 확대되는 가장자리를 방지하기 위해 숨김.

CSS3 변환을 사용할지, 캔버스 자체의 스케일과 번역 방법을 사용할지는 당신에게 달려있지만 계산은 위 링크를 확인하세요.


업데이트: 음! 당신이 링크를 따라가게 하기 보다는 그냥 여기에 코드를 올리겠습니다.

$(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('transform', 'scale(' + scale + ')' + 'translate(' + xNew + 'px, ' + yNew + 'px' + ')')
                           .css('transform-origin', xImage + 'px ' + yImage + 'px')
        return false;
    });
});

물론 캔버스 스케일과 번역 방법을 사용하기 위해서는 이를 조정해야 합니다.

타타라이즈의 대답이 마음에 들지만 대안을 제시하겠습니다.이것은 사소한 선형대수 문제이며, 제가 제시하는 방법은 pan, zoom, skew 등과 잘 맞습니다.즉, 이미 이미지가 변환된 상태라면 잘 작동합니다.

행렬이 축척되면 축척은 (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

c++를 사용해서 이 문제를 겪었습니다. 처음에는 OpenGL 행렬을 사용하지 말았어야 했는데...어쨌든, 만약 당신이 맨 위 왼쪽 모서리를 원점으로 하는 컨트롤을 사용하고 있고, 구글 맵과 같은 pan/zoom를 원한다면, 다음과 같은 레이아웃이 있습니다(내 이벤트 핸들러로 알레그로 사용).

// 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; 
}

센터 지향 이미지를 위한 솔루션은 다음과 같습니다.

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="https://via.placeholder.com/300">
</div>

스케일() 대신 setTransform()을 사용하고 번역()하는 다른 방법이 있습니다.모든 것이 동일한 객체에 저장됩니다.캔버스는 페이지에서 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 - translation) / scale = canvasCoords

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);
}

그림 그리기와 이동 확대하기를 따로 하는 분들을 위해 몇 가지 정보를 여기에 넣으려고 합니다.

이 기능은 확대/축소 및 뷰포트 위치를 저장할 때 유용할 수 있습니다.

여기 서랍장입니다.

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
    }

물론 견인차가 필요합니다.

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();

    }


})

다음은 PIXI.js를 사용하여 @tatarize의 답변을 코드로 구현한 것입니다.매우 큰 이미지(예: 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);
});
  • $canvasContainerhtml 컨테이너 입니다.
  • 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));

확대/축소 전후의 세계 공간(화면 공간과 반대)에서 포인트를 얻은 다음 델타로 변환해야 합니다.

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

한가지 중요한 건...다음과 같은 것이 있을 경우:

body {
  zoom: 0.9;
}

캔버스에 조용한 것을 만들어야 합니다.

canvas {
  zoom: 1.1;
}

해결책은 다음과 같습니다.

// helpers
const diffPoints = (p1, p2) => {
    return {
        x: p1.x - p2.x,
        y: p1.y - p2.y,
    };
};

const addPoints = (p1, p2) => {
    return {
        x: p1.x + p2.x,
        y: p1.y + p2.y,
    };
};

function scalePoint(p1, scale) {
    return { x: p1.x / scale, y: p1.y / scale };
}

// constants
const ORIGIN = Object.freeze({ x: 0, y: 0 });
const SQUARE_SIZE = 20;
const ZOOM_SENSITIVITY = 500; // bigger for lower zoom per scroll
const MAX_SCALE = 50;
const MIN_SCALE = 0.1;

// dom
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
const debugDiv = document.getElementById("debug");

// "props"
const initialScale = 0.75;
const initialOffset = { x: 10, y: 20 };

// "state"
let mousePos = ORIGIN;
let lastMousePos = ORIGIN;
let offset = initialOffset;
let scale = initialScale;

// when setting up canvas, set width/height to devicePixelRation times normal
const { devicePixelRatio = 1 } = window;
context.canvas.width = context.canvas.width * devicePixelRatio;
context.canvas.height = context.canvas.height * devicePixelRatio;

function draw() {
    window.requestAnimationFrame(draw);

    // clear canvas
    context.canvas.width = context.canvas.width;

    // transform coordinates - scale multiplied by devicePixelRatio
    context.scale(scale * devicePixelRatio, scale * devicePixelRatio);
    context.translate(offset.x, offset.y);

    // draw
    context.fillRect(200 + -SQUARE_SIZE / 2, 50 + -SQUARE_SIZE / 2, SQUARE_SIZE, SQUARE_SIZE);

    // debugging
    context.beginPath();
    context.moveTo(0, 0);
    context.lineTo(0, 50);
    context.moveTo(0, 0);
    context.lineTo(50, 0);
    context.stroke();
    // debugDiv.innerText = `scale: ${scale}
    // mouse: ${JSON.stringify(mousePos)}
    // offset: ${JSON.stringify(offset)}
    // `;
}

// calculate mouse position on canvas relative to top left canvas point on page
function calculateMouse(event, canvas) {
    const viewportMousePos = { x: event.pageX, y: event.pageY };
    const boundingRect = canvas.getBoundingClientRect();
    const topLeftCanvasPos = { x: boundingRect.left, y: boundingRect.top };
    return diffPoints(viewportMousePos, topLeftCanvasPos);
}

// zoom
function handleWheel(event) {
    event.preventDefault();

    // update mouse position
    const newMousePos = calculateMouse(event, canvas);
    lastMousePos = mousePos;
    mousePos = newMousePos;

    // calculate new scale/zoom
    const zoom = 1 - event.deltaY / ZOOM_SENSITIVITY;
    const newScale = scale * zoom;
    if (MIN_SCALE > newScale || newScale > MAX_SCALE) {
        return;
    }

    // offset the canvas such that the point under the mouse doesn't move
    const lastMouse = scalePoint(mousePos, scale);
    const newMouse = scalePoint(mousePos, newScale);
    const mouseOffset = diffPoints(lastMouse, newMouse);
    offset = diffPoints(offset, mouseOffset);
    scale = newScale;
}
canvas.addEventListener("wheel", handleWheel);

// panning
const mouseMove = (event) => {
    // update mouse position
    const newMousePos = calculateMouse(event, canvas);
    lastMousePos = mousePos;
    mousePos = newMousePos;
    const mouseDiff = scalePoint(diffPoints(mousePos, lastMousePos), scale);
    offset = addPoints(offset, mouseDiff);
};
const mouseUp = () => {
    document.removeEventListener("mousemove", mouseMove);
    document.removeEventListener("mouseup", mouseUp);
};
const startPan = (event) => {
    document.addEventListener("mousemove", mouseMove);
    document.addEventListener("mouseup", mouseUp);
    // set initial mouse position in case user hasn't moved mouse yet
    mousePos = calculateMouse(event, canvas);
};
canvas.addEventListener("mousedown", startPan);

// repeatedly redraw
window.requestAnimationFrame(draw);
#canvas {
  /*set fixed width and height for what you actually want in css!*/
  /*should be the same as what's passed into canvas element*/
  width: 500px;
  height: 150px;

  position: fixed;
  border: 2px solid black;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}
<!DOCTYPE html>

<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <link rel="stylesheet" href="styles.css" />
</head>

<body>
<!--still need width and height here, same as css-->
<canvas id="canvas" width="500" height="150"></canvas>
<div id="debug"></div>
<script type="module" src="pan_zoom.js"></script>
</body>
</html>

스크롤 to(x,y) 기능을 사용하여 스크롤 바의 위치를 줌 후 표시해야 할 지점까지 바로 처리할 수 있습니다.마우스 사용 위치 찾기.clientX 및 event.clientY.이것이 당신에게 도움이 될 것입니다.

여기에 제가 사용하는 접근 방식이 있습니다. 그림을 그리는 방법에 대한 통제를 강화하기 위해 사용됩니다.

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

var scale = 1;
var xO = 0;
var yO = 0;

draw();

function draw(){
    // Clear screen
    ctx.clearRect(0, 0, canvas.offsetWidth, canvas.offsetHeight);

    // Original coordinates
    const xData = 50, yData = 50, wData = 100, hData = 100;
    
    // Transformed coordinates
    const x = xData * scale + xO,
     y = yData * scale + yO,
     w = wData * scale,
     h = hData * scale;

    // Draw transformed positions
    ctx.fillStyle = "black";
    ctx.fillRect(x,y,w,h);
}

canvas.onwheel = function (e){
    e.preventDefault();

    const r = canvas.getBoundingClientRect(),
      xNode =  e.pageX - r.left,
      yNode =  e.pageY - r.top;

    const newScale = scale * Math.exp(-Math.sign(e.deltaY) * 0.2),
      scaleFactor = newScale/scale;

    xO = xNode - scaleFactor * (xNode - xO);
    yO = yNode - scaleFactor * (yNode - yO);
    scale = newScale;

    draw();
}
<canvas id="canvas" width="600" height="200"></canvas>

C# & WPF에서 제게 도움이 된 답변 추가:

double zoom = scroll > 0 ? 1.2 : (1/1.2);

var CursorPosCanvas = e.GetPosition(Canvas);
pan.X += -(CursorPosCanvas.X - Canvas.RenderSize.Width / 2.0 - pan.X) * (zoom - 1.0);
pan.Y += -(CursorPosCanvas.Y - Canvas.RenderSize.Height / 2.0 - pan.Y) * (zoom - 1.0);

transform.ScaleX *= zoom;
transform.ScaleY *= zoom;

언급URL : https://stackoverflow.com/questions/2916081/zoom-in-on-a-point-using-scale-and-translate

반응형