Echarts热力图自定义底图

需求

看下本次的需求:
希望实现如下图的热力图的展示:
在这里插入图片描述
但是个性化的是需要自定义底图,并且实现摇杆的功能。
在这里插入图片描述
我们来看一下echarts的文档:
https://echarts.apache.org/examples/zh/index.html#chart-type-heatmap
在提供的热力图的case中,只有两种类型,一种是基于百度底图进行扩展的,另一种是直角坐标系下的方块图。
在github的issue中我们可以看到作者的回复,在echarts3.0版本中是没有提供一个自定义底图的底图热力图版本的。
在这里插入图片描述
在这里插入图片描述

ECHARTS2.0的case

很多人说2.0版本实现了这个功能,我们来看一下2.0版本,在网上找到了一个2.0版本的case,看下运行效果图:
在这里插入图片描述显然,这个版本的case可以实现两张图叠加的效果(case地址:https://github.com/feddiyao/echarts2.0-case
显然,我们可以用热力图来进行叠加,看一下实现代码:

 function test(heatData) {
         require.config({
             paths: {
                 echarts: './static/echarts/src'
             }
         });
         require(
             [
                 'echarts',
                 'echarts/chart/heatmap'
             ],
             function (ec) {
                 // 基于准备好的dom,初始化echarts图表
                 var myChart = ec.init(document.getElementById('main'));
                 var option = {
                     series: [
                         {
                             type: 'heatmap',
                             data: heatData,
                             hoverable: false,
                             gradientColors: ['green', 'yellow', 'orange', 'red'],
                             minAlpha: 0.2,
                             valueScale: 2,
                             opacity: 1,
                             blurSize: 5
                         }
                     ]
                 };

                 // 为echarts对象加载数据 
                 myChart.setOption(option);
             }

         );

     };

看下运行结果:
在这里插入图片描述
叠加已经完成了,但是注意产品经理的需求,我们需要摇杆,我们的颜色展示是基于密度的,看下echarts的源码:

define("echarts/chart/heatmap", ["require", "./base", "../layer/heatmap", "../config", "../util/ecData", "zrender/tool/util", "zrender/tool/color", "zrender/shape/Image", "../chart"],
function(e) {
	function t(e, t, n, a, o) {
		i.call(this, e, t, n, a, o),
		this.refresh(a)
	}
	var i = e("./base"),
	n = e("../layer/heatmap"),
	a = e("../config"),
	o = (e("../util/ecData"), e("zrender/tool/util")),
	r = (e("zrender/tool/color"), e("zrender/shape/Image"));
	return a.heatmap = {
		zlevel: 0,
		z: 2,
		clickable: !0
	},
	t.prototype = {
		type: a.CHART_TYPE_HEATMAP,
		refresh: function(e) {
			this.clear(),
			e && (this.option = e, this.series = e.series),
			this._init()
		},
		_init: function() {
			var e = this.series;
			this.backupShapeList();
			for (var t = e.length,
			i = 0; t > i; ++i) if (e[i].type === a.CHART_TYPE_HEATMAP) {
				e[i] = this.reformOption(e[i]);
				var o = new n(e[i]),
				s = o.getCanvas(e[i].data, this.zr.getWidth(), this.zr.getHeight()),
				l = new r({
					position: [0, 0],
					scale: [1, 1],
					hoverable: this.option.hoverable,
					style: {
						x: 0,
						y: 0,
						image: s,
						width: s.width,
						height: s.height
					}
				});
				this.shapeList.push(l)
			}
			this.addShapeList()
		}
	},
	o.inherits(t, i),
	e("../chart").define("heatmap", t),
	t
}),
define("echarts/layer/heatmap", ["require"],
function() {
	function e(e) {
		if (this.option = e, e) for (var i in t) this.option[i] = void 0 !== e[i] ? e[i] : t[i];
		else this.option = t
	}
	var t = {
		blurSize: 30,
		gradientColors: ["blue", "cyan", "lime", "yellow", "red"],
		minAlpha: .05,
		valueScale: 1,
		opacity: 1
	},
	i = 20,
	n = 256;
	return e.prototype = {
		getCanvas: function(e, t, a) {
			var o = this._getBrush(),
			r = this._getGradient(),
			s = i + this.option.blurSize,
			l = document.createElement("canvas");
			l.width = t,
			l.height = a;
			for (var h = l.getContext("2d"), m = e.length, V = 0; m > V; ++V) {
				var d = e[V],
				U = d[0],
				p = d[1],
				c = d[2],
				u = Math.min(1, Math.max(c * this.option.valueScale || this.option.minAlpha, this.option.minAlpha));
				h.globalAlpha = u,
				h.drawImage(o, U - s, p - s)
			}
			for (var g = h.getImageData(0, 0, l.width, l.height), y = g.data, m = y.length / 4; m--;) {
				var b = 4 * m + 3,
				u = y[b] / 256,
				f = Math.floor(u * (n - 1));
				y[b - 3] = r[4 * f],
				y[b - 2] = r[4 * f + 1],
				y[b - 1] = r[4 * f + 2],
				y[b] *= this.option.opacity
			}
			return h.putImageData(g, 0, 0),
			l
		},
		_getBrush: function() {
			if (!this._brushCanvas) {
				this._brushCanvas = document.createElement("canvas");
				var e = i + this.option.blurSize,
				t = 2 * e;
				this._brushCanvas.width = t,
				this._brushCanvas.height = t;
				var n = this._brushCanvas.getContext("2d");
				n.shadowOffsetX = t,
				n.shadowBlur = this.option.blurSize,
				n.shadowColor = "black",
				n.beginPath(),
				n.arc( - e, e, i, 0, 2 * Math.PI, !0),
				n.closePath(),
				n.fill()
			}
			return this._brushCanvas
		},
		_getGradient: function() {
			if (!this._gradientPixels) {
				var e = n,
				t = document.createElement("canvas");
				t.width = 1,
				t.height = e;
				for (var i = t.getContext("2d"), a = i.createLinearGradient(0, 0, 0, e), o = this.option.gradientColors.length, r = 0; o > r; ++r)"string" == typeof this.option.gradientColors[r] ? a.addColorStop((r + 1) / o, this.option.gradientColors[r]) : a.addColorStop(this.option.gradientColors[r].offset, this.option.gradientColors[r].color);
				i.fillStyle = a,
				i.fillRect(0, 0, 1, e),
				this._gradientPixels = i.getImageData(0, 0, 1, e).data
			}
			return this._gradientPixels
		}
	},
	e
}),
define("echarts/layer/heatmap", ["require"],
function() {
	function e(e) {
		if (this.option = e, e) for (var i in t) this.option[i] = void 0 !== e[i] ? e[i] : t[i];
		else this.option = t
	}
	var t = {
		blurSize: 30,
		gradientColors: ["blue", "cyan", "lime", "yellow", "red"],
		minAlpha: .05,
		valueScale: 1,
		opacity: 1
	},
	i = 20,
	n = 256;
	return e.prototype = {
		getCanvas: function(e, t, a) {
			var o = this._getBrush(),
			r = this._getGradient(),
			s = i + this.option.blurSize,
			l = document.createElement("canvas");
			l.width = t,
			l.height = a;
			for (var h = l.getContext("2d"), m = e.length, V = 0; m > V; ++V) {
				var d = e[V],
				U = d[0],
				p = d[1],
				c = d[2],
				u = Math.min(1, Math.max(c * this.option.valueScale || this.option.minAlpha, this.option.minAlpha));
				h.globalAlpha = u,
				h.drawImage(o, U - s, p - s)
			}
			for (var g = h.getImageData(0, 0, l.width, l.height), y = g.data, m = y.length / 4; m--;) {
				var b = 4 * m + 3,
				u = y[b] / 256,
				f = Math.floor(u * (n - 1));
				y[b - 3] = r[4 * f],
				y[b - 2] = r[4 * f + 1],
				y[b - 1] = r[4 * f + 2],
				y[b] *= this.option.opacity
			}
			return h.putImageData(g, 0, 0),
			l
		},
		_getBrush: function() {
			if (!this._brushCanvas) {
				this._brushCanvas = document.createElement("canvas");
				var e = i + this.option.blurSize,
				t = 2 * e;
				this._brushCanvas.width = t,
				this._brushCanvas.height = t;
				var n = this._brushCanvas.getContext("2d");
				n.shadowOffsetX = t,
				n.shadowBlur = this.option.blurSize,
				n.shadowColor = "black",
				n.beginPath(),
				n.arc( - e, e, i, 0, 2 * Math.PI, !0),
				n.closePath(),
				n.fill()
			}
			return this._brushCanvas
		},
		_getGradient: function() {
			if (!this._gradientPixels) {
				var e = n,
				t = document.createElement("canvas");
				t.width = 1,
				t.height = e;
				for (var i = t.getContext("2d"), a = i.createLinearGradient(0, 0, 0, e), o = this.option.gradientColors.length, r = 0; o > r; ++r)"string" == typeof this.option.gradientColors[r] ? a.addColorStop((r + 1) / o, this.option.gradientColors[r]) : a.addColorStop(this.option.gradientColors[r].offset, this.option.gradientColors[r].color);
				i.fillStyle = a,
				i.fillRect(0, 0, 1, e),
				this._gradientPixels = i.getImageData(0, 0, 1, e).data
			}
			return this._gradientPixels
		}
	},
	e
});

_getBrush方法按照高斯模糊的设定进行刷子生成, _getGradient为热点的地方进行涂色。

按照权重选择颜色展示的热力图

但是我们要求的是,按照权重比例进行涂色,我们需要对源码进行修改,看下修改后的逻辑:

define("echarts/chart/heatmap", ["require", "./base", "../layer/heatmap", "../config", "../util/ecData", "zrender/tool/util", "zrender/tool/color", "zrender/shape/Image", "../chart"], function(e) {
    function t(e, t, n, a, o) {
        i.call(this, e, t, n, a, o), this.refresh(a)
    }
    var i = e("./base"),
        n = e("../layer/heatmap"),
        a = e("../config"),
        o = (e("../util/ecData"), e("zrender/tool/util")),
        r = (e("zrender/tool/color"), e("zrender/shape/Image"));
    return a.heatmap = {
        zlevel: 0,
        z: 2,
        clickable: !0
    }, t.prototype = {
        type: a.CHART_TYPE_HEATMAP,
        refresh: function(e) {
            this.clear(), e && (this.option = e, this.series = e.series), this._init()
        },
        _init: function() {
            var e = this.series;
            this.backupShapeList();
            for (var t = e.length, i = 0; t > i; ++i)
                if (e[i].type === a.CHART_TYPE_HEATMAP) {
                    e[i] = this.reformOption(e[i]);
                    var o = new n(e[i]),
                        s = o.getCanvas(e[i].data, this.zr.getWidth(), this.zr.getHeight()),
                        l = new r({
                            position: [0, 0],
                            scale: [1, 1],
                            hoverable: this.option.hoverable,
                            style: {
                                x: 0,
                                y: 0,
                                image: s,
                                width: s.width,
                                height: s.height
                            }
                        });
                    this.shapeList.push(l)
                }
            this.addShapeList()
        }
    }, o.inherits(t, i), e("../chart").define("heatmap", t), t
}), define("echarts/layer/heatmap", ["require"], function() {
    function e(e) {
        if (this.option = e, e)
            for (var i in t) this.option[i] = void 0 !== e[i] ? e[i] : t[i];
        else this.option = t
    }
    var t = {
            blurSize: 30,
            gradientColors: ["blue", "cyan", "lime", "yellow", "red"],
            minAlpha: .05,
            valueScale: 1,
            opacity: 1
        },
        i = 2,
        n = 256;
    return e.prototype = {
        getCanvas: function(e, t, a) {
            var o = this._getBrush(),
                r = this._getGradient(),
                s = i + this.option.blurSize,
                l = document.createElement("canvas");
            l.width = t, l.height = a;
            for (var h = l.getContext("2d"), m = e.length, V = 0; m > V; ++V) {
                var d = e[V],
                    U = d[0],
                    p = d[1],
                    c = d[2],
                    //u的值为权重 * 价值量和低通滤波的最大值 再和1 取最小值
                    u = Math.min(1, Math.max(c * this.option.valueScale || this.option.minAlpha, this.option.minAlpha));
                    //getImageData获得的是四通道的数据
                    for (var g = o.getContext("2d").getImageData(0, 0, o.width, o.height), y = g.data, k = y.length / 4; k --;) {
                        var b = 4 * k + 3,
                            q = (c -  this.option.min) / (this.option.max - this.option.min),
                            f = Math.floor(q * r.length / 4);
                        y[b - 3] = r[f * 4], y[b - 2] = r[f * 4 + 1], y[b - 1] = r[f * 4 + 2], y[b] *= this.option.opacity
                    }
                    o.getContext("2d").putImageData(g, 0, 0)

                //context.drawImage(img,x,y);
                h.globalAlpha = u, h.drawImage(o, U - s , p - s)
            }
            return h, l
        },
        //获取刷子,在一个2*e边长的正方形上画圆,
        _getBrush: function() {
            if (!this._brushCanvas) {
                this._brushCanvas = document.createElement("canvas");
                //内径是i 设定均值滤波blursize,模糊后画圆,模糊圆半径为e
                var e = i + this.option.blurSize,
                    t = 2 * e;
                //在长宽为2e的画布上画圆
                this._brushCanvas.width = t, this._brushCanvas.height = t;
                var n = this._brushCanvas.getContext("2d");
                //shadowOffsetX 设置或返回形状与阴影的水平距离,shadowBlur 属性设置或返回阴影的模糊级数 arc(圆中心的X坐标,圆中心的y坐标,半径,起始角,结束角)
                n.shadowOffsetX = t, n.shadowBlur = this.option.blurSize, n.shadowColor = "black", n.beginPath(), n.arc(-e, e, i, 0, 2 * Math.PI, !0), n.closePath(), n.fill()
            }
            return this._brushCanvas
        },
        _getGradient: function() {
            if (!this._gradientPixels) {
                var e = n,
                    t = document.getElementById("myCanvas");
                t.width = e, t.height = 5;
                // 定义渐变点createLinearGradient(渐变开始点x坐标,渐变开始点y坐标,渐变结束点x坐标,渐变结束点y坐标)offset是每个颜色所占的比重
                for (var i = t.getContext("2d"), a = i.createLinearGradient(0, 0, e, 0), o = this.option.gradientColors.length, r = 0; o > r; ++r) "string" == typeof this.option.gradientColors[r] ? a.addColorStop((r + 1) / o, this.option.gradientColors[r]) : a.addColorStop(this.option.gradientColors[r].offset, this.option.gradientColors[r].color);
                //fillRect(x,y,width,height) 矩形填充颜色
                i.fillStyle = a, i.fillRect(0, 0, e, 5), this._gradientPixels = i.getImageData(0, 0, e, 5).data
            }
            return this._gradientPixels
        }
    }, e
}), define("echarts/layer/heatmap", ["require"], function() {
    function e(e) {
        if (this.option = e, e)
            for (var i in t) this.option[i] = void 0 !== e[i] ? e[i] : t[i];
        else this.option = t
    }
    var t = {
            blurSize: 30,
            gradientColors: ["blue", "cyan", "lime", "yellow", "red"],
            minAlpha: .05,
            valueScale: 1,
            opacity: 1
        },
        i = 20,
        n = 256;
    return e.prototype = {
        getCanvas: function(e, t, a) {
            var o = this._getBrush(),
                r = this._getGradient(),
                s = i + this.option.blurSize,
                l = document.createElement("canvas");
            l.width = t, l.height = a;
            for (var h = l.getContext("2d"), m = e.length, V = 0; m > V; ++V) {
                var d = e[V],
                    U = d[0],
                    p = d[1],
                    c = d[2],
                    u = Math.min(1, Math.max(c * this.option.valueScale || this.option.minAlpha, this.option.minAlpha));
                h.globalAlpha = u, h.drawImage(o, U - s, p - s)
            }
            for (var g = h.getImageData(0, 0, l.width, l.height), y = g.data, m = y.length / 4; m--;) {
                var b = 4 * m + 3,
                    u = y[b] / 256,
                    f = Math.floor(u * (n - 1));
                y[b - 3] = r[4 * f], y[b - 2] = r[4 * f + 1], y[b - 1] = r[4 * f + 2], y[b] *= this.option.opacity
            }
            return h.putImageData(g, 0, 0), l
        },
        _getBrush: function() {
            if (!this._brushCanvas) {
                this._brushCanvas = document.createElement("canvas");
                var e = i + this.option.blurSize,
                    t = 2 * e;
                this._brushCanvas.width = t, this._brushCanvas.height = t;
                var n = this._brushCanvas.getContext("2d");
                n.shadowOffsetX = t, n.shadowBlur = this.option.blurSize, n.shadowColor = "black", n.beginPath(), n.arc(-e, e, i, 0, 2 * Math.PI, !0), n.closePath(), n.fill()
            }
            return this._brushCanvas
        },
        _getGradient: function() {
            if (!this._gradientPixels) {
                var e = n,
                    t = document.createElement("canvas");
                t.width = 1, t.height = e;
                for (var i = t.getContext("2d"), a = i.createLinearGradient(0, 0, 0, e), o = this.option.gradientColors.length, r = 0; o > r; ++r) "string" == typeof this.option.gradientColors[r] ? a.addColorStop((r + 1) / o, this.option.gradientColors[r]) : a.addColorStop(this.option.gradientColors[r].offset, this.option.gradientColors[r].color);
                i.fillStyle = a, i.fillRect(0, 0, 1, e), this._gradientPixels = i.getImageData(0, 0, 1, e).data
            }
            return this._gradientPixels
        }
    }, e
});

_getGradient方法中,我们调整进行颜色的过渡,在getCanvas方法中,我们按照权重在最大最小值中占的比例,来选定刷子的颜色取值,而非之前的直接晕染开来,看下运行后的效果图:
在这里插入图片描述
这里调小了每个点的半径。并且在_getGradient方法中生成摇杆,并且把摇杆横放。
看下主体结构的代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <title>自定义地图样例</title>

    <style type="text/css">
        .range-list {
            width: 320px;
            display: flex
        }

        .range-list span {
            display: inline-flex;
            flex-grow: 1
        }
    </style>
</head>

<body>
    <!-- 为ECharts准备一个具备大小(宽高)的Dom -->
    <div id="graphic" class="col-md-8" style="width: 1130px;float:none!important;">
        <img id="baidu-img" src="./static/test_image/test.jpg">
        <div id="main" style="width: 1130px; height: 400px;position: absolute; top: 0; left: 0;"></div>
    </div>
    <div>
        <canvas id="myCanvas" style="border:1px solid #d3d3d3;"></canvas>
        <div class="range-list">
            <span>0</span>
            <span>125</span>
            <span>250</span>
            <span>375</span>
            <span>500</span>
        </div>
    </div>
    <!-- <div id="main" style="height: 400px; width: 45%; min-width: 600px; float: left;" ></div> -->
    <!-- ECharts单文件引入 -->
    <script src="./static/echarts/src/echarts-oss.js" charset="UTF-8"></script>
    <!-- <script src="./static/echarts/jquery.js"></script> -->
    <script src="./static/echarts/esl.js"></script>
    <script type="text/javascript">
        let heatData = [[0,0,1],[50,200,350],[51,200,350],[70,200,499],[71,200,499],[72,200,499],[73,200,499],[74,200,499],[75,200,499],[76,200,499],[77,200,499],[78,200,499],[79,200,499],[80,200,499],[81,200,499]];
        //max和min为数据中的最大值和最小值(可通过遍历数据获得)
        let max = 500;
        let min = 0;
        test(heatData);


        function test(heatData) {
            require.config({
                paths: {
                    echarts: './static/echarts/src'
                }
            });
            require(
                [
                    'echarts',
                    'echarts/chart/heatmap'
                ],
                function (ec) {
                    // 基于准备好的dom,初始化echarts图表
                    var myChart = ec.init(document.getElementById('main'));
                    var option = {
                        series: [
                            {
                                type: 'heatmap',
                                data: heatData,
                                hoverable: false,
                                gradientColors: ['green', 'yellow', 'orange', 'red'],
                                minAlpha: 0.2,
                                valueScale: 2,
                                opacity: 1,
                                max: max,
                                min: min,
                                blurSize: 5
                            }
                        ]
                    };

                    // 为echarts对象加载数据 
                    myChart.setOption(option);
                }

            );

        };

        let myCanvas = document.getElementById("myCanvas")

        myCanvas.addEventListener("mousedown", function () {
            //标准的获取鼠标点击相对于canvas画布的坐标公式
            let x = event.pageX - $("#myCanvas")[0].getBoundingClientRect().left;
            let per = x / 256, mindata = per * (max - min) + min
            getRangeCanvas(mindata);
        });

        myCanvas.addEventListener("mousemove", throttle(mouseMoveHandle,1000))

        myCanvas.addEventListener("mouseup", function () {
            let x = event.clientX - $("#myCanvas")[0].getBoundingClientRect().left;
            let per = x / 256, mindata = per * (max - min) + min
            getRangeCanvas(mindata);
        })

        function mouseMoveHandle() {
            let x = event.clientX - $("#myCanvas")[0].getBoundingClientRect().left;
            let per = x / 256, mindata = per * (max - min) + min
            getRangeCanvas(mindata);
        }

        function getRangeCanvas(mindata) {
            let copy_heatdata = heatData;
            let points = [].concat.apply([], copy_heatdata.map(function (item) {
                if (item[2] > mindata) {
                    return [item]
                }
                return [];
            }))
            test(points)
        }

        function throttle(fn, wait) {
            var pre = Date.now();
            return function () {
                var context = this;
                var args = arguments;
                var now = Date.now();
                if (now - pre >= wait) {
                    fn.apply(context, args);
                    pre = Date.now();
                }
            }
        }
    </script>

</body>

我们为摇杆加入了监听事件,并且做了一些函数截流的操作,避免请求过于频繁,这样调整摇杆就能进行数据的筛选,这样就实现了整个热力图的功能
资源地址:https://download.csdn.net/download/yfm120750310/13583102

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值