前言
在开发中,除了常用的图片缩放和剪切以外,还有对图片进行标记点位的功能,如鼠标点击图片标记点位和已有的标记点位展示,如果不设置固定宽高的img标签,而是使用自适应img标签,在不同分辨率下会使图片大小不一,从而导致坐标的改变,使标记点位偏移。
计算公式及原理
- 相对原图坐标映射计算:
- 当前图片比原图宽高多出(减少)了多少px = clientWidth - naturalWidth(或自定义宽高);
- 多出(减少)的px占当前图片的百分之几 = 步骤1的得数 / clientWidth;
- 原图宽高在当前图片中占比百分之几 = 1 - 步骤2的得数;
- 缩放图片的坐标映射为原图的坐标(称为映射坐标) = 步骤3的得数 * offsetX(鼠标坐标);
- 知道了每个计算的含义,接下来可以把计算简化为:(naturalWidth / clientWidth) * offsetX;
- 标记坐标映射计算:
- 缩放系数(大于1是放大,小于1是缩小) = clientWidth / 原图宽高 or 自定义宽高
- 标记在缩放图片上的实际坐标 = 缩放系数 * 映射坐标
上述“原图”和“naturalWidth”可以是自己定义的宽高
提示:
- 测试使用的chrome和FireFox,发现offsetWidth(Height)和offsetX(Y)会相差1px。目前使用三目运算符来判断解决相差1px的问题。
- 目前发现的规律是使用高度或宽度有>=0.5的小数点,使用flex,inline-block或不使用也会相差1px。
- 以上说的相差1px并不是全部图片,而是有些会有些不会(单独一张图片时又会是正常)
效果预览
源代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
html, body {
margin: 0;
width: 100%;
}
h1 {
margin: 0;
}
.img1920, .img319 {
display: flex;
}
.img1920 div, .img319 div{
position: relative;
}
.img1920 div, .img319 div {
margin-right: 30px;
}
.tips {
position: absolute;
left: 0;
top: 0;
z-index: 100;
}
.tips p {
margin: 0;
color: red;
}
</style>
</head>
<body>
<div class="tips">
<span class="icon">👆</span>
<p></p>
</div>
<h1>319*400</h1>
<div class="img319">
<div>
<img src="123.jpg" alt="" style="width: 220px">
<p>小图X:<span>0</span></p>
<p>小图Y:<span>0</span></p>
</div>
<div>
<img src="123.jpg" alt="" style="width: 420px">
<p>中图X:<span>0</span></p>
<p>中图Y:<span>0</span></p>
</div>
<div>
<img src="123.jpg" alt="" style="width: 643px">
<p>大图X:<span>0</span></p>
<p>大图Y:<span>0</span></p>
</div>
<div>
<img src="123.jpg" alt="" style="width: 319px;height: 400px">
<p>原图X:<span>0</span></p>
<p>原图Y:<span>0</span></p>
</div>
</div>
<h1>1920*1080:</h1>
<div class="img1920">
<div>
<img src="1532619760582.jpg" alt="" style="width: 220px">
<p>小图X:<span>0</span></p>
<p>小图Y:<span>0</span></p>
</div>
<div>
<img src="1532619760582.jpg" alt="" style="width: 420px">
<p>中图X:<span>0</span></p>
<p>中图Y:<span>0</span></p>
</div>
<div>
<img src="1532619760582.jpg" alt="" style="width: 643px">
<p>大图X:<span>0</span></p>
<p>大图Y:<span>0</span></p>
</div>
</div>
<h1>1920*1080放大2000*2000:</h1>
<div>
<div>
<img src="1532619760582.jpg" alt="" style="width: 2000px;height: 2000px">
<p>原图X:<span>0</span></p>
<p>原图Y:<span>0</span></p>
</div>
</div>
<script>
let doc = document
let img = doc.querySelectorAll('img');
let len = img.length;
let tips = doc.querySelector('.tips')
let img319 = doc.querySelector('.img319')
let img319Div = img319.querySelectorAll('div')
let body = doc.querySelector('body')
let x = 0;
let y = 0;
let numY = 0;
let numX = 0;
body.onclick = function (e) {
tips.style.left = `${e.pageX - 5}px`
tips.style.top = `${e.pageY - 2}px`
tips.querySelector('p').innerText = `x:${x} y:${y}`;
}
/**
* 标记坐标展示计算
**/
img319.onclick = function (e) {
for(let item of img319Div){
let span = doc.createElement('span')
span.style.width = '10px'
span.style.height = '10px'
span.style.background = 'red'
span.style.position = 'absolute'
span.style.left = `${((item.querySelector('img').clientWidth / item.querySelector('img').naturalWidth) * x) - 5}px`
span.style.top = `${((item.querySelector('img').clientHeight / item.querySelector('img').naturalHeight) * y) - 5}px`
item.append(span)
}
}
for (let i = 0; i < len; i++) {
img[i].onmousemove = mousemove
}
function mousemove(e) {
// console.log('X:', this.offsetWidth, e.offsetX, 'Y:', this.offsetHeight, e.offsetY);
numY = this.offsetHeight - 1 === e.offsetY ? this.offsetHeight : e.offsetY;
numX = this.offsetWidth - 1 === e.offsetX ? this.offsetWidth : e.offsetX;
// 缩放多出(减少)了多少px = this.clientWidth - this.naturalWidth
// 缩放多出(减少)了多少px占当前图片的百分之几 = 缩放多出(减少)了多少px / this.clientWidth
// 原图片宽高在当前图片宽高中占比百分之几 = 1 - 缩放多出(减少)了多少px占当前图片的百分之几
// 鼠标在缩放图片上时映射为原图宽高的坐标 = 原图片宽高在当前图片宽高中占比百分之几 * numX
// console.log((1 - (this.clientWidth - this.naturalWidth) / this.clientWidth) * numX)
// 可简化为 (this.naturalWidth / this.clientWidth) * numX
x = Math.round((this.naturalWidth / this.clientWidth) * numX);
y = Math.round((this.naturalHeight / this.clientHeight) * numY);
this.nextElementSibling.querySelector('span').innerText = x;
this.nextElementSibling.nextElementSibling.querySelector('span').innerText = y
}
</script>
</body>
</html>
建议上传后端标记坐标时,带上当前创建这条标记时的图片宽高,接口获取到标记坐标和宽高后在循环计算