Страницы

Поиск по вопросам

воскресенье, 1 декабря 2019 г.

Ограничить движение объекта в границах определённой формы

#javascript #html #css #jquery



  Предлагаю использовать этот вопрос как некий конкурс, далее выделю 500рп тому,
чей ответ наберёт большее кол-во голосов "за".


Условие

Есть код, который позволяет передвигать блок определённых границах (в данном случае
это квадрат).



let reg = $('.region'), obj = $('.region .object');

reg.on('mousemove', function(e){
  let p = {
        // region size
        rW: reg.width(), rH: reg.height(),
        // object size
        oW: obj.width(), oH: obj.height(),
        // mouse position
        mY: e.pageY - reg.offset().top, mX: e.pageX - reg.offset().left,
        // object position (center)
        oY: obj.width() / 2, oX: obj.height() / 2
      };
   // object moving
   obj.css({
    'top': p.mY <= p.oY ? 0 : p.mY >= p.rW - p.oW ? p.rW - p.oW : p.mY - p.oY,
   'left': p.mX <= p.oX ? 0 : p.mX >= p.rH - p.oH ? p.rH - p.oH : p.mX - p.oX
   });
});
body {
  width: 100wh; height: 100vh;
  margin: 0;
  overflow: hidden;
  position: relative;
}

.region {
  display: block;
  width: 200px; height: 200px;
  box-shadow: 0 0 0 2px blue;
  position: absolute;
  left: 0; top: 0; right: 0; bottom: 0;
  margin: auto;
}

.object {
  display: block;
  width: 20px; height: 20px;
  background: red;
  position: absolute;
  left: 0; top: 0;
  pointer-events: none;
}


Тут даже ежу понятно, что "механика" слишком простая, ибо форма примитивная. А как быть, к примеру, если форма, по которой нужно будет ограничить движение - будет являться фигурой, допустим кругом, треугольником или каким-то сложной (относительно) фигурой. Задача Сделать движение элемента по ограниченной фигуре (одной или несколько), в приоритете круг, треугольник. Нюансы Как можно меньше трогать вёрстку html. желательно использовать "динамические" размеры фигуры (т.е. получать от самой фигуры (если это представляет возможности). Итоги Конкурс продлиться до 12.08.19


Ответы

Ответ 1



Предлагаю вот такой вариант. В лоб за O^2n обходим аппроксимации обоих путей и ищем точки пересечения.... let borderPts = getPoints(border), figurePts = getPoints(figure), drag; addEventListener('mouseup', e => { drag && (drag.element.style.pointerEvents = 'all'); drag = null; }); addEventListener('mousedown', e => { let tr = e.target.getAttribute('transform'); if (!tr) return; let xy = tr.split(/\(|\)|\,/); drag = {x: xy[1] - e.x, y: xy[2] - e.y, element: e.target}; e.target.style.pointerEvents = 'none' }); let debugPoint = pt => ``; addEventListener('mousemove', e => { if (!drag) return; let intersectionPts = intersect(e.x, e.y); let canMove = e.target === border && intersectionPts.length === 0; drag.element.setAttribute('stroke', canMove ? 'black' : 'red' ); if (mode.checked) debug.innerHTML = intersectionPts.map(debugPoint).join(''); if (canMove || mode.checked) drag.element.setAttribute('transform', `translate(${drag.x + e.x},${drag.y + e.y})`); }); function intersect(x,y) { let pts = figurePts.map(pt => [pt[0] + x + drag.x, pt[1] + y + drag.y]); let points = []; forEachPair(pts, (pt1, pt2) => { forEachPair(borderPts, (pt3, pt4) => { let intersection = findIntersection(...pt1,...pt2,...pt3,...pt4); intersection && points.push(intersection); }); }); return points; } function forEachPair(pts, f) { for (let i = 0; i { let p = path.getPointAtLength(t*precision); return [p.x, p.y]; }); } function findIntersection(x1, y1, x2, y2, x3, y3, x4, y4) { // http://paulbourke.net/geometry/pointlineplane/ let denom = (y4 - y3)*(x2 - x1) - (x4 - x3)*(y2 - y1); if (denom == 0) return null; let ua = ((x4 - x3)*(y1 - y3) - (y4 - y3)*(x1 - x3))/denom; let ub = ((x2 - x1)*(y1 - y3) - (y2 - y1)*(x1 - x3))/denom; if (ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1) return { x: x1 + ua * (x2 - x1), y: y1 + ua * (y2 - y1) }; }
mode

Ответ 2



Для двух окружностей все еще проще чем для прямоугольника, необходимо лишь посчитать что расстояние между центрами не больше чем разница радиусов большой и малой окружности. В этом случае не сложно сделать так, чтобы при коллизии малая окружность не упиралась и могла перемещаться в направлении курсора не выходя за границы большой. Для этого вычисляем вектор направления от центра большой окружности до курсора и умножаем его на разницу радиусов. let x = +bounds.getAttribute('cx'); let y = +bounds.getAttribute('cy'); let dr = +bounds.getAttribute('r') - drag.getAttribute('r'); addEventListener('mousemove', e => { let dy = y - e.y; let dx = x - e.x; if (dx*dx + dy*dy < dr*dr) { dx = e.layerX; dy = e.layerY; } else { let a = Math.atan2(dy, dx); dx = x - Math.cos(a)*dr; dy = y - Math.sin(a)*dr; } drag.setAttribute('transform', `translate(${dx},${dy})`); });

Комментариев нет:

Отправить комментарий