1 说明
基本思路是把图片划分为多个区域,点击图片的时候获取到坐标,判断是否在指定的区域内
2 划分图片区域
2.1 采集图片坐标的工具
2.1.1 网址

2.2.2 说明
这个网址可以上传图片,然后用多个点画区域,获取到这些点的坐标
2.2.3 示例
1)第一步:上传图片

2)第二部:在下方去操作,框出想要的形状
下面这里一条代表框出一个区域
shape:要框出的区域的形状 rect矩形 poly多边形 circle圆形

如下图,选择的多边形,把左边这个人框出来了

3)第三步,查看点的坐标
点击 show me the code,可以看到点位的坐标coords,每两个数字是一组,分别是x和y坐标,如下面第一组就是x:157,y:137。这个坐标是按照你取点的顺序排列的。
另外,这个坐标系的原点是图片的左上角,横向是x轴,纵向是y轴。坐标大小是按照图片的分辨率来的
现在我们就获取到了一组坐标,可以形成一个多边形区域-也就是我们想要的区域

3 编码
3.1 json
{
"navigationBarTitleText": "不规则区域点击示例"
}
3.2 wxss
/* pages/index/index.wxss */
.page-container {
padding: 20rpx;
background-color: #f5f5f5;
min-height: 100vh;
}
.page-title {
display: block;
font-size: 36rpx;
font-weight: bold;
margin-bottom: 20rpx;
color: #333;
}
/* 图片容器:相对定位,确保Canvas能叠在图片上 */
.img-container {
position: relative;
width: 100%; /* 容器宽度占满屏幕,图片自适应 */
}
/* 目标图片:宽度自适应,避免拉伸 */
.target-img {
width: 100%;
border-radius: 8rpx;
vertical-align: top; /* 消除图片底部空白 */
}
/* Canvas:绝对定位,不拦截点击事件 */
.border-canvas {
position: absolute;
top: 0;
left: 0;
width: 100%; /* 与图片宽度一致 */
height: 100%; /* 与图片高度一致 */
pointer-events: none; /* 关键:允许点击穿透到图片 */
}
.result {
margin-top: 30rpx;
padding: 20rpx;
background-color: #fff;
border-radius: 8rpx;
font-size: 32rpx;
color: #666;
}
3.3 wxml
<!-- pages/index/index.wxml -->
<view class="page-container">
<!-- 图片容器:bindtouchstart绑定事件,确保能触发 -->
<view class="img-container" bindtouchstart="handleTouch">
<!-- 目标图片:mode="widthFix"保持宽高比 -->
<image
class="target-img"
src="{{ imgSrc }}"
mode="widthFix"
bindload="handleImgLoad"
></image>
</view>
<!-- 点击结果展示,为了方便的看到点击效果 -->
<view class="result" wx:if="{{ selectedArea }}">
点击区域:{{ selectedArea }}
</view>
</view>
3.4 js
3.4.1 数据
1)imgSrc换成自己的图片路径
2)originSize 图片的原始大小(分辨率)
3)clickAreas:区域的数据的数组。下面是在图片上框出了两个区域。
points:区域的坐标数组
3.4.2 图片缩放-坐标也需要缩放
图片在放到小程序后,大概率是会变更大小的,所以需要做一下坐标缩放适应
在初始化函数handleImgLoad中,获取到了实际的图片大小,根据实际图片大小和原图大小计算出缩放比例,最后把points里面的坐标也缩放,存放到adaptedAreas 中,后面实际使用的是缩放后的坐标
3.4.3 点击图片事件
函数handleTouch
获取到的pageX和pageY是相对于页面左上角的坐标,而不是相对于图片左上角的坐标,所以需要转换下
const pageX = touch.pageX; // 相对于页面左上角的X坐标
const pageY = touch.pageY; // 相对于页面左上角的Y坐标
使用pageX减去图片左上角相对于页面左上角的横向距离
使用pageY减去图片左上角相对于页面左上角的总向距离
获取到的就是点击的点相对于图片的坐标了
const clickXInImg = pageX - imgRect.left;
const clickYInImg = pageY - imgRect.top;
3.4.4 判断点击的位置的坐标是否在区域内
函数isPointInPolygon就是用来进行判断的算法
参数分别是这个点的横坐标和纵坐标以及要判断的区域的坐标集合
// pages/index/index.js
Page({
data: {
imgSrc: 'https://xxx.aliyuncs.com/room/B-2.jpg', // 图片路径(需自行替换)
originSize: { width: 2004, height: 1437 }, // 图片原始尺寸(必须与实际一致)
clickAreas: [ // 不规则区域配置(相对坐标,0~1)
{
id: 'area1',
name: '区域1',
color: '#ff4d4f',
points: [
{ x: 420, y: 1138},
{ x: 489, y:938 },
{ x: 640, y: 835 },
{ x: 535, y: 726 },
{ x: 311, y: 821 },
{ x: 289, y: 857 },
{ x: 153, y: 926 },
{ x: 82, y: 1081 },
{ x: 79, y: 1101 }
]
},
{
id: 'area2',
name: '区域2',
color: '#ff4d4f',
points: [
{ x: 525, y: 1136},
{ x: 727, y:1203 },
{ x: 992, y: 1118 },
{ x: 948, y: 986 },
{ x: 700, y: 1055 },
{ x: 546, y: 1041 }
]
}
],
adaptedAreas: [], // 适配后的绝对坐标区域
selectedArea: '', // 选中的区域名称
canvasCtx: null, // Canvas上下文
imgRect: {} // 图片渲染后的位置信息(含宽高、距离顶部/左侧的距离)
},
onLoad() {
},
// 图片加载完成:获取渲染后的位置和尺寸(关键,用于坐标适配)
handleImgLoad(e) {
const _this = this;
// 获取图片渲染后的位置信息(含宽高、left、top)
const query = this.createSelectorQuery();
query.select('.target-img')
.boundingClientRect(rect => {
// rect包含:width(渲染宽)、height(渲染高)、left(距离屏幕左侧)、top(距离屏幕顶部)
_this.setData({ imgRect: rect });
console.log('rect',rect)
// 计算缩放比例(原始尺寸 → 渲染尺寸)
const scaleX = rect.width / _this.data.originSize.width;
const scaleY = rect.height / _this.data.originSize.height;
// 适配坐标:相对坐标 → 绝对坐标(基于图片渲染后的位置)
const adaptedAreas = _this.data.clickAreas.map(area => ({
...area,
points: area.points.map(point => ({
// 绝对坐标 = 相对坐标 × 缩放比例(基于图片自身的渲染尺寸)
x: point.x * scaleX,
y: point.y * scaleY
}))
}));
console.log('adaptedAreas',adaptedAreas)
_this.setData({ adaptedAreas }, () => {
});
})
.exec();
},
// 处理触摸事件:正确获取坐标并判断区域
handleTouch(e) {
const { adaptedAreas, imgRect } = this.data;
if (!imgRect.width || adaptedAreas.length === 0) return;
// 1. 正确获取触摸坐标(关键:用pageX/pageY,避免滚动影响)
const touch = e.touches[0];
const pageX = touch.pageX; // 相对于页面左上角的X坐标
const pageY = touch.pageY; // 相对于页面左上角的Y坐标
// 2. 计算点击点相对于图片左上角的坐标(核心适配)
// 图片左上角的页面坐标 = imgRect.left(X)、imgRect.top(Y)
// 点击点在图片内的坐标 = 触摸坐标 - 图片左上角坐标
const clickXInImg = pageX - imgRect.left;
const clickYInImg = pageY - imgRect.top;
console.log('touch',touch.pageX,touch.pageY)
console.log('图片位置:', imgRect);
console.log('图片内坐标:', clickXInImg, clickYInImg);
console.log('区域',adaptedAreas)
// 3. 验证点击是否在图片范围内(避免点击图片外区域误触发)
if (
clickXInImg < 0 ||
clickXInImg > imgRect.width ||
clickYInImg < 0 ||
clickYInImg > imgRect.height
) {
this.setData({ selectedArea: '' });
return;
}
// 4. 判断点击点属于哪个不规则区域
for (const area of adaptedAreas) {
if (this.isPointInPolygon(clickXInImg, clickYInImg, area.points)) {
this.setData({ selectedArea: area.name });
// 可添加额外逻辑(如跳转、弹窗)
wx.showToast({ title: `选中${area.name}`, icon: 'none' });
break;
}
}
},
// 核心算法:射线法判断点是否在多边形内
isPointInPolygon(clickX, clickY, points) {
let isInside = false;
const len = points.length;
let c = false;
for (let i = 0, j = len-1; i < len; j = i++) {
if ( ((points[i].y>clickY) != (points[j].y>clickY)) &&
(clickX < (points[j].x-points[i].x) * (clickY-points[i].y) / (points[j].y-points[i].y) + points[i].x) )
c = !c;
}
console.log('isPointInPolygon',c)
return c;
}
});