#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})`); });
Комментариев нет:
Отправить комментарий