将画布缩放到鼠标光标

S2a*_*2am 66 javascript html5 scrollwheel canvas zoom

我正在编写一个HTML5 <canvas>项目,该项目涉及使用滚轮放大和缩小图像.我想像谷歌地图一样向光标缩放,但我完全迷失了如何计算动作.

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

Phr*_*ogz 216

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

ctx.translate(pt.x,pt.y);
ctx.scale(factor,factor);
ctx.translate(-pt.x,-pt.y);
Run Code Online (Sandbox Code Playgroud)

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

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

唯一(当前)问题是Safari与Chrome或Firefox相比缩放得太快.

  • 这个例子很好。谢谢! (3认同)
  • 哇,@ phrogz,你超越了! (3认同)
  • 嘿@Phrogz,这太棒了!我只是想知道我们是否可以限制拖动,以便无法将图像拖出边界。如果没有更多图像可供拖动,则拖动应该停止在那里,而不是允许无限期地拖动。我尝试了一下,但似乎我无法正确计算:-( (3认同)
  • @Yasir是的.这是代码.你可以做任何事情. (3认同)
  • @Christoph 这很容易。获取比例 - 您可以从以下位置获取比例:var scale = ctx.getTransform().a; 然后取图像的当前左上角位置: var curX = ctx.getTransform().e; var curY = ctx.getTransform().f; 估计位置的变化:var deltaX = pt.x - dragStart.x;var deltaY = pt.y - dragStart.y; 然后是原始图像大小,让我们以宽度为例(当 scale=1 时):imageW 并且有画布宽度:canvasW 那么条件应该为假以允许拖动:curX + deltaX + imageW * scale &lt; canvasW 和一个( curX + deltaX &gt; 0 || curY + deltaY &gt; 0) (2认同)
  • 这很难通过手势在移动设备上实现吗?像,允许 2 指捏只放大图像而不是整个网站? (2认同)

Ale*_*xey 15

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

  1. 放大镜

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

  1. CanvasZoom

https://github.com/akademy/CanvasZoom

  1. 滚轮

https://github.com/zynga/scroller

至于我,我正在使用放大镜.这很棒!对你来说最好的情况 - 卷轴.


Kri*_*ian 8

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

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

我花了很长时间来提出它,希望它节省了一些人的时间.

$(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()
})
Run Code Online (Sandbox Code Playgroud)
<!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>
Run Code Online (Sandbox Code Playgroud)


vog*_*gdb 6

我以@ 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
Run Code Online (Sandbox Code Playgroud)

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


Bli*_*n67 5

快点

与多个矩阵调用相比,使用, ,ctx.setTransform可以提供更高的性能。ctx.translatectx.scalectx.translate

不需要复杂的变换反转,因为昂贵的 DOM 矩阵调用可以在缩放坐标系和屏幕坐标系之间转换点。

灵活的

灵活性,因为您不需要使用ctx.save并且ctx.restore如果您使用不同的转换来渲染内容。返回到转换而ctx.setTransform不是潜在的帧速率破坏ctx.restore调用

轻松反转变换并获取(屏幕)像素位置的世界坐标,反之亦然。

例子

使用鼠标和鼠标滚轮在鼠标位置进行放大和缩小

使用此方法通过 CSS 变换在某个点(鼠标)缩放页面内容的示例答案底部的 CSS 演示也包含下一个示例中的演示的副本。

该方法的一个示例用于使用 setTransform 在某个点缩放画布内容

如何

给定比例和像素位置,您可以获得新的比例,如下所示......

const origin = {x:0, y:0};         // canvas origin
var scale = 1;                     // current scale
function scaleAt(x, y, scaleBy) {  // at pixel coords x, y scale by scaleBy
    scale *= scaleBy;
    origin.x = x - (x - origin.x) * scaleBy;
    origin.y = y - (y - origin.y) * scaleBy;
}
Run Code Online (Sandbox Code Playgroud)

定位画布并绘制内容

ctx.setTransform(scale, 0, 0, scale, origin.x, origin.y);
ctx.drawImage(img, 0, 0);
Run Code Online (Sandbox Code Playgroud)

如果您有鼠标坐标即可使用

const zoomBy = 1.1;                    // zoom in amount
scaleAt(mouse.x, mouse.y, zoomBy);     // will zoom in at mouse x, y
scaleAt(mouse.x, mouse.y, 1 / zoomBy); // will zoom out by same amount at mouse x,y
Run Code Online (Sandbox Code Playgroud)

恢复默认变换

ctx.setTransform(1,0,0,1,0,0);
Run Code Online (Sandbox Code Playgroud)

倒转

获取缩放坐标系中点的坐标以及缩放坐标系中点的屏幕位置

屏幕通向世界

function toWorld(x, y) {  // convert to world coordinates
    x = (x - origin.x) / scale;
    y = (y - origin.y) / scale;
    return {x, y};
}
Run Code Online (Sandbox Code Playgroud)

屏幕上的世界

function toScreen(x, y) {
    x = x * scale + origin.x;
    y = y * scale + origin.y;
    return {x, y};
}
Run Code Online (Sandbox Code Playgroud)