Создание линии между двумя объектами ( изображениями ) HTML, JavaScript

74
12 февраля 2022, 06:00

Есть файл HTML

<div> 
<img src="1.jpg" id="img1"> 
<img src="2.jpg" id="img2"> 
</div>

Как найти координаты x,y изображений на сайте с помощью JavaScript и провести между ними линию ?

Answer 1

Вы хотели что-то типа такого?) Как я к этому пришёл - ссылка на мой ответ на мой вопрос как рисовать линию по двум точкам
Вы задали ваш вопрос и я решил это сделать, это оказалось не просто, я вспомнил тригонометрию :D
Если вы хотите сохранить свои нервы в порядке - сделайте это на canvas, там всё намного проще. Просто указываете две точки и между ними рисуется линия.

Поясню свой код:

Функция elemPosition возвращает значение, где находится элемент на странице, даже если странница проскроллена, эта фунция вернёт позиции элемента относительно левого края документа и верхнего края документа

function elemPosition(elem) {
    let pos = {
        top: window.pageYOffset + elem.getBoundingClientRect().top,
        left: window.pageXOffset + elem.getBoundingClientRect().left,
        right: window.pageXOffset + elem.getBoundingClientRect().right,
        bottom: window.pageYOffset + elem.getBoundingClientRect().bottom
    };
    return pos;
}

Функция centerElem возвращает центр элемента по x и по у

function centerElem(elem) {
    let width = elem.offsetWidth;
    let height = elem.offsetHeight;
    let centerX = width/2 + elemPosition(elem).left;
    let centerY = height/2 + elemPosition(elem).top;
    let pos = {
        y: centerY,
        x: centerX
    };
    return pos;
}

Функция createPath создаёт элемент-линию и анимирует её

function createPath(from, fromIndex, to, toIndex) {
    let child = whatElems(from, fromIndex, to, toIndex);
    let childFrom = child.from;
    let childTo = child.to;
    render(childFrom, childTo);
}

Функция whatElems возвращает, какие элементы выбрать для анимации у родителя

function whatElems(from, fromIndex, to, toIndex) {
    let child = {
        from : from.children[fromIndex],
        to : to.children[toIndex]
    }
    return child;
}

Функция createDiv создаёт div с классом .harmony, добавляет его в конец body и возвращает этот div, чтобы элементом можно было манипулировать сразу, после вызова этой функции

function createDiv() {
    let div = document.createElement('div');
    div.classList.add('harmony');
    document.body.append(div);
    return div;
}

Функция createPathandPush создаёт элемент и анимирует с помощью createPath, а также добавляет элемент в массив paths, чтобы каждым элементом потом можно было манипулировать

function createPathAndPush(from, fromIndex, to, toIndex) {
    createPath(from, fromIndex, to, toIndex);
    paths.push([from, fromIndex, to, toIndex]);
}

Слушатель события load(загрузки изображений) вызывает срабатывание createPathAndPush для элементов через циклы, а также после циклов в глобальную переменную harmonies заносит все элементы, которые мы создали на странице

Функция render рендерит элемент и делает вычисления для точек, высоты, углов, на которые поворачивает. Если у функции render третий аргумент равен undefined, то она создаёт элемент и анимирует его, если передался третий аргумент, то функция просто ресайзит его, меняя у него угол поворота, длину и местоположение исходных точек
Если что непонятно - пишите, отвечу

P.S.: при клике по странице анимация начинается заново

let items = document.querySelectorAll('.item'); 
 
let reqAnimFrame = (function() { 
    return requestAnimationFrame       || 
           mozRequestAnimationFrame    || 
           webkitRequestAnimationFrame || 
           oRequestAnimationFrame      || 
           msRequestAnimationFrame     || 
    function(callback) { 
        setTimeout(callback, 1000 / 60); 
    } 
})(); 
 
function animate({timing, draw, duration, active}) { 
 
  let start = performance.now(); 
 
  reqAnimFrame(function animate(time) { 
    // timeFraction изменяется от 0 до 1 
    let timeFraction = (time - start) / duration; 
    if (timeFraction > 1) timeFraction = 1; 
 
    // вычисление текущего состояния анимации 
    let progress = timing(timeFraction); 
 
    draw(progress); // отрисовать её 
 
    if (timeFraction < 1) { 
      requestAnimationFrame(animate); 
    } 
 
  }); 
} 
 
function elemPosition(elem) { 
	let pos = { 
	    top: window.pageYOffset + elem.getBoundingClientRect().top, 
	    left: window.pageXOffset + elem.getBoundingClientRect().left, 
	    right: window.pageXOffset + elem.getBoundingClientRect().right, 
	    bottom: window.pageYOffset + elem.getBoundingClientRect().bottom 
	}; 
	return pos; 
} 
 
function centerElem(elem) { 
	let width = elem.offsetWidth; 
	let height = elem.offsetHeight; 
	let centerX = width/2 + elemPosition(elem).left; 
	let centerY = height/2 + elemPosition(elem).top; 
	let pos = { 
		y: centerY, 
		x: centerX 
	}; 
	return pos; 
} 
 
function whatElems(from, fromIndex, to, toIndex) { 
	let child = { 
		from : from.children[fromIndex], 
		to : to.children[toIndex] 
	} 
	return child; 
} 
 
function createDiv() { 
	let div = document.createElement('div'); 
	div.classList.add('harmony'); 
	document.body.append(div); 
	return div; 
} 
 
function render(childF, childT, elemResize, j) { 
 
	let div = elemResize; 
 
	if (elemResize == undefined) { 
		div = createDiv(); 
	} 
 
 
	let height = window.getComputedStyle(div, null).height; // исходная высота прямой 
	height = Number(height.slice(0, height.length-2)); // исходная высота прямой 
	let d, tang, arctg, angle, scale, final, start; 
 
	function logic() { 
 
		final = { 
			x: centerElem(childT).x, 
			y: elemPosition(childT).top 
		}; // координаты конечной точки 
 
		start = { 
			x: centerElem(childF).x, 
			y: elemPosition(childF).bottom 
		}; // координаты начальной точки 
 
		d = Math.sqrt( Math.pow((final.x - start.x), 2) + Math.pow((final.y - start.y), 2) ); // длина прямой после анимации 
		tang = (final.y-start.y) / (final.x-start.x)// Тангенс 
		arctg = Math.atan(tang); // Арктангенс 
		angle = arctg * 180 / Math.PI; // угол в градусах 
		scale = d/height; // насколько масштабировать прямую по высоте 
 
		if (tang < 0) { 
			angle = 90 + angle; 
		} else if (tang > 0) { 
			angle = -(90-angle); 
		} 
 
	} 
 
	logic(); 
 
	if (elemResize !== undefined) { 
 
		if (progressGlobal[j+1] == 1) { 
			reqAnimFrame(startResize); 
		} 
 
		function startResize() { 
			div.style.transform = `translate(${start.x}px, ${start.y}px) rotate(${angle}deg) scale(1, ${scale}) translateZ(0)`; 
		} 
	} else { 
		animate({ 
			duration: 5000, 
			timing(timeFraction) { 
				return timeFraction; 
			}, 
			draw(progress) { 
				logic(); 
				div.style.transform = `translate(${start.x}px, ${start.y}px) rotate(${angle}deg) scale(1, ${progress*scale}) translateZ(0)`; 
				progressGlobal[j] = progress; 
			} 
		}); 
 
	} 
 
} 
 
function createPath(from, fromIndex, to, toIndex, j) { 
 
	let child = whatElems(from, fromIndex, to, toIndex); 
	let childFrom = child.from; 
	let childTo = child.to; 
	render(childFrom, childTo, undefined, j); 
 
} 
 
let progressGlobal = []; 
let harmonies; 
let paths = []; 
 
function startHell() { 
 
	function createPathAndPush(from, fromIndex, to, toIndex, j) { 
		createPath(from, fromIndex, to, toIndex, j); 
		paths.push([from, fromIndex, to, toIndex, j]); 
		j++; 
		return j; 
	} 
 
	let j = 0; 
 
	for (let i = 3; i <= 5; i++) { 
		j = createPathAndPush(items[i-3], 0, items[i], 1, j); 
		j = createPathAndPush(items[i-3], 0, items[i], 0, j); 
	} 
 
	for (let i = 6; i <= 8; i++) { 
		j = createPathAndPush(items[i-3], 0, items[i], 0, j); 
		j = createPathAndPush(items[i-3], 0, items[i], 1, j); 
		j = createPathAndPush(items[i-3], 1, items[i], 1, j); 
		j = createPathAndPush(items[i-3], 1, items[i], 2, j); 
	} 
 
	harmonies = document.querySelectorAll('.harmony'); 
 
	for (let i = 0; i < harmonies.length; i++) { 
		progressGlobal[i] = 0; 
	} 
 
} 
 
window.addEventListener('load', () => { 
	startHell(); 
}); 
 
window.addEventListener('resize', () => { 
	for (let i = 0; i < harmonies.length; i++) { 
		let child = whatElems(paths[i][0], paths[i][1], paths[i][2], paths[i][3]); 
		let childFrom = child.from; 
		let childTo = child.to; 
		render(childFrom, childTo, harmonies[i], i-1); 
	} 
}); 
 
window.addEventListener('click', () => { 
 
	for (let i = 0; i < harmonies.length; i++) { 
		harmonies[i].remove(); 
	} 
 
	startHell(); 
 
});
@import url('https://fonts.googleapis.com/css?family=Open+Sans&display=swap'); 
 
*, *:before, *:after { 
	-webkit-box-sizing: border-box; 
	-moz-box-sizing: border-box; 
	box-sizing: border-box; 
} 
 
body { 
	margin: 0; 
	color: black; 
	font-size: 16px; 
	font-family: 'Open Sans', sans-serif; 
} 
 
img { 
	display: block; 
	max-width: 100%; 
	height: auto; 
} 
 
.wrapper { 
	max-width: 1400px; 
	margin: 0 auto; 
} 
 
.line:not(:first-child) { 
	margin-top: 50px; 
} 
 
.item { 
	display: -webkit-flex; 
	display: -moz-flex; 
	display: -ms-flex; 
	display: -o-flex; 
	display: flex; 
	flex: 1; 
} 
 
.item:not(:last-child) { 
	margin-right: 2%; 
} 
 
.item__img { 
	z-index: 2; 
} 
 
.item__img:not(:first-child) { 
	margin-left: 5px; 
} 
 
.line { 
	display: -webkit-flex; 
	display: -moz-flex; 
	display: -ms-flex; 
	display: -o-flex; 
	display: flex; 
	transition: opacity .3s ease-in-out; 
} 
 
.harmony { 
	z-index: 3; 
	position: absolute; 
	width: 2.5px; 
	background-color: red; 
 	height: 80px; 
 	left: 0; 
 	top: 0; 
	transform: translateZ(0); 
	transform-origin: 50% 0%; 
}
<!DOCTYPE html> 
<html lang="ru"> 
<head> 
	<meta charset="UTF-8"> 
	<meta name="viewport" content="width=device-width, initial-scale=1"> 
	<title>Document</title> 
	<link rel="stylesheet" href="style.css"> 
</head> 
<body> 
	 
<div class="wrapper"> 
 
	<div class="line line__one"> 
 
		<div class="item"> 
			<div class="item__img"> 
				<img src="https://picsum.photos/500/250"> 
			</div> 
		</div> 
 
		<div class="item"> 
			<div class="item__img"> 
				<img src="https://picsum.photos/500/250"> 
			</div> 
		</div> 
 
		<div class="item"> 
			<div class="item__img"> 
				<img src="https://picsum.photos/500/250"> 
			</div> 
		</div> 
 
	</div> <!-- .line__one --> 
 
	<div class="line line__two"> 
 
		<div class="item"> 
			<div class="item__img"> 
				<img src="https://picsum.photos/300/150"> 
			</div> 
			<div class="item__img"> 
				<img src="https://picsum.photos/300/150"> 
			</div> 
		</div> 
 
		<div class="item"> 
			<div class="item__img"> 
				<img src="https://picsum.photos/300/150"> 
			</div> 
			<div class="item__img"> 
				<img src="https://picsum.photos/300/150"> 
			</div> 
		</div> 
 
		<div class="item"> 
			<div class="item__img"> 
				<img src="https://picsum.photos/300/150"> 
			</div> 
			<div class="item__img"> 
				<img src="https://picsum.photos/300/150"> 
			</div> 
		</div> 
 
	</div> <!-- .line__two --> 
 
	<div class="line line__three"> 
 
		<div class="item"> 
			<div class="item__img"> 
				<img src="https://picsum.photos/200/100"> 
			</div> 
			<div class="item__img"> 
				<img src="https://picsum.photos/200/100"> 
			</div> 
			<div class="item__img"> 
				<img src="https://picsum.photos/200/100"> 
			</div> 
		</div> 
 
		<div class="item"> 
			<div class="item__img"> 
				<img src="https://picsum.photos/200/100"> 
			</div> 
			<div class="item__img"> 
				<img src="https://picsum.photos/200/100"> 
			</div> 
			<div class="item__img"> 
				<img src="https://picsum.photos/200/100"> 
			</div> 
		</div> 
 
		<div class="item"> 
			<div class="item__img"> 
				<img src="https://picsum.photos/200/100"> 
			</div> 
			<div class="item__img"> 
				<img src="https://picsum.photos/200/100"> 
			</div> 
			<div class="item__img"> 
				<img src="https://picsum.photos/200/100"> 
			</div> 
		</div> 
 
	</div> <!-- .line__three --> 
 
 
</div> <!-- .wrapper --> 
 
<script src="script.js"></script> 
 
</body> 
</html>

READ ALSO
fancybox- не могу удалить значение кнопки close

fancybox- не могу удалить значение кнопки close

Проблема была в том что не правильно прописал селектор которому она была подключенаНадо добавлять $('

116
Почему htmlentities не может на 100% обезопасить от XSS?

Почему htmlentities не может на 100% обезопасить от XSS?

На Stack Overflow и на других форумах в темах по защите от XSS видел, что люди говорят про то, что htmlentities не спасет во всех случаях, но почему, никто...

101
Вложенный SELECT метода IN в EntityFramework linq

Вложенный SELECT метода IN в EntityFramework linq

В месте вызова определен контекст

89