在Mapbox GL中,有一种方法可以使符号标签为自定义HTML

问题描述

我正在使用自定义图像创建带有图层符号的符号图层,效果很好。 我还想创建带有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();
    }

}