问题描述
我正在使用自定义图像创建带有图层符号的符号图层,效果很好。 我还想创建带有HTML的自定义标签(基本上是带有宽松边框和标签的蓝色背景),但是我不确定这是否可行或如何实现。我包括了我现在正在使用的渲染点,获取图标渲染了每个点的自定义图像,这些图像是使用map.loadImage预先加载的。@H_404_3@
map.addLayer({
id: 'points',type: 'symbol',source: 'points',paint: {
"text-color": "#ffffff",},layout: {
'icon-image': ['get','icon'],// 'cat','icon-size': 1,'icon-allow-overlap': true,'text-field': ['get','name'],'text-font': ['Open Sans Semibold','Arial Unicode MS Bold'],'text-offset': [0,2.00],'text-size': 14,'text-anchor': 'top','text-allow-overlap': false,})
解决方法
您不能在符号层中使用HTML。您可以:
- 使用
Marker
对象而不是符号图层。 - 在符号层中使用格式化的内容,并混合使用字体,字体粗细等。
- 使用9个部分的图像制作自定义边框。
这几乎是我过去为房屋租赁公司所做的工作。 基本上,我必须出示带有价格的标签,房子是否有3D图片,并添加蓝色背景... 下面是可以修改以满足您的需求的源代码
我所做的是像这样在“ icon-image”中编码所有信息:
...
'icon-image': ['concat','projectmarker|',['get','id'],'|','price'],'3d'],'name'],'highlight']]
...
然后发生的是,mapbox找不到图像,而是调用了“ styleimagemissing”回调,该回调使用该元素完成了所有工作,并最终将其转换为dataimage。
const missingImages = [];
map.on('styleimagemissing',function (e) {
const id = e.id;
const blue = '#1f2d41';
const white = '#ffffff';
const yellow = '#a3a326';
// only create once
if (missingImages.indexOf(id) !== -1) return;
missingImages.push(id);
// check if this missing icon is one this function can generate
if (id.indexOf('projectmarker') !== 0) return;
// extract infos
const projectId = parseInt((id.split('|')[1]));
let price = parseInt((id.split('|')[2])).toString().replace(/\B(?=(\d{3})+(?!\d))/g,",");
const hasPrice = price !== "0";
const threeD = 'true' === (id.split('|')[3]);
const highlight = '1' === (id.split('|')[5]);
if (!hasPrice) {
price = id.split('|')[4];
} else {
price += ' ' + currencyCode;
}
// create canvas
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
const height = 20;
const leftWidth = 40;
const rightWidth = (8 * price.length) + 10;
const leftBg = blue;
const rightBg = highlight ? yellow : white;
const radius = 4;
if (threeD) {
// 3d bg
ctx.fillStyle = leftBg;
roundRect(ctx,leftWidth,height,{tl: radius,tr: 0,br: 0,bl: radius},true,true);
// 3d text
ctx.textAlign = "center";
ctx.font = "bold 14px Arial";
ctx.fillStyle = white;
ctx.fillText('360°',leftWidth / 2,16);
// price bg
ctx.fillStyle = rightBg;
roundRect(ctx,rightWidth,{tl: 0,tr: radius,br: radius,bl: 0},true);
} else {
// price bg
ctx.fillStyle = rightBg;
roundRect(ctx,radius,true);
}
// price
ctx.textAlign = "center";
ctx.font = "14px Arial";
ctx.fillStyle = blue;
ctx.fillText(price.replace(',',' '),(threeD ? leftWidth : 0) + (rightWidth / 2),15);
// extract data and create mapbox image
const imageData = ctx.getImageData(0,(threeD ? leftWidth : 0) + rightWidth,height);
map.addImage(id,imageData);
});
下面是roundRect助手
const roundRect = (ctx,x,y,width,fill,stroke) => {
if (typeof stroke === 'undefined') {
stroke = true;
}
if (typeof radius === 'undefined') {
radius = 5;
}
if (typeof radius === 'number') {
radius = {tl: radius,bl: radius};
} else {
const defaultRadius = {tl: 0,bl: 0};
for (let side in defaultRadius) {
radius[side] = radius[side] || defaultRadius[side];
}
}
ctx.beginPath();
ctx.moveTo(x + radius.tl,y);
ctx.lineTo(x + width - radius.tr,y);
ctx.quadraticCurveTo(x + width,x + width,y + radius.tr);
ctx.lineTo(x + width,y + height - radius.br);
ctx.quadraticCurveTo(x + width,y + height,x + width - radius.br,y + height);
ctx.lineTo(x + radius.bl,y + height);
ctx.quadraticCurveTo(x,y + height - radius.bl);
ctx.lineTo(x,y + radius.tl);
ctx.quadraticCurveTo(x,x + radius.tl,y);
ctx.closePath();
if (fill) {
ctx.fill();
}
if (stroke) {
ctx.stroke();
}
}