web-dev-qa-db-fra.com

Zoom du curseur du canevas à la souris

Je programme un projet HTML5 <canvas> qui implique un zoom avant et arrière sur les images à l'aide de la molette de défilement. Je veux zoomer vers le curseur comme le fait Google Maps, mais je suis complètement perdu sur la façon de calculer les mouvements.

Ce que j'ai: image x et y (coin supérieur gauche); largeur et hauteur de l'image; curseur x et y par rapport au centre du canevas.

64
S2am

En bref, vous voulez translate() le contexte du canevas par votre décalage, scale() pour zoomer ou dézoomer, puis translate() en arrière à l'opposé de la souris décalage. Notez que vous devez transformer la position du curseur de l'espace d'écran dans le contexte de canevas transformé.

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

Démo: http://phrogz.net/tmp/canvas_zoom_to_cursor.html

J'ai mis un exemple de travail complet sur mon site Web pour que vous puissiez l'examiner, prendre en charge le glissement, cliquer pour zoomer, cliquer-déplacer vers l'arrière ou faire défiler la molette vers le haut/vers le bas.

Le seul problème (actuel) est que Safari zoome trop vite par rapport à Chrome ou Firefox.

211
Phrogz

J'espère que ces bibliothèques JS vous aideront: (HTML5, JS)

  1. Loupe

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

  1. CanvasZoom

https://github.com/akademy/CanvasZoom

  1. Scroller

https://github.com/zynga/scroller

Quant à moi, j'utilise de la loupe. C'est génial! Pour vous le meilleur cas - scroller.

15
Alexey

J'ai récemment dû archiver les mêmes résultats que Phrogz l'avait déjà fait, mais au lieu d'utiliser context.scale(), j'ai calculé chaque taille d'objet en fonction du ratio.

C'est ce que j'ai trouvé. La logique derrière c'est très simple. Avant la mise à l'échelle, je calcule la distance en points à partir d'Edge en pourcentages et ajuste plus tard la fenêtre d'affichage à l'emplacement correct.

Il m'a fallu un certain temps pour le proposer, j'espère que cela fera gagner du temps à quelqu'un.

$(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>
7
Kristian

J'ai pris la réponse de @ Phrogz comme base et créé une petite bibliothèque qui permet le canevas avec glisser, zoomer et tourner. Voici l'exemple.

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

Vous pouvez trouver des exemples plus détaillés et de la documentation sur le projet page

6
vogdb