【问题标题】:javascript/canvas, map style point zoomingjavascript/canvas,地图样式点缩放
【发布时间】:2017-08-06 12:12:52
【问题描述】:

我有一个带有一堆对象的画布。我有一个缩放功能,它会增加一个 zoom 变量,每个坐标都乘以该变量。

我希望能够指向一个坐标并朝那个方向缩放,类似于在线地图的做法。

我已经设置了我的代码当前状态的演示 here

【问题讨论】:

    标签: javascript canvas


    【解决方案1】:

    在坐标处缩放

    如果给定的屏幕坐标具有缩放的内容,您需要将原点移向或远离该点,移动量与缩放相匹配。

    如果您放大原点,则会向鼠标的位置移动。并缩小。

    所以用鼠标获得 x,y 位置,并根据滚轮方向放大或缩小量

    // e is the mouse wheel event
    const x = e.offsetX;
    const y = e.offsetY;
    const amount = e.wheelDelta > 0 ?  1.1 : 1 / 1.1; 
    

    然后将其应用于当前比例并移动原点以匹配

    scale *= amount;  // the new scale
    // move the origin
    origin.x = x - (x - origin.x) * amount;
    origin.y = y - (y - origin.y) * amount;
    

    然后您可以设置 2D 位置和比例

    ctx.setTransform(scale, 0, 0, scale, origin.x, origin.y);
    

    示例

    下面是一个简单的视图示例,使用您的小提琴中的一些代码。

    对象view 跟踪当前视图并维护一个可应用于画布的矩阵。

    const ctx = canvas.getContext("2d");
    canvas.width = 500;
    canvas.height = 500;
    const randI = (min, max = min + (min = 0)) => (Math.random() * (max - min) + min) | 0;
    const rand = (min, max = min + (min = 0)) => Math.random() * (max - min) + min;
    
    const objects = [];
    for (let i = 0; i < 100; i++) {
      objects.push({
        x: rand(canvas.width),
        y: rand(canvas.height),
        w: rand(40),
        h: rand(40),
        col: `rgb(${randI(255)},${randI(255)},${randI(255)})`,
      });
    }
    canvas.addEventListener("mousewheel", onmousewheel, false);
    canvas.addEventListener("DOMMouseScroll", onmousewheel, false);
    requestAnimationFrame(drawCanvas); // this will call drawcanvas after all other code has run
    
    const view = (() => {
      const matrix = [1, 0, 0, 1, 0, 0]; // current view transform
      var m = matrix; // alias for clear code
      var scale = 1; // current scale
      var ctx; // reference to the 2D context
      const pos = { x: 0, y: 0 }; // current position of origin
      var dirty = true;
      const API = {
        setContext(_ctx) { ctx = _ctx; dirty = true },
        apply() {
          if (dirty) { this.update() }
          ctx.setTransform(m[0], m[1], m[2], m[3], m[4], m[5])
        },
        getScale() { return scale },
        getPosition() { return pos },
        isDirty() { return dirty },
        update() {
          dirty = false;
          m[3] = m[0] = scale;
          m[2] = m[1] = 0;
          m[4] = pos.x;
          m[5] = pos.y;
        },
        scaleAt(at, amount) { // at in screen coords
          if (dirty) { this.update() }
          scale *= amount;
          pos.x = at.x - (at.x - pos.x) * amount;
          pos.y = at.y - (at.y - pos.y) * amount;
          dirty = true;
        },
      };
      return API;
    })();
    view.setContext(ctx);
    
    function drawCanvas() {
      if (view.isDirty()) { // has the view changed, then draw all
        ctx.setTransform(1, 0, 0, 1, 0, 0); // default transform for clear
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        view.apply(); // set the 2D context transform to the view
        for (i = 0; i < objects.length; i++) {
          var obj = objects[i];
          ctx.fillStyle = obj.col;
          ctx.fillRect(obj.x, obj.y, obj.h, obj.h);
        }
      }
    
      requestAnimationFrame(drawCanvas);
    }
    
    function onmousewheel(event) {
      var e = window.event || event;
      var x = e.offsetX;
      var y = e.offsetY;
      const delta = e.type === "mousewheel" ? e.wheelDelta : -e.detail;
      if (delta > 0) { view.scaleAt({x, y}, 1.1) }
      else { view.scaleAt({x, y}, 1 / 1.1) }
      e.preventDefault();
    }
    #canvas {
      border: 2px solid;
    }
    &lt;canvas id="canvas"&gt;&lt;/canvas&gt;

    【讨论】:

    • 看起来不错! ctx.setTransform()ctx.scale() 不同吗?最初我有一个使用 ctx.scale() 的方法,但它似乎规模化且质量松散。
    • @meganukebmp setTransform 只是设置当前转换的一种比使用缩放、旋转或平移更直接的方法。还 setTransform 设置当前变换,而其他方法应用于当前变换。
    • 啊,我明白了。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-03-29
    • 2012-05-18
    • 2014-12-05
    • 2017-08-14
    • 1970-01-01
    • 2022-12-06
    • 1970-01-01
    相关资源
    最近更新 更多