matrixTransform на svg: неожиданное поведение

114
01 октября 2021, 03:10

У меня есть div, содержащий изображение SVG размером 300x300 пикселей и viewBox 1000x1000. Изображение состоит из синего прямоугольника поверх красного.
Когда я перемещаю мышь, круг следует за положением мыши внутри изображения:

Все идеально, за исключением того, что когда я применяю преобразование, изменяющее перспективу и вращение, указатель мыши и центр круга больше не совпадают:

Мой код:

$(function() { 
	$('#image').mousemove(function(event) { 
		var svg = document.querySelector('svg'); 
		var pt = svg.createSVGPoint(); 
		pt.x = event.clientX; 
		pt.y = event.clientY; 
		pt = pt.matrixTransform(svg.getScreenCTM().inverse()); 
		overlay = document.getElementById('overlay'); 
		$('#overlay').html( 
			"<circle cx='" + pt.x + "' cy='" + pt.y + "' r='50' stroke='#8f00ff' fill='transparent' stroke-width='10' /></svg>" 
		); 
		refresh = $("#overlay").html(); 
		$("#overlay").html( refresh ) 
	}); 
}); 
function Transform() { 
	$('#image').css({ 
		transformOrigin: '500px 500px', 
		transform: 'perspective(100px) rotateX(5deg)' 
	}); 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> 
<div id='image' tabindex='0' > 
	<svg id='svgmap' width='300' height='300' xmlns='http://www.w3.org/2000/svg' version='1.1' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='0 0 1000 1000'> 
		<rect x='0' y='0' width='1000' height='1000' fill='red' /> 
		<rect x='250' y='250' width='500' height='500' stroke='yellow' fill='blue' stroke-width='10' /> 
		<g id='overlay'></g> 
	</svg> 
</div> 
<button onclick='Transform()'>Transform</button>

Моя цель - сохранить соответствие между фиолетовым центром круга и указателем мыши, даже если к объекту применено преобразование. Есть ли способ сделать это?

Answer 1

В коде #image - это div. Чтобы это работало, нужно применить преобразование к элементу SVG (#svgmap), и преобразование должно быть преобразованием SVG.

$(function() { 
	$('#svgmap').mousemove(function(event) { 
		var svg = document.querySelector('svg'); 
		var pt = svg.createSVGPoint(); 
		pt.x = event.clientX; 
		pt.y = event.clientY; 
		pt = pt.matrixTransform(svg.getScreenCTM().inverse()); 
		overlay = document.getElementById('overlay'); 
		$('#overlay').html( 
			"<circle cx='" + pt.x + "' cy='" + pt.y + "' r='50' stroke='#8f00ff' fill='transparent' stroke-width='10' /></svg>" 
		); 
		refresh = $("#layer_wafer").html(); 
		$("#layer_wafer").html( refresh ) 
	}); 
}); 
function Transform() { 
	svgmap.setAttributeNS(null,"transform", "skewX(-20) translate(100)"); 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> 
<div id='image' tabindex='0' > 
	<svg id='svgmap' width='300' height='300' xmlns='http://www.w3.org/2000/svg' version='1.1' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='0 0 1000 1000' transform=""> 
		<rect x='0' y='0' width='1000' height='1000' fill='red' /> 
     
		<rect x='250' y='250' width='500' height='500' stroke='yellow' fill='blue' stroke-width='10' /> 
		<g id='overlay'></g> 
	</svg> 
</div> 
<button onclick='Transform()'>Transform</button>

Я понимаю, что желательно преобразование 3D CSS, но это (по крайней мере, на данный момент) не работает.

Это статья, где можно прочитать больше о 3D преобразованиях в SVG. Цитирую из этой статьи:

Все функции трехмерного преобразования, описанные в этом разделе, должны рассматриваться как «будущие»

READ ALSO
ghbdtn =&gt; привет. Перевод с английского регистра на русский

ghbdtn => привет. Перевод с английского регистра на русский

Пишу бота для дословного перевода сообщений с английского на русскийХотелось бы узнать об общепринятых решениях этой задачи, а не придумывать...

105
jQuery: заменить последнее числовое значение в строке

jQuery: заменить последнее числовое значение в строке

Есть плагин для клонирования полей формы, в котором индекс в атрибуте name прирастает при каждом клонировании, те

158
Не могу установить data атрибут

Не могу установить data атрибут

Есть select, который используется с плагином select2И мне надо использовать свои атрибуты

97