Zoom Canvas to Mouse Cursor

后端 未结 4 1874
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-11-22 15:03

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

相关标签:
4条回答
  • 2020-11-22 15:42

    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.

    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

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

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

    0 讨论(0)
  • 2020-11-22 15:49

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

    1. Loupe

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

    1. CanvasZoom

    https://github.com/akademy/CanvasZoom

    1. Scroller

    https://github.com/zynga/scroller

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

    0 讨论(0)
  • 2020-11-22 15:49

    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.

    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>

    0 讨论(0)
  • 2020-11-22 15:53

    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.

    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

    0 讨论(0)
提交回复
热议问题