Mapboxgl 动态添加多种颜色、形状的图标

发布时间 2023-04-12 13:58:24作者: 宇宙野牛

适用场景:
需要在地图上展示很多种数据,这些数据使用的图标形状是有限的几种,但颜色可能由后台自由配置后传给前端。
数据种类可能会变化,颜色可能会变化,这种情况下让设计同事切一堆图反而不方便维护,使用动态加载图标可以一定程度上节省前端的后期工作。
前端框架是vue3+vite,其他框架能否实现未验证。

局限:
目前只做了单色纯色不透明度为1的动态图标,所以生成的图标有可能看起来有点粗糙
最好能动态获取源图标的长宽像素值,这样保证再生成的动态图标形状完整

准备:

  1. 在工程中准备好需要的基本形状,颜色无所谓(但要有颜色),命名用你定的规则就可以。比如"圆形.png"、"c_circle.png"。
  2. 无论是接口请求还是别的方式,最终处理拿到形状、颜色(、图标名)这样的参数。

代码如下:

/**
 * @description: 将动态生成的图片添加到地图
 * @param {*}
 */
function addDynamicImage(imageName, imageShape, imageColor) {
	let dynamicImage = drawDynamicColorIcon(imageShape, imageColor);
	if (!map.hasImage(imageName)) {
		map.addImage(imageName, dynamicImage);
		console.log("load dynamic:" + imageName);
	}
}
/**
 * @description: 根据指定的形状和颜色生成图片
 * @param {*}
 * @return {*} img
 */
function drawDynamicColorIcon(base, color = [255, 255, 0]) {
        // 如果是color是16进制格式的颜色,处理成rgb数组格式
	if (color && color[0] === "#") {
		color = set16ToRgb(color);
	}
	let imgUrl = imagesMap[base]; // 拿到基本图形的图片地址,这里我使用的是一位博主提供的动态导入方法:https://www.cnblogs.com/HE0318bei/p/16918206.html
	let [r, g, b] = color;
	const width = baseConf[base][0]; // 对应的基本图形的宽
	const height = baseConf[base][1]; // 对应的基本图形的高
	return {
		width: width,
		height: height,
		data: new Uint8Array(height * width * 4),

		onAdd: function () {
			const canvas = document.createElement("canvas");
			canvas.width = this.width;
			canvas.height = this.height;
			this.context = canvas.getContext("2d");
		},

		render: function () {
			let _this = this;
			const context = this.context;
			const img = new Image();
			img.src = imgUrl; // 图片路径
			img.onload = function () {
				let imageData = context.getImageData(0, 0, _this.width, _this.height);
				let length = imageData.data.length;
				for (let index = 0; index < length; index += 4) {
					imageData.data[index] = r;
					imageData.data[index + 1] = g;
					imageData.data[index + 2] = b;
				}
				// 更新新数据
				context.putImageData(imageData, 0, 0);
				_this.data = imageData.data;
				context.drawImage(img, 0, 0);
			};
			return true;
		},
	};
}