目录
项目概述

项目地址:https://gitcode.com/szkygc/HarmonyOs_PC-PGC/blob/main/MyHeatMap
项目背景
在HarmonyOS应用开发中,热力图(HeatMap)是一种常见的数据可视化组件,用于展示数据的密度分布和热点区域。热力图通过颜色渐变直观地表示数据的集中程度,广泛应用于用户行为分析、地理信息展示、数据统计等场景。本项目基于Qt/QML框架,开发了一个功能完整、性能优异的热力图组件,支持交互式添加热点、权重叠加、径向渐变渲染等功能,为HarmonyOS应用提供强大的数据可视化能力。
组件特性
本项目实现的热力图组件具有以下特性:
- ✅ 交互式添加热点:点击画布即可添加热点,操作简单直观
- ✅ 权重叠加机制:同一位置多次点击会叠加权重值,真实反映数据密度
- ✅ 径向渐变渲染:每个热点使用径向渐变,中心权重高,边缘权重低,视觉效果自然
- ✅ 颜色映射系统:支持blue→cyan→green→yellow→red的渐变颜色映射,直观展示数据强度
- ✅ 网格背景:提供网格背景便于查看和定位
- ✅ 可调参数:支持自定义热点半径、整体透明度等参数
- ✅ 响应式设计:支持不同屏幕尺寸,自动适配显示
- ✅ 性能优化:使用权重表算法,优化绘制性能
技术栈选择
前端框架:Qt/QML
选择理由:
- Canvas绘制能力:QML的Canvas组件提供强大的2D绘制API,支持自定义绘制热力图
- 性能优势:Qt的渲染引擎基于OpenGL ES,Canvas绘制性能优异
- 声明式UI:QML的声明式语法使得UI开发更加直观和高效
- JavaScript支持:内置JavaScript引擎,便于实现复杂的算法逻辑
- 组件化开发:QML组件系统便于代码组织和复用
核心技术点
- QtQuick 2.15:UI框架
- Canvas API:2D绘制API,用于绘制热力图和网格
- JavaScript算法:权重计算、颜色映射等核心算法
- MouseArea:处理鼠标点击事件
- 数组和对象:存储热点数据和权重表
组件设计
整体架构
热力图组件采用分层架构设计:
Item (root)
├── Canvas (gridCanvas) - 网格背景层
│ └── 绘制黑色网格线
└── Canvas (canvas) - 热力图绘制层
└── 权重表算法 + 颜色映射绘制
└── MouseArea - 鼠标交互层
└── 点击添加热点
属性接口
组件提供了丰富的可配置属性:
property int radius: 80 // 热点半径
property int heatAlpha: 200 // 热力图整体透明度 [0-255]
property var points: [] // 热点数据数组 [{x, y, radius, count}]
property int maxCount: 1 // 最大权重值
property var colorList: [] // 颜色表(256个颜色值)
公共方法
组件提供以下公共方法供外部调用:
function addPoint(x, y, r) // 添加热点
function clear() // 清空所有热点
function initColorList() // 初始化颜色表
核心功能实现
1. 颜色表初始化
功能特点:
- 预计算256个颜色值,对应alpha值0-255
- 实现blue→cyan→green→yellow→red的渐变映射
- 支持整体透明度控制
实现原理:
function initColorList() {
var colors = []
// 创建渐变颜色映射:blue -> cyan -> green -> yellow -> red
for (var i = 0; i < 256; i++) {
var ratio = i / 255.0
var r, g, b
if (ratio < 0.4) {
// blue (0-0.4)
r = 0; g = 0; b = 255
} else if (ratio < 0.5) {
// blue -> cyan (0.4-0.5)
var t = (ratio - 0.4) / 0.1
r = 0; g = Math.floor(255 * t); b = 255
} else if (ratio < 0.6) {
// cyan -> green (0.5-0.6)
var t = (ratio - 0.5) / 0.1
r = 0; g = 255; b = Math.floor(255 * (1 - t))
} else if (ratio < 0.8) {
// green -> yellow (0.6-0.8)
var t = (ratio - 0.6) / 0.2
r = Math.floor(255 * t); g = 255; b = 0
} else {
// yellow -> red (0.8-1.0)
var t = (ratio - 0.8) / 0.2
r = 255; g = Math.floor(255 * (1 - t)); b = 0
}
// 计算透明度并格式化为#AARRGGBB
var alpha = Math.floor(heatAlpha / 255.0 * i)
var colorStr = "#" +
("00" + alpha.toString(16)).slice(-2) +
("00" + r.toString(16)).slice(-2) +
("00" + g.toString(16)).slice(-2) +
("00" + b.toString(16)).slice(-2)
colors.push(colorStr)
}
colorList = colors
}
关键技术点:
- 分段线性插值实现平滑的颜色渐变
- 使用十六进制字符串格式化颜色值
- 预计算颜色表,避免实时计算,提升性能
2. 热点添加与权重叠加
功能特点:
- 点击画布添加热点
- 同一位置多次点击会叠加权重
- 自动更新最大权重值
实现原理:
function addPoint(x, y, r) {
// 检查是否已存在该点
var found = false
for (var i = 0; i < points.length; i++) {
if (points[i].x === x && points[i].y === y) {
points[i].count++
found = true
break
}
}
if (!found) {
points.push({x: x, y: y, radius: r, count: 1})
}
// 更新最大权重
var totalCount = 0
for (var j = 0; j < points.length; j++) {
if (points[j].x === x && points[j].y === y) {
totalCount = points[j].count
break
}
}
if (totalCount > maxCount) {
maxCount = totalCount
}
// 重新绘制
canvas.requestPaint()
}
关键技术点:
- 使用数组存储热点数据,每个热点包含位置、半径和权重
- 通过坐标比较判断是否为同一位置
- 权重叠加机制真实反映数据密度
3. 权重表算法
功能特点:
- 计算每个像素的权重值
- 支持径向渐变叠加
- 优化绘制性能
实现原理:
// 创建权重表(二维数组)
var weightTable = []
for (var initY = 0; initY < height; initY++) {
weightTable[initY] = []
for (var initX = 0; initX < width; initX++) {
weightTable[initY][initX] = 0
}
}
// 计算每个像素的权重值(径向渐变叠加)
for (var i = 0; i < points.length; i++) {
var pt = points[i]
var centerX = pt.x
var centerY = pt.y
var r = pt.radius
var count = pt.count
// 遍历热点范围内的所有像素
for (var y = Math.max(0, centerY - r); y <= Math.min(height - 1, centerY + r); y++) {
for (var x = Math.max(0, centerX - r); x <= Math.min(width - 1, centerX + r); x++) {
var dx = x - centerX
var dy = y - centerY
var dist = Math.sqrt(dx * dx + dy * dy)
if (dist <= r) {
// 计算径向渐变的alpha值(从中心到边缘:1 -> 0)
var alphaRatio = 1 - (dist / r)
weightTable[y][x] += count * alphaRatio
}
}
}
}
关键技术点:
- 使用二维数组存储每个像素的权重值
- 只遍历热点范围内的像素,减少计算量
- 径向渐变通过距离计算实现,中心权重高,边缘权重低
- 多个热点可以叠加,权重值累加
4. 热力图绘制
功能特点:
- 根据权重值映射到颜色
- 逐像素绘制,精确控制颜色
- 支持透明度控制
实现原理:
// 找到最大权重值
var maxWeight = 0
for (var findY = 0; findY < height; findY++) {
for (var findX = 0; findX < width; findX++) {
if (weightTable[findY][findX] > maxWeight) {
maxWeight = weightTable[findY][findX]
}
}
}
// 根据权重值绘制热力图
if (maxWeight > 0) {
for (var drawY = 0; drawY < height; drawY++) {
for (var drawX = 0; drawX < width; drawX++) {
var weight = weightTable[drawY][drawX]
if (weight > 0) {
// 将权重转换为alpha值(0-255)
var alpha = Math.floor(weight / maxWeight * 255)
// 根据alpha值从颜色表获取颜色
var color = colorList[alpha]
ctx.fillStyle = color
ctx.fillRect(drawX, drawY, 1, 1)
}
}
}
}
关键技术点:
- 归一化权重值到0-255范围
- 使用预计算的颜色表快速查找颜色
- 只绘制有权重的像素,优化性能
5. 网格背景绘制
功能特点:
- 绘制黑色网格线,便于查看和定位
- 网格间距50像素
实现原理:
Canvas {
id: gridCanvas
anchors.fill: parent
z: 0
onPaint: {
var ctx = getContext("2d")
ctx.strokeStyle = "#000000"
ctx.lineWidth = 2
// 绘制垂直线
for (var x = 0; x <= width; x += 50) {
ctx.beginPath()
ctx.moveTo(x, 0)
ctx.lineTo(x, height)
ctx.stroke()
}
// 绘制水平线
for (var y = 0; y <= height; y += 50) {
ctx.beginPath()
ctx.moveTo(0, y)
ctx.lineTo(width, y)
ctx.stroke()
}
}
}
关键技术点:
- 使用独立的Canvas绘制网格,避免影响热力图绘制
- 通过z-index控制图层顺序
- 简单的循环绘制网格线
开发要点与技巧
1. 性能优化策略
问题: 逐像素绘制可能导致性能问题,特别是在大尺寸画布上。
解决方案:
- 权重表算法:先计算权重表,再统一绘制,避免重复计算
- 范围限制:只遍历热点范围内的像素,减少计算量
- 颜色表预计算:预计算256个颜色值,避免实时计算
- 条件绘制:只绘制有权重的像素,跳过空白区域
示例:
// 优化:只遍历热点范围内的像素
for (var y = Math.max(0, centerY - r); y <= Math.min(height - 1, centerY + r); y++) {
for (var x = Math.max(0, centerX - r); x <= Math.min(width - 1, centerX + r); x++) {
// ... 计算权重
}
}
2. 径向渐变实现
问题: Canvas API不直接支持径向渐变叠加。
解决方案:
- 使用数学计算模拟径向渐变
- 通过距离计算alpha比例
- 多个热点权重叠加
示例:
var dist = Math.sqrt(dx * dx + dy * dy)
if (dist <= r) {
// 计算径向渐变的alpha值(从中心到边缘:1 -> 0)
var alphaRatio = 1 - (dist / r)
weightTable[y][x] += count * alphaRatio
}
3. 颜色映射优化
问题: 实时计算颜色值性能较差。
解决方案:
- 预计算256个颜色值
- 使用数组存储,通过索引快速查找
- 分段线性插值实现平滑渐变
示例:
// 预计算颜色表
Component.onCompleted: {
initColorList()
}
// 使用时快速查找
var alpha = Math.floor(weight / maxWeight * 255)
var color = colorList[alpha]
4. 权重归一化
问题: 不同权重值需要映射到统一的颜色范围。
解决方案:
- 找到最大权重值
- 将所有权重值归一化到0-255范围
- 使用归一化后的值查找颜色
示例:
// 找到最大权重
var maxWeight = 0
for (var y = 0; y < height; y++) {
for (var x = 0; x < width; x++) {
if (weightTable[y][x] > maxWeight) {
maxWeight = weightTable[y][x]
}
}
}
// 归一化并绘制
var alpha = Math.floor(weight / maxWeight * 255)
5. 内存管理
问题: 权重表占用大量内存,特别是在高分辨率下。
解决方案:
- 使用JavaScript数组,自动管理内存
- 清空时重置数组,释放内存
- 考虑使用稀疏数组优化(如果热点较少)
示例:
function clear() {
points = [] // 清空热点数组
maxCount = 1 // 重置最大权重
canvas.requestPaint() // 重新绘制(权重表会在绘制时重新创建)
}
使用示例
基础使用
最简单的使用方式,使用默认配置:
import QtQuick 2.15
import QtQuick.Controls 2.15
ApplicationWindow {
width: 1600
height: 1200
visible: true
HeatMap {
anchors.fill: parent
radius: 80
}
}
完整示例
包含控制栏和参数调节的完整示例:
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
ApplicationWindow {
id: root
width: 1800
height: 1400
visible: true
title: "HeatMap"
readonly property real scaleFactor: 1.5
ColumnLayout {
anchors.fill: parent
spacing: 10 * scaleFactor
// 控制栏
RowLayout {
Layout.fillWidth: true
Layout.margins: 10 * scaleFactor
Text {
text: "Radius:"
font.pixelSize: 20 * scaleFactor
}
SpinBox {
id: radiusSpinBox
from: 10
to: 300
value: 80
font.pixelSize: 18 * scaleFactor
}
Item {
Layout.fillWidth: true
}
Button {
text: "Clear"
font.pixelSize: 18 * scaleFactor
Layout.preferredWidth: 100 * scaleFactor
Layout.preferredHeight: 40 * scaleFactor
onClicked: {
heatMap.clear()
}
}
}
// 热力图组件
HeatMap {
id: heatMap
Layout.fillWidth: true
Layout.fillHeight: true
radius: radiusSpinBox.value
}
}
}
API调用示例
HeatMap {
id: heatMap
// 添加热点
function addHotspot() {
heatMap.addPoint(400, 300, 100)
}
// 清空所有热点
function clearAll() {
heatMap.clear()
}
// 设置透明度
Component.onCompleted: {
heatMap.heatAlpha = 200
}
}
总结与展望
项目总结
本项目成功实现了一个功能完整、性能优异的热力图组件,具有以下优势:
- 功能完整:支持交互式添加热点、权重叠加、颜色映射等核心功能
- 性能优良:使用权重表算法和颜色表预计算,优化绘制性能
- 易于使用:提供简洁的API接口,集成方便
- 视觉效果佳:径向渐变和颜色映射提供直观的数据可视化效果
技术亮点
- 权重表算法:高效的权重计算和叠加机制
- 颜色映射系统:预计算颜色表,支持平滑渐变
- 径向渐变模拟:通过数学计算实现径向渐变效果
- 性能优化:范围限制、条件绘制等优化策略
未来改进方向
-
性能优化:
- 使用WebGL或Shader实现GPU加速绘制
- 实现增量更新,只重绘变化区域
- 使用Worker线程进行权重计算
-
功能扩展:
- 支持数据导入导出(JSON、CSV等格式)
- 支持多种颜色方案(自定义渐变、预设方案)
- 支持动画效果(数据变化动画)
- 支持缩放和平移操作
-
交互增强:
- 支持拖拽移动热点
- 支持删除单个热点
- 显示热点信息(权重值、位置等)
- 支持撤销/重做操作
-
可视化增强:
- 支持3D热力图
- 支持时间序列热力图
- 支持地理坐标映射
适用场景
本热力图组件适用于以下场景:
- 用户行为分析:展示用户点击、浏览等行为的分布
- 数据统计可视化:展示数据的密度分布和热点区域
- 地理信息展示:展示地理位置相关的数据分布
- 性能监控:展示系统负载、错误分布等
- 任何需要展示数据密度的场景
1512

被折叠的 条评论
为什么被折叠?



