简介:一款面向网页开发者的图片热区插件,基于 jQuery 构建,用于在图片上绘制矩形、圆形或不规则形状,并为每个热区绑定不同链接,适合在线地图、产品展示、交互式教程等场景。压缩包共 8 个文件,主要包含 JavaScript 逻辑、层叠样式表、演示页面、图片素材、配置文件及说明文档,整个包仅 209KB,轻量易用。插件支持拖放与尺寸调整,可精确创建复杂页面模型;源码带有丰富注释,便于阅读与二次开发;同时支持在集成开发环境中直接预览编辑,提高调试效率。目前已有 2271 人学习下载。随包附有演示页与说明文档,目录结构清晰,开发者能快速上手,并可通过样式表自定义热区外观或扩展接口,适合初中级前端人员和需要快速实现图片交互的团队参考。
1. 图片热区 JS 插件:图片局部可点化的选型与业务价值
当你需要在一张设备结构图、楼层平面图或产品宣传图上划分多个可点击区域时,通用的做法不是把整张图绑一个 click 事件再去做坐标换算,而是用图片热区 JS 插件把「哪个区域可点、这个区域有什么数据、点击后做什么」集中管理起来。我第一次处理这种需求是在一个巡检页面上,一张设备拆解图有十多个零件位置要分别跳详情,如果每个位置都用绝对定位硬编码,页面会很快变成一个难以维护的 DOM 堆积场。图片热区插件支持矩形、圆形、多边形三种区域,悬停和点击都有回调,适合产品图导购、楼层指引、设备标注这类场景。后面按原理、配置、踩坑的顺序逐步展开。
2. 热区实现原理:覆盖层、坐标体系与命中检测
2.1 为什么不直接给整图绑定 click:命中检测的粒度问题
给整张图片绑定一个 click 事件,再用 event.offsetX 和 event.offsetY 判断是否命中某个区域,这种做法在热区数量少的时候能用,但有几个绕不开的问题。第一,每加一个热区,事件回调里就要多一层坐标判断逻辑,图中区域一多,这段逻辑几乎没法维护。第二,hover 状态很棘手,整图绑定的是同一个事件源,要区分「当前悬停在哪个区域上」就得每次去遍历所有区域做几何判断,而且浏览器对 mousemove 的触发频率很高,重复计算会让页面有可感知的卡顿。第三,圆形、多边形这类不规则区域的命中判断公式一多,代码直接变成一坨数据与算法纠缠的泥巴。所以成熟的实现走的是「覆盖层 + 独立节点」这条路:每个热区是图片上方一个独立的绝对定位节点,浏览器自身的命中检测能力直接复用,代码只需要关心坐标和回调。
覆盖层结构说穿了就三层:外层容器 position: relative,图片和热区都在它内部;图片按正常文档流渲染,撑起容器尺寸;热区节点按配置逐个插入容器,用 position: absolute 定位。用 DOM 节点承载热区还有个好处是调试直观,浏览器开发者工具里能直接看到每个热区节点的盒模型,悬停、点击、样式问题都能单独排查。热区数量达到几十个时,DOM 节点数量仍然可控,这也是这套方案能成为主流的原因。
2.2 坐标体系:用百分比而不是像素定位
热区坐标如果写成像素值,缩一次浏览器窗口就可能全部错位。因此资源包里的坐标体系以百分比作为存储单位——所有位置和尺寸都以相对容器宽高的比例表示。矩形热区在 areas 配置里是四个字段:
{ shape: 'rect', left: 23, // 相对容器左边缘的百分比,范围 0 到 100 top: 10, // 相对容器上边缘的百分比 width: 15, // 相对容器宽度的百分比 height: 18 // 相对容器高度的百分比 }这一条配置的实际含义是:热区节点的左边距离容器左边缘 23% 处,顶边距离容器上边缘 10% 处,宽度和高度分别占容器宽高的 15% 与 18%。插件在渲染时用容器当前尺寸去换算像素,换算逻辑通常是这样的:
const rect = container.getBoundingClientRect(); const leftPx = (area.left / 100) * rect.width; const topPx = (area.top / 100) * rect.height; const widthPx = (area.width / 100) * rect.width; const heightPx = (area.height / 100) * rect.height; areaNode.style.left = leftPx + 'px'; areaNode.style.top = topPx + 'px'; areaNode.style.width = widthPx + 'px'; areaNode.style.height = heightPx + 'px';这里有一个必须提醒的细节:getBoundingClientRect 读到的是图片渲染后的实际布局尺寸,是包含边框、内边距和缩放后的最终值。容器尺寸没有稳定之前,比如图片还没有加载完成时,换算出来的热区位置全部是错的。所以插件内部第一轮渲染应该挂在图片 onload 之后,不能在脚本执行的同步阶段直接做。我自己的习惯是初始化前先检查 container.querySelector('img').complete,如果图片已经缓存命中,就立即渲染;否则等 load 事件。
用百分比还有一个隐藏收益:无论图片被放到什么尺寸的容器里,热区的相对位置都不变。桌面端、移动端、缩放浏览器窗口,改动比例是统一的。美中不足的是,如果图片本身被 CSS 拉伸成了和原始比例不一样的比例,比如正方形图被压成宽屏,那不管是像素坐标还是百分比坐标都会跟着图片一起变形。遇到这种场景,先保证图片宽高比不被 CSS 破坏,才有资格谈热区精度。
2.3 命中检测:矩形、圆形与多边形的几何判断
虽然浏览器已经替 DOM 热区节点做了命中检测,但有些场景会需要自己做坐标判断。比如热区重叠,想确认用户点的位置到底归属哪个区域;再比如某个热区节点内部要再细分几个逻辑子区域,这种时候自带的 hitTest 函数就能派上用场。我把这个函数按三种形状拆开说明。
function hitTest(area, x, y) { if (area.shape === 'rect') { const x1 = area.left; const y1 = area.top; const x2 = area.left + area.width; const y2 = area.top + area.height; return x >= x1 && x <= x2 && y >= y1 && y <= y2; } if (area.shape === 'circle') { const dx = x - area.cx; const dy = y - area.cy; return dx * dx + dy * dy <= area.r * area.r; } if (area.shape === 'polygon') { let inside = false; const pts = area.points; for (let i = 0, j = pts.length - 1; i < pts.length; j = i++) { const xi = pts[i][0]; const yi = pts[i][1]; const xj = pts[j][0]; const yj = pts[j][1]; const intersect = (yi > y) !== (yj > y) && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi; if (intersect) inside = !inside; } return inside; } return false; }矩形分支的命中条件是两个方向的区间比较,x 和 y 必须同时落在区域内。这里容易翻车的地方是坐标基准不统一:left/top 是百分比,传入的 x/y 是像素值,直接比较结果必然错。我习惯在调用 hitTest 之前先把 x/y 换算成百分比,或者把 area 的坐标临时换算成像素,两者统一之后再进入函数。
圆形分支用平方距离与半径平方比较,免去开根号,性能上更优。这个公式只关心圆心和半径,所以圆形热区的位置字段在配置里是 cx、cy、r 三个值,而不是 left/top/width/height。
多边形分支的射线法是一个通用算法:从目标点向左画一条水平射线,如果射线穿过边界的次数是奇数,说明点在多边形内部,偶数则在外部。循环中 j 从最后一个点开始,配合 i 遍历每一条边,逐边判断射线是否与边相交,状态用 inside 翻转累计。这个算法对凸多边形、凹多边形、顶点乱序都有效,前提是顶点坐标本身是准确的。调试多边形区域时,我一般把 points 打印出来,再用设计稿比对:如果形状扭曲成蝴蝶结,基本是顶点顺序写错或者相邻两点坐标抄错。
这三种形状基本覆盖了图片热区的绝大多数需求。楼层平面图的房间一般用矩形或圆形,产品结构图的不规则轮廓用多边形。在配置文档里,shape 字段只接受 rect、circle、polygon 三种取值,写错插件会直接跳过这条配置并打错误提示,这个容错机制对大面积配置热区很友好。
3. 接入与配置:容器结构、热区数据与事件回调
3.1 引入方式与最小页面结构
资源包的使用路径很直接:引入核心 JS 文件,在页面里放一个容器节点,然后调用构造函数。最小页面是下面这个样子:
<div class="hotspot-container" id="demoContainer"> <img src="device.png" alt="设备结构图" /> </div>对应的样式只需要保证容器是块级定位锚点:
.hotspot-container { position: relative; display: inline-block; }把核心文件用 script 标签引入后,初始化代码如下:
const hotspot = new Hotspot('#demoContainer', { areas: [], onClick: function(area, event) {}, onHover: function(area) {} });容器用 id 选择器传入,插件内部直接 getElementById 处理。这里有两个实际工作中容易踩的细节:一是容器的 display 不能是 flex 或 grid 之外的特殊布局状态,默认 block 或 inline-block 都行,但容器如果带了 transform 属性,会成为热区节点的定位上下文,要确认 transform 不会引起坐标偏移;二是图片要用 img 标签而不是背景图,因为插件需要读取 img 元素的 naturalWidth 和 naturalHeight 来推算图片实际渲染比例。如果用背景图,插件也能工作,但坐标系标定会麻烦不少,我建议统一用 img。
3.2 热区数据格式:坐标、label 与 data 的职责划分
areas 数组的每一项代表一个热区,完整字段用矩形示例是这样的:
{ shape: 'rect', left: 10, top: 20, width: 30, height: 15, label: '进气阀', data: { id: 101, sku: 'SP-021', price: 128 }, className: 'area-primary' }这里每个字段的角色互不重叠:shape 决定渲染什么形状和用哪些坐标字段;left/top/width/height 是矩形热区的坐标字段,circle 用 cx/cy/r,polygon 用 points 二维数组;label 主要用于悬停提示和调试面板显示,属于展示层;data 是业务数据容器,回调里直接读取,不参与渲染;className 追加到热区节点的 class 列表,用于定制样式。
这种职责划分的实质是把「热区坐标」和「业务数据」彻底解耦。页面收到一份 areas 配置后,改坐标只改配置,改跳转逻辑只动回调,两边的维护边界非常清楚。我接过一个模拟项目 X 的需求,热区坐标是运营人员在后台配置的,前端只负责把接口返回的 JSON 直接塞给插件,后续运营调整热区位置,前端一张代码都不用动。
还有一个容易被忽略的字段是 visible,值为 false 的热区不会渲染。这个字段在做「按条件显示部分热点」的需求时很有用,比如同一张产品图对应两个套餐版本,A 版本显示配件区,B 版本显示服务区。在 beforeRender 钩子里统一过滤 visible 即可,不用改 areas 原始数据。
3.3 事件绑定与回调设计:onClick、onHover 的合理用法
插件的事件回调是业务层和热区交互之间的接口。常见的完整写法是这样:
const hotspot = new Hotspot('#demoContainer', { areas: areaData, onClick: function(area, event) { const info = area.data; if (info.url) { window.location.href = info.url; } else { console.log('点击区域:', area.label, '携带数据:', info); } }, onHover: function(area, event) { const tip = document.getElementById('tooltip'); tip.textContent = area.label || ''; }, onLeave: function(area, event) { const tip = document.getElementById('tooltip'); tip.textContent = ''; } });onClick 回调接收两个参数,第一个是热区配置对象,第二个是原生事件。业务里最常见的行为就是读 area.data 里的埋点字段或跳转地址。onHover 在鼠标进入热区时触发,onLeave 在移出时触发,这两个回调适合做提示框或子区域高亮。
关于事件机制的实现方式,插件内部用的是事件委托:只在容器上绑定一个 click 和两个鼠标事件,通过 event.target.closest('.hotspot-area') 找到实际命中的热区节点。这样做的好处是热区数量再多也不会造成事件处理器爆炸,而且热区重新渲染时不需要重新绑事件。需要提一点的是,onHover 在鼠标于热区之间快速滑动时可能触发频率较高,如果提示框渲染逻辑比较复杂,建议加一个简单的防抖:
let tipTimer = null; onHover: function(area) { clearTimeout(tipTimer); tipTimer = setTimeout(() => { // 更新提示框内容 }, 50); }50ms 的延迟在视觉上几乎无感,但能避免鼠标横穿多个热区时提示框内容闪烁跳动。这个细节在热区排布密集的地图上特别明显,不加的话视觉体验会很差。
4. 交互增强与适配:hover 高亮、提示框与响应式重算
4.1 hover 高亮与 tooltip:样式层如何与事件回调配合
热区渲染完成只是第一步,多数业务场景还需要交互反馈:鼠标移上去要有点颜色或边框变化,最好再弹个小提示框显示区域名称。这类效果插件没有把样式写死在 JS 里,而是把状态类名交给开发者控制。默认热区节点上有 .hotspot-area 类,激活状态追加 .hotspot-active 类。样式可以这样写:
.hotspot-area { border: 1px dashed rgba(0, 120, 255, 0.3); transition: background-color 0.2s; } .hotspot-area.hotspot-active { background: rgba(255, 200, 0, 0.35); border-color: #ffa500; }事件回调里只需要切换类名,不直接操作内联样式。原因有两条:一是类名切换能让视觉状态和业务状态分离,二是浏览器对 class 变更的样式重算比逐条修改 style 属性要高效,也方便后续维护主题。实际项目里,我把热区高亮色和选中色做成了 CSS 变量,换肤时直接改变量定义,不用动 JS。
tooltip 的实现建议不要挂在热区节点内部,因为热区节点本身是透明覆盖层,如果它内部再放一个提示框,提示框会跟着节点一起参与坐标系变换,定位容易乱。常见做法是在页面 body 下单独挂一个 tooltip 节点,onHover 里根据 event.pageX 和 event.pageY 设置位置,并在 onLeave 里隐藏。这么做的好处是 tooltip 不受容器 overflow 属性限制,被裁剪的问题直接消失。
4.2 响应式重算:resize 监听与热区位置同步
百分比坐标理论上能跟随窗口尺寸变化自动缩放,因为热区节点定位用的就是容器宽度和高度的百分比换算结果。但这里面有一个前提:插件必须在容器尺寸变化后重新计算一次像素值。有些实现只在初始化时算一次,窗口一 resize 热区就留在原位,和图片错开。正确做法是在 resize 事件里触发整体重算:
window.addEventListener('resize', () => { hotspot.update(); });update 方法内部做两件事:重新读取容器 getBoundingClientRect(),然后遍历所有热区节点,按最新容器尺寸重新设置 left/top/width/height。这是最直观的同步方式。不过这里也有个性能问题需要处理:resize 事件在拖拽窗口时高频触发,每次全量重算会带来不必要的布局抖动。我一般会给 update 调用加个节流,简单版本是这样:
let resizeTimer = null; window.addEventListener('resize', () => { clearTimeout(resizeTimer); resizeTimer = setTimeout(() => { hotspot.update(); }, 150); });150ms 的节流足够平稳。还有更隐蔽的一种情况是容器尺寸变化不来自窗口 resize,而是来自侧边栏折叠、图片懒加载等布局变化。这种场景单纯监听 window resize 是无效的,建议在布局动作完成后手动调用一次 hotspot.update()。我自己维护的页面上,侧边栏折叠和热区重算绑定在同一个状态回调里,保证时序一致。
4.3 触摸事件适配:移动端点按与页面滚动的矛盾
移动端是图片热区最容易翻车的地方。桌面端鼠标点击就是 click,事件干净利落,移动端却要面对 click 延迟、长按手势、触摸和滚动冲突。常见适配方案是监听 touchstart/touchend,通过判断触摸位移来区分点击和滚动:
let touchStartX = 0; let touchStartY = 0; hotspot.container.addEventListener('touchstart', (event) => { const touch = event.changedTouches[0]; touchStartX = touch.clientX; touchStartY = touch.clientY; }, { passive: true }); hotspot.container.addEventListener('touchend', (event) => { const touch = event.changedTouches[0]; const deltaX = Math.abs(touch.clientX - touchStartX); const deltaY = Math.abs(touch.clientY - touchStartY); if (deltaX < 8 && deltaY < 8) { const areaNode = document.elementFromPoint(touch.clientX, touch.clientY); const node = areaNode ? areaNode.closest('.hotspot-area') : null; if (node) { const area = hotspot.getAreaByNode(node); if (area) { hotspot.onClick(area, event); } } } }, { passive: false });这个处理逻辑的核心是位移阈值:如果触摸起点和终点的位移不超过 8px,就认为是点按,否则判定为滚动。使用 elementFromPoint 从触摸坐标反推元素,再通过节点找到热区配置对象。为什么不用原生 click?因为移动端浏览器的 click 会延迟约 300ms 触发,感知上明显有卡顿;手写 touchend 后响应即时,滚动判定也更准确。这块代码片段不是插件默认行为,因为部分浏览器对 touch 事件有兼容性差异,按需开启是更稳的做法。
另外,图片长按弹出保存菜单的问题也要处理。移动端长按图片默认会弹系统菜单,体验很突兀,可以在容器上拦截 contextmenu 事件。但要注意,不能盲目对整张页面禁用 contextmenu,否则页面其他区域的右键功能会连带失活。我的做法是把禁用范围限定在图片容器内,并且用 CSS 设置 img 的 user-select 和 -webkit-touch-callout 配合处理:
.hotspot-container img { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; pointer-events: none; }给图片本身加 pointer-events: none 是个关键操作:移动端的触摸事件会穿透明图片直接落到热区节点上,热区的点击命中更准确,同时图片也不会响应长按手势。这个样式在桌面端也不会有副作用,可以全局保留。
5. 避坑记录:五类热区翻车场景与排查处理
5.1 热区全部堆在图片左上角
现象:页面加载时热区闪烁一下,然后全部堆在图片左上角,形成一个透明块叠加。
原因:插件初始化时机早于图片加载完成。图片没有加载完成时容器高度为 0,getBoundingClientRect() 返回的高度是 0,所有 top/height 换算结果要么是 0 要么是 NaN。
解决:在图片 onload 之后初始化,或者初始化前检查 img.complete。具体做法是:
const img = container.querySelector('img'); if (img.complete) { hotspot.init(); } else { img.addEventListener('load', () => hotspot.init(), { once: true }); }这个时机问题不光影响热区,也影响任何依赖图片尺寸计算位置的组件,比如图片上的标注点、商品卖点气泡。排查顺序建议是:先看容器的 clientHeight 是否正常,再谈坐标换算。如果容器的 clientHeight 是 0,那就是加载时序问题,不是坐标问题。
5.2 容器定位缺失导致热区跑出图片范围
现象:图片在页面中央显示正常,但热区节点全部渲染到页面左上角或滚动区域外部。
原因:热区节点是绝对定位,它的定位基准是最近的有定位属性的祖先元素。如果容器没有设置 position: relative,定位基准就是视口初始包含块,热区自然跑偏。
解决:容器必须设置 position: relative。这个属性最好在初始化之前就确认,不要把希望寄托在某个上级元素上——上级元素一旦有 transform 或 fixed 定位,基准也会跟着变。检查办法很简单,打开开发者工具,选中热区节点,看它的 offsetParent 是不是容器本身。如果 offsetParent 是 body 或其他元素,就说明容器没有形成定位上下文。
5.3 图片使用了 object-fit,热区按普通比例换算后错位
现象:容器设置了固定宽高,图片用 object-fit: cover 填充,显示上图片内容是居中裁剪的,但热区坐标仍然按容器整体宽高换算,结果热区与图片实际内容对不上。
原因:object-fit: cover 让图片内容在容器内缩放并居中,图片的显示区域已经不等于容器区域,比例关系被裁切和留白打破。
解决:此时不能用「容器宽高 + 百分比」这套基础换算,要把图片的显示区域单独计算出来。公式是:先算图片原始宽高比,再算容器宽高比,按 cover 的规则求出图片实际渲染尺寸和偏移量,热区坐标以这个显示区域为基准重新换算。
const img = container.querySelector('img'); const cw = container.clientWidth; const ch = container.clientHeight; const ir = img.naturalWidth / img.naturalHeight; const cr = cw / ch; let displayW, displayH, offsetX, offsetY; if (ir > cr) { displayW = cw; displayH = cw / ir; offsetX = 0; offsetY = (ch - displayH) / 2; } else { displayH = ch; displayW = ch * ir; offsetX = (cw - displayW) / 2; offsetY = 0; } // 热区坐标基于 displayW/displayH 换算,并叠加上 offsetX/offsetY这段逻辑解释一下:ir 是图片原始宽高比,cr 是容器宽高比。当 ir 大于 cr 时,图片按容器宽度铺满,高度会被裁切,上下留出偏移 offsetY;反过来则按容器高度铺满,左右留出偏移 offsetX。所有热区百分比坐标都应针对 displayW 和 displayH 计算,渲染到页面上时再加上 offsetX 和 offsetY。如果项目对兼容性要求不高,更省事的方案是放弃 object-fit,让图片原比例渲染,热区坐标才稳定可靠。我的实际经验是:真的要精确热区,就别对 img 用 object-fit,让图片按原始宽高比自适应,后面省掉一堆换算。
5.4 热区重叠时点击命中区域总是固定一个
现象:两个热区存在交叠区域,点击交叠处时,永远触发其中一个区域的回调,另一个区域完全拿不到点击。
原因:热区节点按 DOM 顺序渲染,后面的节点天然覆盖前面的节点,浏览器只把事件分发给最上层的元素。这不是插件 bug,是 CSS 叠放机制的正常表现。
解决:确定热区层级优先级。比如想让某个热区优先响应,就把它放在 areas 数组靠后位置,或者动态调整它的 z-index。重叠场景里最常见的需求是「小面积热区优先于大面积热区」,这时渲染前把 areas 按面积从小到大排序即可,面积小的渲染在更上层,天然优先命中。排序逻辑写在 beforeRender 里,数据不需要改:
beforeRender: function(areas) { return areas.sort((a, b) => { const areaA = a.width * a.height; const areaB = b.width * b.height; return areaA - areaB; }); }这个排序只对矩形和圆形有意义,多边形没有直接的面积字段,需要先按顶点算出面积再排序。如果业务里没有明确要求哪个热区优先,那统一按面积从小到大排序最符合直觉:小目标本来就难点,让它更容易命中。
5.5 移动端点击热区变成滚动或弹出系统菜单
现象:手机浏览器里点击热区时,页面先滚动了一下,热区点击没有触发;长按热区图片时,系统弹出「存储图像」菜单。
原因:原生 touch 事件与滚动行为冲突,未做位移判断就过早判定为点击;图片元素默认支持系统长按手势,热区节点被图片的事件覆盖。
解决:按第 4 章的方案,在 touchend 里用位移阈值区分点击和滚动;同时给图片设置 pointer-events: none、-webkit-touch-callout: none。这里要特别注意的是,试图通过给热区节点绑定 click 来绕过触摸冲突效果不好,click 在移动端有延迟且滚动手势对它不敏感,体验依旧僵硬。触摸逻辑应该走 touchstart/touchend 那一套,别偷懒。这个坑在测试机上最容易暴露,真机调试的时候先用手势横向滑动再回来说点击,如果滑动结束后热区还能响应点击,说明位移判定逻辑起作用了。
6. 热区体验优化的三个技巧:可视化调试、坐标批处理与状态联动
6.1 可视化调试:打开边框与 label 面板
热区坐标是配置数据,看不见摸不着,出问题很难直觉定位。资源包内置了一个 debug 方法,开启后会给所有热区加上明显边框,并直接把 label 文本渲染到区域中央。我接新项目时第一件事就是打开它:
hotspot.setDebug(true);验证流程是:先看边框是否贴合目标区域,再开着页面拖动窗口观察热区是否跟随图片缩放。边框位置有偏差说明坐标基准有问题,偏差方向能直接告诉我是百分比换算错误还是容器定位错误。调完再 setDebug(false) 关掉,不影响线上。
6.2 坐标批处理:从设计稿标注生成 areas 配置
手写坐标容易出错,批量场景可以用简单脚本生成。设计稿里通常会给标注图层,比如每个热区用矩形框选区域后用像素坐标导出。常见做法是把标注数据转成 JSON,再用一段脚本统一转百分比:
const baseWidth = 1200; const baseHeight = 800; const rawCoords = [ { x: 100, y: 200, w: 150, h: 80, label: '区域A' }, { x: 400, y: 300, w: 80, h: 120, label: '区域B' } ]; const areas = rawCoords.map((item) => ({ shape: 'rect', left: Number((item.x / baseWidth * 100).toFixed(2)), top: Number((item.y / baseHeight * 100).toFixed(2)), width: Number((item.w / baseWidth * 100).toFixed(2)), height: Number((item.h / baseHeight * 100).toFixed(2)), label: item.label, data: item.data || {} }));这段脚本的基准是设计稿原始像素宽高。只要页面容器里图片的宽高比和设计稿一致,转换结果就能直接用。如果图片在页面里被压缩过,基准改为容器实际渲染尺寸即可。
6.3 状态联动:多个热区互斥高亮与选中态
产品图导购场景常常要求点击某个零件后,其他零件的高亮全部取消,只保留当前选中项。这个逻辑在插件回调里写很简单,核心是维护一个「当前选中项」的状态变量:
let currentArea = null; const hotspot = new Hotspot('#demoContainer', { areas: areaData, onClick: function(area) { if (currentArea) { currentArea.node.classList.remove('hotspot-selected'); } area.node.classList.add('hotspot-selected'); currentArea = area; } });热区配置对象经过插件渲染后,会自动挂上 node 属性指向对应的 DOM 节点,因此状态切换完全不需要查 DOM。这个技巧的核心在于把热区数据和业务状态绑定在配置对象上,不用反复 getElementById。
从第一次在模拟项目 X 中接图片热区需求到现在,我每次写完新页面都会强制过一遍同样的流程:确认图片加载时序、确认容器定位、确认热区坐标基准、打开 debug 验证一遍交互、再关闭 debug 检查样式。这套流程虽然简单,却帮我拦住过不少上线后才被发现的热区错位问题。希望帮到你。
本文还有配套的精品资源,点击获取