使用带有变换原点中心的滚轮在鼠标点上放大/缩小图像。在计算中需要帮助 [已解决]
Posted
技术标签:
【中文标题】使用带有变换原点中心的滚轮在鼠标点上放大/缩小图像。在计算中需要帮助 [已解决]【英文标题】:Zoom image in/out on mouse point using wheel with transform origin center. Need help in calculation [ SOLVED ] 【发布时间】:2022-01-09 14:01:17 【问题描述】:上下文:
我的要求是通过用户对图像的操作进行图像转换,例如缩放、旋转和平移。 使用变换原点 50% 50%(默认)在其中心完成图像的旋转和平移。 为了使用鼠标滚轮缩放鼠标点上的图像,我能够使其与变换原点 0 0 一起使用 如果我更改原点,为了适应缩放行为,我需要重新计算相对于新原点的其他转换。问题:
如果变换原点默认为 50% 50%,如何在鼠标点上缩放图像? 以下小提琴适用于变换原点 0 0 以供参考。我需要帮助来使用转换原点 50% 50% 优化此计算。有人可以协助进一步优化此计算,使其适用于默认变换原点吗?
<!DOCTYPE html>
<html lang="en">
<head>
<style>
.container
background-color: lightgrey;
.stage
height: 100%;
width: 100%;
overflow: hidden;
#image
transform-origin: 0 0;
height: auto;
width: 80%;
cursor: grab;
.actions
display: flex;
position: absolute;
bottom: 0;
left: 0;
height: 1.5rem;
width: 100%;
background-color: lightgrey;
.action
margin-right: 1rem;
</style>
</head>
<body>
<div class="container">
<div class="stage">
<img id="image" src="https://cdn.pixabay.com/photo/2018/01/14/23/12/nature-3082832__480.jpg" />
</div>
<div class="actions">
<div class="action">
<label for="rotate">Rotate </label>
<input type="range" id="rotate" name="rotate" min="0" max="360">
</div>
</div>
</div>
<script>
const img = document.getElementById('image');
const rotate = document.getElementById('rotate');
let mouseX;
let mouseY;
let mouseTX;
let mouseTY;
let startXOffset = 222.6665;
let startYOffset = 224.713;
let startX = 0;
let startY = 0;
let panning = false;
const ts =
scale: 1,
rotate: 0,
translate:
x: 0,
y: 0
;
rotate.oninput = function(event)
event.preventDefault();
ts.rotate = event.target.value;
setTransform();
;
img.onwheel = function(event)
event.preventDefault();
let xs = (event.clientX - ts.translate.x) / ts.scale;
let ys = (event.clientY - ts.translate.y) / ts.scale;
let delta = (event.wheelDelta ? event.wheelDelta : -event.deltaY);
ts.scale = (delta > 0) ? (ts.scale * 1.2) : (ts.scale / 1.2);
ts.translate.x = event.clientX - xs * ts.scale;
ts.translate.y = event.clientY - ys * ts.scale;
setTransform();
;
img.onmousedown = function(event)
event.preventDefault();
panning = true;
img.style.cursor = 'grabbing';
mouseX = event.clientX;
mouseY = event.clientY;
mouseTX = ts.translate.x;
mouseTY = ts.translate.y;
;
img.onmouseup = function(event)
panning = false;
img.style.cursor = 'grab';
;
img.onmousemove = function(event)
event.preventDefault();
const x = event.clientX;
const y = event.clientY;
pointX = (x - startX);
pointY = (y - startY);
if (!panning)
return;
ts.translate.x =
mouseTX + (x - mouseX);
ts.translate.y =
mouseTY + (y - mouseY);
setTransform();
;
function setTransform()
const steps = `translate($ts.translate.xpx,$ts.translate.ypx) scale($ts.scale) rotate($ts.rotatedeg)`;
img.style.transform = steps;
</script>
</body>
</html>
【问题讨论】:
我不完全理解为什么缩放应该涉及重新定位 - 你能解释一下你想在这里实现什么吗? 如上所述,要求是在图像上的鼠标点上使用鼠标滚轮启用放大/缩小。当我们缩放具有特定原点的图像时,鼠标点下方的图像会移动到不同的位置。因此,为了模拟该特定点上的图像缩放,我们需要通过适当的平移来补偿它。如果您可以检查小提琴,您就会理解这种行为。 我确实检查了小提琴,但我不明白这种行为,这就是我问这个问题的原因。好的,所以鼠标要停留在图像的同一点上,明白了。这就是我们需要将 CSS 缩放属性与转换的其余部分分开的地方 - 即将推出,但尚未出现在所有浏览器中。 鼠标点的比例仅在原点设置为 0 0 时有效。但我希望旋转发生在图像的中心。因此,为了使所有内容都适应 50% 50%(默认)的共同起源,如何更改“onwheel”回调的计算以使其按预期工作? 【参考方案1】:如果变换原点默认为50% 50%,如何在鼠标点上缩放图像?
要将原点移动到 50% 50%,我们需要鼠标相对于图像的 x,y 位置,即图像左上角作为原点直到图像右下角。然后我们通过使用相对坐标来补偿图像位置。我们还需要考虑图像尺寸。
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<style>
.container
background-color: lightgrey;
.stage
height: 90vh;
width: 100%;
overflow: hidden;
#image
transform-origin: 50% 50%;
height: auto;
width: 40%;
cursor: grab;
.actions
display: flex;
position: absolute;
bottom: 0;
left: 0;
height: 1.5rem;
width: 100%;
background-color: lightgrey;
.action
margin-right: 1rem;
</style>
</head>
<body>
<div class="container">
<div class="stage">
<img id="image" src="https://cdn.pixabay.com/photo/2018/01/14/23/12/nature-3082832__480.jpg" />
</div>
<div class="actions">
<div class="action">
<label for="rotate">Rotate </label>
<input type="range" id="rotate" name="rotate" min="0" max="360">
<button onclick="reset()">Reset All</button>
</div>
</div>
</div>
<script>
const img = document.getElementById('image');
const rotate = document.getElementById('rotate');
let mouseX;
let mouseY;
let mouseTX;
let mouseTY;
let startXOffset = 222.6665;
let startYOffset = 224.713;
let startX = 0;
let startY = 0;
let panning = false;
const ts =
scale: 1,
rotate: 0,
translate:
x: 0,
y: 0
;
rotate.oninput = function(event)
event.preventDefault();
ts.rotate = event.target.value;
setTransform();
;
img.onwheel = function(event)
event.preventDefault();
//need more handling to avoid fast scrolls
var func = img.onwheel;
img.onwheel = null;
let rec = img.getBoundingClientRect();
let x = (event.clientX - rec.x) / ts.scale;
let y = (event.clientY - rec.y) / ts.scale;
let delta = (event.wheelDelta ? event.wheelDelta : -event.deltaY);
ts.scale = (delta > 0) ? (ts.scale + 0.2) : (ts.scale - 0.2);
//let m = (ts.scale - 1) / 2;
let m = (delta > 0) ? 0.1 : -0.1;
ts.translate.x += (-x * m * 2) + (img.offsetWidth * m);
ts.translate.y += (-y * m * 2) + (img.offsetHeight * m);
setTransform();
img.onwheel = func;
;
img.onmousedown = function(event)
event.preventDefault();
panning = true;
img.style.cursor = 'grabbing';
mouseX = event.clientX;
mouseY = event.clientY;
mouseTX = ts.translate.x;
mouseTY = ts.translate.y;
;
img.onmouseup = function(event)
panning = false;
img.style.cursor = 'grab';
;
img.onmousemove = function(event)
event.preventDefault();
let rec = img.getBoundingClientRect();
let xx = event.clientX - rec.x;
let xy = event.clientY - rec.y;
const x = event.clientX;
const y = event.clientY;
pointX = (x - startX);
pointY = (y - startY);
if (!panning)
return;
ts.translate.x =
mouseTX + (x - mouseX);
ts.translate.y =
mouseTY + (y - mouseY);
setTransform();
;
function setTransform()
const steps = `translate($ts.translate.xpx,$ts.translate.ypx) scale($ts.scale) rotate($ts.rotatedeg) translate3d(0,0,0)`;
//console.log(steps);
img.style.transform = steps;
function reset()
ts.scale = 1;
ts.rotate = 0;
ts.translate =
x: 0,
y: 0
;
rotate.value = 180;
img.style.transform = 'none';
setTransform();
</script>
</body>
</html>
为了简单起见,我将 0.2 添加到比例而不是乘以 1.2。你可以用正确的比例把它改回来。还添加了一个重置按钮。 现在,随着变换原点已转移到 50% 50%,您可以在中心旋转图像。
【讨论】:
感谢您的解决方案。非常感谢您花时间解决这个问题。 乐于助人!我希望 devtools 有元素坐标指示器。 :( 只是一个想法,如果您可以探索使用transform: translate(10px, 10px) scale(2) translate(-5px, 10px) rotate(30deg);
的可能性,我不知道您一次可以堆叠多少。此外,您可以在绝对定位的图像上设置顶部和左侧而不是平移(x,y) . 我的意思是使用 top, left 进行拖动和平移以补偿缩放。这可以使计算变得简单。
我最初使用top
left
来翻译元素。但是为了在更新位置时减少绘制周期数并在移动元素时达到一致的 60 fps,我使用了变换。从而减少每个鼠标事件的重新计算。以上是关于使用带有变换原点中心的滚轮在鼠标点上放大/缩小图像。在计算中需要帮助 [已解决]的主要内容,如果未能解决你的问题,请参考以下文章
Java AWT 图形界面编程使用鼠标滚轮放大缩小 Canvas 画布 ( 鼠标滚轮事件监听器 MouseWheelListener )