JavaScript / холст, масштабирование точки стиля карты

1

У меня есть холст с кучей объектов. У меня есть функция масштабирования, которая набирает zoom переменную, по которой умножается каждая координата.

Я хочу иметь возможность указывать на координаты и приближать к этому, подобно тому, как это делают онлайн-карты.

Я имею установку демо здесь, текущего состояния моего кода.

Теги:
canvas

1 ответ

2
Лучший ответ

Масштаб по координате

Если задана координата экрана, которая имеет масштабированный контент, вам нужно переместить начало координат в сторону или от нее в размере, соответствующем масштабированию.

Если вы увеличиваете масштаб координат, движение перемещается в направлении мыши. и уменьшить масштаб.

Таким образом, мышь получим позицию 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;
}
<canvas id="canvas"></canvas>
  • 0
    Выглядит неплохо! ctx.setTransform() ли ctx.setTransform() от ctx.scale() ? Первоначально у меня был метод с ctx.scale() но, похоже, он масштабировался и ctx.scale() качество.
  • 0
    @meganukebmp setTransform - это просто более прямой способ установки текущего преобразования, чем использование масштабирования, поворота или преобразования. Также setTransform устанавливает текущее преобразование, в то время как другие методы применяются к текущему преобразованию.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню