heatmap.js动画使用报错:Canvas2dRenderer._colorize源高度为0的解决方法
修复heatmap.js动画报错:Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The source height is 0
我参考heatmap.js的动画示例实现功能时,遇到了如下报错:
heatmap.js:490 Uncaught DOMException: Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The source height is 0. at Canvas2dRenderer._colorize
我的实现代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>heatmap.js</title> <style> body, html, h2 { margin: 0; padding: 0; height: 100%; } body { font-family: sans-serif; } body * { font-weight: 200; } #heatmapContainerWrapper { width: 100%; height: 100%; position: absolute; background: rgba(0, 0, 0, 0.1); } #heatmapContainer { width: 100%; height: 100%; } </style> </head> <body> <div class="timeline-wrapper"></div> <div class="heatmap"></div> <div id="heatmapContainerWrapper"> <div id="heatmapContainer"></div> </div> <script src="/build/heatmap.js"></script> <script> window.onload = function () { // creating a class to wrap the heatmap cycling logic function AnimationPlayer(options) { this.heatmap = options.heatmap this.data = options.data this.interval = null this.animationSpeed = options.animationSpeed || 300 this.wrapperEl = options.wrapperEl this.isPlaying = false this.init() } // define the prototype functions AnimationPlayer.prototype = { init: function () { var dataLen = this.data.length this.wrapperEl.innerHTML = '' var playButton = (this.playButton = document.createElement('button')) playButton.onclick = function () { if (this.isPlaying) { this.stop() } else { this.play() } this.isPlaying = !this.isPlaying }.bind(this) playButton.innerText = 'play' this.wrapperEl.appendChild(playButton) var events = document.createElement('div') events.className = 'heatmap-timeline' events.innerHTML = '' for (var i = 0; i < dataLen; i++) { var xOffset = (100 / (dataLen - 1)) * i var ev = document.createElement('div') ev.className = 'time-point' ev.style.left = xOffset + '%' ev.onclick = function (i) { return function () { this.isPlaying = false this.stop() this.setFrame(i) }.bind(this) }.bind(this)(i) events.appendChild(ev) } this.wrapperEl.appendChild(events) this.setFrame(0) }, play: function () { var dataLen = this.data.length this.playButton.innerText = 'pause' this.interval = setInterval( function () { this.setFrame(++this.currentFrame % dataLen) }.bind(this), this.animationSpeed ) }, stop: function () { clearInterval(this.interval) this.playButton.innerText = 'play' }, setFrame: function (frame) { this.currentFrame = frame var snapshot = this.data[frame] this.heatmap.setData(snapshot) var timePoints = $('.heatmap-timeline .time-point') for (var i = 0; i < timePoints.length; i++) { timePoints[i].classList.remove('active') } timePoints[frame].classList.add('active') }, setAnimationData: function (data) { this.isPlaying = false this.stop() this.data = data this.init() }, setAnimationSpeed: function (speed) { this.isPlaying = false this.stop() this.animationSpeed = speed }, } var heatmapInstance = h337.create({ container: document.querySelector('.heatmap'), }) // animationData contains an array of heatmap data objects var animationData = [] console.log(document.body.clientHeight) function generateRandomData(len) { var max = 100 var min = 1 var maxX = document.body.clientWidth var maxY = document.body.clientHeight var data = [] while (len--) { data.push({ x: (Math.random() * maxX) >> 0, y: (Math.random() * maxY) >> 0, radius: (Math.random() * 50 + min) >> 0, }) } return { max: max, min: min, data: data, } } // generate some heatmap data objects for (var i = 0; i < 20; i++) { animationData.push(generateRandomData(300)) } var player = new AnimationPlayer({ heatmap: heatmapInstance, wrapperEl: document.querySelector('.timeline-wrapper'), data: animationData, animationSpeed: 100, }) } </script> </body> </html>
问题根源
报错的核心原因是heatmap容器.heatmap没有设置宽高,导致heatmap.js初始化时无法获取有效的画布尺寸,进而调用getImageData时出现高度为0的错误。
修复步骤
为heatmap容器设置明确宽高
修改CSS,给.heatmap添加宽高属性,比如占满整个视口:.heatmap { width: 100vw; height: 100vh; position: relative; }或者直接复用已有的
#heatmapContainerWrapper容器,将heatmap实例挂载到#heatmapContainer上,删除冗余的.heatmap元素。修复jQuery依赖缺失问题
代码中setFrame方法使用了$('.heatmap-timeline .time-point'),但页面未引入jQuery,替换为原生DOM API即可:var timePoints = document.querySelectorAll('.heatmap-timeline .time-point');
其他实现heatmap.js动画的思路
- 用requestAnimationFrame替代setInterval
与浏览器刷新帧率同步,动画更流畅,避免掉帧:play: function () { this.playButton.innerText = 'pause'; this.isPlaying = true; const animate = () => { if (!this.isPlaying) return; this.setFrame(++this.currentFrame % this.data.length); requestAnimationFrame(animate); }; animate(); }, stop: function () { this.isPlaying = false; this.playButton.innerText = 'play'; } - 增量更新数据
如果帧之间数据差异小,可使用heatmapInstance.addData()增量添加数据,配合setData重置,减少渲染开销。
内容的提问来源于stack exchange,提问作者MOLLY
相关产品推荐
相关产品推荐

