热点标记组件

一个功能丰富的图像热点标记组件,支持交互式热点标注、自定义提示和模态框详情

组件简介

CutestUI热点标记组件是一个专为网页设计的交互式图像标记工具,允许开发者在图片上创建各种类型的热点标记点。该组件支持自定义样式、提示框、交互动画和详情模态框,为用户提供直观的图像导航和信息展示体验。适用于地图标注、产品展示、建筑平面图、医学图像标记等多种场景。

组件特性

  • 支持多种热点类型(主色调、成功、警告、信息)
  • 自定义热点大小(小、默认、大、超大)
  • 精美的脉冲动画效果
  • 悬停显示详细提示信息
  • 点击热点打开详情模态框
  • 响应式设计,适配各种屏幕尺寸
  • 支持自定义热点内容(文本、图标等)
  • 缩放区域功能,支持局部放大查看
  • 完整的无障碍支持和打印样式

基本使用示例

示例图片
基本热点标记
主要热点
成功热点
警告热点
信息热点
小热点
大热点
!
超大热点

高级使用示例

高级示例图片
产品详情
服务介绍

HTML结构

<!-- 基础图像映射容器 -->
<div class="cutest-image-map">
    <img src="image.jpg" alt="示例图片">
    
    <!-- 基本热点标记 -->
    <div class="cutest-hotspot" style="top: 30%; left: 20%;">
        <div class="cutest-hotspot-tooltip">热点提示信息</div>
    </div>
    
    <!-- 不同类型的热点 -->
    <div class="cutest-hotspot type-primary" style="top: 45%; left: 60%;"></div>
    <div class="cutest-hotspot type-success" style="top: 65%; left: 30%;"></div>
    <div class="cutest-hotspot type-warning" style="top: 20%; left: 70%;"></div>
    <div class="cutest-hotspot type-info" style="top: 55%; left: 85%;"></div>
    
    <!-- 不同大小的热点 -->
    <div class="cutest-hotspot size-small" style="top: 35%; left: 45%;"></div>
    <div class="cutest-hotspot size-large" style="top: 70%; left: 60%;"></div>
    <div class="cutest-hotspot size-xl" style="top: 15%; left: 40%;">
        <div class="cutest-hotspot-content">!</div>
    </div>
    
    <!-- 缩放区域 -->
    <div class="cutest-zoom-area" data-zoom="1.5" style="top: 25%; left: 15%; width: 25%; height: 30%;"></div>
</div>

<!-- 模态框结构 -->
<div class="cutest-image-modal" id="hotspotModal">
    <div class="cutest-modal-content">
        <div class="cutest-modal-header">
            <h3 class="cutest-modal-title" id="modalTitle">热点详情</h3>
            <button class="cutest-modal-close" id="closeModal">×</button>
        </div>
        <div class="cutest-modal-body" id="modalBody">
            <!-- 模态框内容将通过JavaScript动态填充 -->
        </div>
    </div>
</div>

JavaScript实现

// CutestUI 热点标记组件 JavaScript实现
class CutestImageMap {
    constructor(containerSelector, options = {}) {
        this.container = document.querySelector(containerSelector);
        this.image = this.container.querySelector('img');
        this.hotspots = this.container.querySelectorAll('.cutest-hotspot');
        this.zoomAreas = this.container.querySelectorAll('.cutest-zoom-area');
        this.modal = document.getElementById('hotspotModal');
        this.modalTitle = document.getElementById('modalTitle');
        this.modalBody = document.getElementById('modalBody');
        this.closeModalBtn = document.getElementById('closeModal');
        
        this.options = {
            zoomFactor: 1.5,
            hotspotData: {},
            ...options
        };
        
        this.currentZoom = 1;
        this.initEvents();
    }
    
    initEvents() {
        // 热点点击事件
        this.hotspots.forEach(hotspot => {
            hotspot.addEventListener('click', () => this.handleHotspotClick(hotspot));
        });
        
        // 缩放区域点击事件
        this.zoomAreas.forEach(area => {
            area.addEventListener('click', () => this.handleZoomAreaClick(area));
        });
        
        // 模态框关闭事件
        if (this.closeModalBtn) {
            this.closeModalBtn.addEventListener('click', () => this.closeModal());
        }
        
        // 点击模态框背景关闭
        if (this.modal) {
            this.modal.addEventListener('click', (e) => {
                if (e.target === this.modal) {
                    this.closeModal();
                }
            });
        }
    }
    
    handleHotspotClick(hotspot) {
        const hotspotId = hotspot.dataset.hotspotId;
        const hotspotData = this.options.hotspotData[hotspotId];
        
        if (hotspotData && this.modal) {
            // 填充模态框内容
            this.modalTitle.textContent = hotspotData.title || '热点详情';
            this.modalBody.innerHTML = `
                <div class="cutest-hotspot-details">
                    ${hotspotData.description ? `<p class="cutest-hotspot-description">${hotspotData.description}</p>` : ''}
                    ${hotspotData.details ? hotspotData.details : ''}
                    ${hotspotData.meta ? this.renderMeta(hotspotData.meta) : ''}
                </div>
            `;
            
            // 显示模态框
            this.modal.classList.add('show');
        }
        
        // 添加激活状态
        this.hotspots.forEach(h => h.classList.remove('active'));
        hotspot.classList.add('active');
    }
    
    handleZoomAreaClick(area) {
        const zoom = parseFloat(area.dataset.zoom) || this.options.zoomFactor;
        this.currentZoom = zoom;
        
        // 计算缩放中心
        const rect = area.getBoundingClientRect();
        const containerRect = this.container.getBoundingClientRect();
        
        const centerX = (rect.left - containerRect.left + rect.width / 2) / this.image.offsetWidth;
        const centerY = (rect.top - containerRect.top + rect.height / 2) / this.image.offsetHeight;
        
        // 应用缩放和定位
        this.image.style.transform = `scale(${zoom})`;
        this.image.style.transformOrigin = `${centerX * 100}% ${centerY * 100}%`;
    }
    
    renderMeta(meta) {
        let metaHtml = '<div class="cutest-hotspot-meta">';
        Object.entries(meta).forEach(([key, value]) => {
            metaHtml += `<span class="cutest-hotspot-meta-item">${key}: ${value}</span>`;
        });
        metaHtml += '</div>';
        return metaHtml;
    }
    
    closeModal() {
        if (this.modal) {
            this.modal.classList.remove('show');
        }
    }
    
    toggleHotspots() {
        const isHidden = this.hotspots[0].style.display === 'none';
        this.hotspots.forEach(hotspot => {
            hotspot.style.display = isHidden ? 'flex' : 'none';
        });
        return !isHidden;
    }
    
    zoomIn() {
        this.currentZoom += 0.2;
        this.image.style.transform = `scale(${this.currentZoom})`;
    }
    
    zoomOut() {
        this.currentZoom = Math.max(1, this.currentZoom - 0.2);
        this.image.style.transform = `scale(${this.currentZoom})`;
    }
    
    resetZoom() {
        this.currentZoom = 1;
        this.image.style.transform = 'scale(1)';
        this.image.style.transformOrigin = 'center center';
    }
}

// 使用示例
document.addEventListener('DOMContentLoaded', function() {
    // 初始化高级示例
    const advancedMap = new CutestImageMap('#advancedImageMap', {
        hotspotData: {
            'detail1': {
                title: '产品详情',
                description: '这是我们的旗舰产品,具有高性能、低功耗的特点。',
                meta: {
                    '型号': 'Pro Max',
                    '价格': '¥9999',
                    '库存': '充足'
                }
            },
            'detail2': {
                title: '服务介绍',
                description: '我们提供全方位的售后服务支持,包括24小时技术支持和上门维修服务。',
                meta: {
                    '响应时间': '24小时',
                    '保修期': '2年',
                    '满意度': '98%'
                }
            }
        }
    });
    
    // 绑定控制按钮事件
    document.getElementById('toggleHotspots').addEventListener('click', () => {
        advancedMap.toggleHotspots();
    });
    
    document.getElementById('zoomIn').addEventListener('click', () => {
        advancedMap.zoomIn();
    });
    
    document.getElementById('zoomOut').addEventListener('click', () => {
        advancedMap.zoomOut();
    });
    
    document.getElementById('resetZoom').addEventListener('click', () => {
        advancedMap.resetZoom();
    });
});

使用指南

基本设置

  1. 引入CSS文件:<link rel="stylesheet" href="/static/css/image-map.css">
  2. 创建图像映射容器:<div class="cutest-image-map"><img src="image.jpg"></div>
  3. 添加热点标记:<div class="cutest-hotspot" style="top: 30%; left: 20%;"></div>

热点类型

  • .type-primary - 主色调热点(蓝色)
  • .type-success - 成功热点(绿色)
  • .type-warning - 警告热点(橙色)
  • .type-info - 信息热点(青色)

热点大小

  • .size-small - 小热点(16px)
  • .size-large - 大热点(28px)
  • .size-xl - 超大热点(40px)

热点定位

使用CSS的topleft属性(百分比值)定位热点在图像中的位置。例如:style="top: 30%; left: 20%;"

JavaScript初始化

// 简单初始化
const imageMap = new CutestImageMap('#yourImageMap');

// 带选项的初始化
const imageMap = new CutestImageMap('#yourImageMap', {
    zoomFactor: 1.5, // 默认缩放因子
    hotspotData: {   // 热点数据
        'hotspot1': {
            title: '热点标题',
            description: '热点描述内容',
            meta: {
                '属性1': '值1',
                '属性2': '值2'
            }
        }
    }
});

主题定制

CutestUI热点标记组件支持通过修改CSS变量来定制主题颜色。以下是主要的定制选项:

/* 自定义热点颜色 */
.cutest-hotspot {
    /* 基础红色热点 */
    --hotspot-primary-bg: rgba(255, 77, 77, 0.7);
    --hotspot-primary-hover: rgba(255, 77, 77, 0.9);
}

.cutest-hotspot.type-primary {
    /* 蓝色热点 */
    --hotspot-primary-bg: rgba(77, 153, 255, 0.7);
    --hotspot-primary-hover: rgba(77, 153, 255, 0.9);
}

/* 自定义脉冲动画 */
@keyframes custom-pulse {
    0% {
        transform: translate(-50%, -50%) scale(1);
        opacity: 1;
    }
    100% {
        transform: translate(-50%, -50%) scale(2.5);
        opacity: 0;
    }
}

.cutest-hotspot.custom-pulse::before {
    animation: custom-pulse 1.5s infinite;
}