代码之家  ›  专栏  ›  技术社区  ›  Tareq Jami

使用鼠标(坐标系)放大我的canva

  •  1
  • Tareq Jami  · 技术社区  · 6 年前

    我正在开发一个webapp,它包括一个部分,我在其中绘制一个函数的图形,坐标系是由画布制成的。问题是,我无法放大坐标系。我想让它能够放大和缩小+使用鼠标移动坐标系。放大/缩小时,x和y值也应增大/减小。

    谁能帮我一下吗?

    我寻找了一些解决方案,但没有找到任何有用的。这就是为什么我决定在这里问这个问题。

    以下是我的代码:

    <canvas id="myCanvas" width="300" height="300" style="border:1px solid #d3d3d3;"></canvas>
    
    <!--Canva startup-->
    <script>
        // Setup values
        var height = 300;
        var width = 300;
        var zoomFactor = 15;
    
        // --------
        var c = document.getElementById("myCanvas");
        var xZero = width / 2;
        var yZero = height / 2;
        var ctx = c.getContext("2d");
    
        // Draw Cord-System-Grid
        ctx.beginPath();
        ctx.moveTo(xZero, 0);
        ctx.lineTo(xZero, height);
        ctx.strokeStyle = "#000000";
        ctx.stroke();
        ctx.moveTo(0, yZero);
        ctx.lineTo(width, yZero);
        ctx.strokeStyle = "#000000";
        ctx.stroke();
        ctx.beginPath();
    
        // Draw Numbers
        ctx.font = "10px Georgia";
        var heightTextX = yZero + 10;
        for(var i = 0; i < width; i = i + width / 10) {
            var numberX = (-1 * xZero / zoomFactor) + i / zoomFactor;  
            ctx.fillText(numberX, i, heightTextX);
        }
    
        var heightTextY = yZero + 10;
        for(var n = 0; n < height; n = n + height / 10) {
            var numberY = (-1 * yZero / zoomFactor) + n / zoomFactor;
            if(numberY !== 0)
                ctx.fillText(numberY * -1, heightTextY, n);
        }
    
    </script>
    
    1 回复  |  直到 6 年前
        1
  •  1
  •   TerrySu    6 年前

    我以前也对这个问题感到困惑,但最终还是解决了。
    事实上,我们必须 嘲弄 画布 查看端口 支持缩放和平移的。
    这里,我列出了缩放和平移的解决方案。
    你可以 以下代码可查看 后果 (用鼠标放大或缩小)首先不要分析代码的细节,如果这是你想要的,那么你可以在代码中找到答案。

    class ViewPort {
        constructor(canvas) {
          this.canvas = canvas
    
          /**
            * Point used to calculate the change of every point's position on
            * canvas after view port is zoomed and panned
            */
          this.center = this.basicCenter
    
          this.zoom = 1
    
          this.shouldPan = false
          this.prevZoomingPoint = null
        }
    
        get canvasWidth() {
          return this.canvas.getBoundingClientRect().width
        }
    
        get canvasHeight() {
          return this.canvas.getBoundingClientRect().height
        }
    
        get canvasLeft() {
          return this.canvas.getBoundingClientRect().left
        }
    
        get canvasTop() {
          return this.canvas.getBoundingClientRect().top
        }
    
        get context() {
          return this.canvas.getContext('2d')
        }
    
        get basicCenter() {
          const { canvasWidth, canvasHeight } = this
    
          const point = {
            x: canvasWidth / 2,
            y: canvasHeight / 2
          }
          return point
        }
    
        get basicWidth() {
          const width = this.canvasWidth
          return width
        }
    
        get basicHeight() {
          const height = this.canvasHeight
          return height
        }
    
        get width() {
          const { basicWidth, zoom } = this
          const width = basicWidth * zoom
          return width
        }
    
        get height() {
          const { basicHeight, zoom } = this
          const height = basicHeight * zoom
          return height
        }
    
        get movement() {
          const { width, height, basicWidth, basicHeight } = this
          const { x: cx, y: cy } = this.center
          const { x: basicCX, y: basicCY } = this.basicCenter
    
          const deltaX = cx - basicCX - ((width - basicWidth) / 2)
          const deltaY = cy - basicCY - ((height - basicHeight) / 2)
          const res = {
            x: deltaX,
            y: deltaY
          }
    
          return res
        }
    
        get pan() {
          const { center, zoom, basicCenter } = this
          const res = {
            x: center.x - basicCenter.x,
            y: center.y - basicCenter.y
          }
          return res
        }
    
        zoomBy(center, deltaZoom) {
          const prevZoom = this.zoom
    
          this.zoom = this.zoom + deltaZoom
    
          this.center = this.zoomPoint(center, this.zoom / prevZoom, this.center)
        }
    
        zoomIn(point) {
          this.zoomBy(point, 0.1)
        }
    
        zoomOut(point) {
          this.zoom > 0.25 && this.zoomBy(point, -0.1)
        }
    
        zoomPoint(center, rate, point) {
          const { x: cx, y: cy } = center
          const { x, y } = point
    
          const deltaX = (x - cx) * rate
          const deltaY = (y - cy) * rate
    
          const newPoint = {
            x: cx + deltaX,
            y: cy + deltaY
          }
          return newPoint
        }
    
        panBy(deltaX, deltaY) {
          const { x: centerX, y: centerY } = this.center
          this.center = {
            x: centerX + deltaX,
            y: centerY + deltaY
          }
        }
    
        getDeltaPointToPrevPanningPoint(point) {
          const { x, y } = point
          const { x: prevX, y: prevY } = this.prevZoomingPoint
    
          const deltaPoint = {
            x: x - prevX,
            y: y - prevY
          }
          return deltaPoint
        }
    
    
        startPan(event) {
          const point = {
            x: event.x - this.canvasLeft,
            y: event.y - this.canvasTop,
          }
    
          this.shouldPan = true
    
          this.prevZoomingPoint = point
        }
    
        panning(event) {
          const point = {
            x: event.x - this.canvasLeft,
            y: event.y - this.canvasTop,
          }
    
          const deltaX = this.getDeltaPointToPrevPanningPoint(point).x
          const deltaY = this.getDeltaPointToPrevPanningPoint(point).y
    
          this.prevZoomingPoint = point
    
          this.panBy(deltaX, deltaY)
        }
    
        stopPan() {
          this.shouldPan = false
        }
    
        transformToInitial(point) {
          const { x, y } = point
          const { movement, zoom } = this
          const res = {
            x: (x - movement.x) / zoom,
            y: (y - movement.y) / zoom
          }
          return res
        }
    
        transform(point) {
          const { x, y } = point
          const { movement, zoom } = this
          const res = {
            x: x * zoom + movement.x,
            y: y * zoom + movement.y
          }
          return res
        }
    
        clearCanvas() {
          this.context.setTransform(1, 0, 0, 1, 0, 0)
          this.context.clearRect(
            0,
            0,
            viewPort.canvasWidth,
            viewPort.canvasHeight
          )
        }
      }
    
      class Interaction {
        constructor({
          canvas,
          viewPort,
          dragger
        }) {
    
          canvas.removeEventListener("mousewheel", mousewheelListener)
          canvas.addEventListener("mousewheel", mousewheelListener)
    
          canvas.removeEventListener("mousedown", mousedownListener)
          canvas.addEventListener("mousedown", mousedownListener)
    
          canvas.removeEventListener("mousemove", mousemoveListener)
          canvas.addEventListener("mousemove", mousemoveListener)
    
          canvas.removeEventListener("mouseup", mouseupListener)
          canvas.addEventListener("mouseup", mouseupListener)
    
    
          function mousewheelListener(event) {
            event.preventDefault()
    
            const point = {
              x: event.x - canvas.getBoundingClientRect().left,
              y: event.y - canvas.getBoundingClientRect().top,
            }
    
            const { deltaX, deltaY } = event
    
            if (isDecreasing()) {
              viewPort.zoomIn(point)
            }
    
            if (isIncreasing()) {
              viewPort.zoomOut(point)
            }
    
            function isIncreasing() {
              const res = deltaX > 0 || deltaY > 0
              return res
            }
            function isDecreasing() {
              const res = deltaX < 0 || deltaY < 0
              return res
            }
    
            render()
    
          }
    
    
          function mousedownListener(event) {
            viewPort.startPan(event)
          }
    
          function mousemoveListener(event) {
            viewPort.shouldPan && viewPort.panning(event)
            viewPort.shouldPan && render()
          }
    
          function mouseupListener(event) {
            viewPort.stopPan(event)
          }
        }
    
      }
      const canvas = document.getElementById("myCanvas")
      const viewPort = new ViewPort(canvas)
      const interaction = new Interaction({ viewPort, canvas })
    
      function render() {
        const { abs, max } = Math
        const { zoom, movement, context: ctx, pan, center, basicCenter } = viewPort
    
        viewPort.clearCanvas()
        ctx.setTransform(zoom, 0, 0, zoom, movement.x, movement.y)
    
    
        // Original codes are rewrote
        const { canvasWidth, canvasHeight } = viewPort
    
        const interval = 20
        const basicWidth = canvasWidth
        const basicHeight = canvasHeight
    
        const potentialWidth = 2 * max(abs(viewPort.transformToInitial({ x: 0, y: 0 }).x - basicCenter.x), abs(viewPort.transformToInitial({ x: basicWidth, y: 0 }).x - basicCenter.x))
        const width = potentialWidth > basicWidth ? potentialWidth : basicWidth
    
        const potentialHeight = 2 * max(abs(viewPort.transformToInitial({ x: 0, y: 0 }).y - basicCenter.y), abs(viewPort.transformToInitial({ x: 0, y: basicHeight }).y - basicCenter.y))
        const height = potentialHeight > basicHeight ? potentialHeight : basicHeight
    
        drawXAxis()
        drawYAxis()
        drawOriginCoordinate()
        drawXCoordinates()
        drawYCoordinates()
    
        function drawXAxis() {
          const path = new Path2D
    
          path.moveTo(basicCenter.x - width / 2, basicHeight / 2)
          path.lineTo(basicCenter.x + width / 2, basicHeight / 2)
    
          ctx.stroke(path)
        }
    
        function drawYAxis() {
          const path = new Path2D
          path.moveTo(basicWidth / 2, basicCenter.y - height / 2)
          path.lineTo(basicWidth / 2, basicCenter.y + height / 2)
    
          ctx.stroke(path)
        }
    
        function drawOriginCoordinate() {
          ctx.fillText(`O`, basicCenter.x + 5, basicCenter.y - 5)
        }
    
        function drawXCoordinates() {
          for (let i = 1; i <= width / 2 / interval; i++) {
            total = i * interval
            ctx.fillText(` ${i} `, basicCenter.x + total, basicHeight / 2)
          }
    
          for (let i = 1; i <= width / 2 / interval; i++) {
            total = i * interval
            ctx.fillText(` -${i} `, basicCenter.x - total, basicHeight / 2)
          }
        }
    
        function drawYCoordinates() {
          for (let i = 1; i <= height / 2 / interval; i++) {
            total = i * interval
            ctx.fillText(` ${i} `, basicWidth / 2, basicCenter.y + total)
          }
    
          for (let i = 1; i <= height / 2 / interval; i++) {
            total = i * interval
            ctx.fillText(` -${i} `, basicWidth / 2, basicCenter.y - total)
          }
        }
      }
    
      render()
    <canvas id="myCanvas" width="300" height="300" style="border:1px solid #d3d3d3;"></canvas>