Javascript 将画布缩放到鼠标光标

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5189968/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 16:04:50  来源:igfitidea点击:

Zoom Canvas to Mouse Cursor

javascripthtmlcanvaszoomscrollwheel

提问by S2am

I'm programming a HTML5 < canvas > project that involves zooming in and out of images using the scroll wheel. I want to zoom towards the cursor like google maps does but I'm completely lost on how to calculate the movements.

我正在编写一个 HTML5 <canvas > 项目,该项目涉及使用滚轮放大和缩小图像。我想像谷歌地图一样放大光标,但我完全不知道如何计算运动。

What I have: image x and y (top-left corner); image width and height; cursor x and y relative to the center of the canvas.

我所拥有的:图像 x 和 y(左上角);图像宽度和高度;相对于画布中心的光标 x 和 y。

回答by Phrogz

In short, you want to translate()the canvas context by your offset, scale()it to zoom in or out, and then translate()back by the opposite of the mouse offset. Note that you need to transform the cursor position from screen space into the transformed canvas context.

简而言之,您希望translate()通过偏移scale()来放大或缩小画布上下文,然后translate()通过鼠标偏移的相反位置返回。请注意,您需要将光标位置从屏幕空间转换为转换后的画布上下文。

ctx.translate(pt.x,pt.y);
ctx.scale(factor,factor);
ctx.translate(-pt.x,-pt.y);

Demo: http://phrogz.net/tmp/canvas_zoom_to_cursor.html

演示:http: //phrogz.net/tmp/canvas_zoom_to_cursor.html

I've put up a full working exampleon my website for you to examine, supporting dragging, click to zoom in, shift-click to out, or scroll wheel up/down.

我在我的网站上提供了一个完整的工作示例供您检查、支持拖动、单击放大、按住 shift 单击缩小或向上/向下滚轮。

The only (current) issue is that Safari zooms too fastcompared to Chrome or Firefox.

唯一的(当前)问题是与 Chrome 或 Firefox 相比,Safari 的缩放速度太快

回答by Alexey

I hope, these JS libraries will help you: (HTML5, JS)

我希望,这些 JS 库会帮助你:(HTML5,JS)

  1. Loupe
  1. 放大镜

http://www.netzgesta.de/loupe/

http://www.netzgesta.de/loupe/

  1. CanvasZoom
  1. 画布缩放

https://github.com/akademy/CanvasZoom

https://github.com/akademy/CanvasZoom

  1. Scroller
  1. 滚动条

https://github.com/zynga/scroller

https://github.com/zynga/scroller

As for me, I'm using loupe. It's awesome! For you the best case - scroller.

至于我,我正在使用放大镜。这很棒!对你来说最好的情况 - 滚动条。

回答by Kristian

I recently needed to archive same results as Phrogz had already done but instead of using context.scale(), I calculated each object size based on ratio.

我最近需要存档与 Phrogz 已经完成的相同的结果,但context.scale()我没有使用,而是根据比率计算每个对象的大小。

This is what I came up with. Logic behind it is very simple. Before scaling, I calculate point distance from edge in percentages and later adjust viewport to correct place.

这就是我想出的。其背后的逻辑非常简单。在缩放之前,我以百分比计算与边缘的点距离,然后将视口调整到正确的位置。

It took me quite a while to come up with it, hope it saves someones time.

我花了很长时间才想出它,希望它可以节省某人的时间。

$(function () {
  var canvas = $('canvas.main').get(0)
  var canvasContext = canvas.getContext('2d')

  var ratio = 1
  var vpx = 0
  var vpy = 0
  var vpw = window.innerWidth
  var vph = window.innerHeight

  var orig_width = 4000
  var orig_height = 4000

  var width = 4000
  var height = 4000

  $(window).on('resize', function () {
    $(canvas).prop({
      width: window.innerWidth,
      height: window.innerHeight,
    })
  }).trigger('resize')

  $(canvas).on('wheel', function (ev) {
    ev.preventDefault() // for stackoverflow

    var step

    if (ev.originalEvent.wheelDelta) {
      step = (ev.originalEvent.wheelDelta > 0) ? 0.05 : -0.05
    }

    if (ev.originalEvent.deltaY) {
      step = (ev.originalEvent.deltaY > 0) ? 0.05 : -0.05
    }

    if (!step) return false // yea..

    var new_ratio = ratio + step
    var min_ratio = Math.max(vpw / orig_width, vph / orig_height)
    var max_ratio = 3.0

    if (new_ratio < min_ratio) {
      new_ratio = min_ratio
    }

    if (new_ratio > max_ratio) {
      new_ratio = max_ratio
    }

    // zoom center point
    var targetX = ev.originalEvent.clientX || (vpw / 2)
    var targetY = ev.originalEvent.clientY || (vph / 2)

    // percentages from side
    var pX = ((vpx * -1) + targetX) * 100 / width
    var pY = ((vpy * -1) + targetY) * 100 / height

    // update ratio and dimentsions
    ratio = new_ratio
    width = orig_width * new_ratio
    height = orig_height * new_ratio

    // translate view back to center point
    var x = ((width * pX / 100) - targetX)
    var y = ((height * pY / 100) - targetY)

    // don't let viewport go over edges
    if (x < 0) {
      x = 0
    }

    if (x + vpw > width) {
      x = width - vpw
    }

    if (y < 0) {
      y = 0
    }

    if (y + vph > height) {
      y = height - vph
    }

    vpx = x * -1
    vpy = y * -1
  })

  var is_down, is_drag, last_drag

  $(canvas).on('mousedown', function (ev) {
    is_down = true
    is_drag = false
    last_drag = { x: ev.clientX, y: ev.clientY }
  })

  $(canvas).on('mousemove', function (ev) {
    is_drag = true

    if (is_down) {
      var x = vpx - (last_drag.x - ev.clientX)
      var y = vpy - (last_drag.y - ev.clientY)

      if (x <= 0 && vpw < x + width) {
        vpx = x
      }

      if (y <= 0 && vph < y + height) {
        vpy = y
      }

      last_drag = { x: ev.clientX, y: ev.clientY }
    }
  })

  $(canvas).on('mouseup', function (ev) {
    is_down = false
    last_drag = null

    var was_click = !is_drag
    is_drag = false

    if (was_click) {

    }
  })

  $(canvas).css({ position: 'absolute', top: 0, left: 0 }).appendTo(document.body)

  function animate () {
    window.requestAnimationFrame(animate)

    canvasContext.clearRect(0, 0, canvas.width, canvas.height)

    canvasContext.lineWidth = 1
    canvasContext.strokeStyle = '#ccc'

    var step = 100 * ratio

    for (var x = vpx; x < width + vpx; x += step) {
      canvasContext.beginPath()
      canvasContext.moveTo(x, vpy)
      canvasContext.lineTo(x, vpy + height)
      canvasContext.stroke()
    }
    for (var y = vpy; y < height + vpy; y += step) {
      canvasContext.beginPath()
      canvasContext.moveTo(vpx, y)
      canvasContext.lineTo(vpx + width, y)
      canvasContext.stroke()
    }

    canvasContext.strokeRect(vpx, vpy, width, height)

    canvasContext.beginPath()
    canvasContext.moveTo(vpx, vpy)
    canvasContext.lineTo(vpx + width, vpy + height)
    canvasContext.stroke()

    canvasContext.beginPath()
    canvasContext.moveTo(vpx + width, vpy)
    canvasContext.lineTo(vpx, vpy + height)
    canvasContext.stroke()

    canvasContext.restore()
  }

  animate()
})
<!DOCTYPE html>
<html>
<head>
 <title></title>
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</head>
<body>
 <canvas class="main"></canvas>
</body>
</html>

回答by vogdb

I took @Phrogz's answer as a basis and made a small library that enables canvas with dragging, zooming and rotating. Here is the example.

我以@Phrogz 的回答为基础,制作了一个小型库,使画布能够拖动、缩放和旋转。这是示例。

var canvas = document.getElementById('canvas')
//assuming that @param draw is a function where you do your main drawing.
var control = new CanvasManipulation(canvas, draw)
control.init()
control.layout()
//now you can drag, zoom and rotate in canvas

You can find more detailed examples and documentation on the project's page

您可以在项目页面上找到更详细的示例和文档