'Integrate mapbox with fabric js

I am using fabric.js for html 5 interactive canvas app and I'm tryna to do an integration with mapbox, basically putting the map inside a fabric js element such as rectangle or a circle anb manipulate it on the canvas.

I was reading the fabricjs docs and the way it indicates to achieve that is to set an absolute position in the map container and place it on the fabric element inside the canvas and with an event listener depending on whether the element is moved or its size is modified I move the map with it, but I have a problem with that approach. Basically with the container being an absolute element, it's going to be always overlaid on top of everything, so I won't be able to put any other element that I create on fabirc on top of it.

So is there any other approach to achive this?

Here is the code:

HTML:

<canvas id="canvas-layer"></canvas>

<div id="map-container">
  <div id="geocoder" class="geocoder"></div>
  <div id="map"></div>
</div>

CSS:

#map-container {
  position: absolute;
}

#map {
  width: 100%;
  height: 85%;
  margin-top: 20px;
}

.mapboxgl-ctrl-geocoder {
  min-width: 100%;
}

JS:

const CONTAINER_PADDING = 50;
// Fabric js declarations
const canvas = new fabric.Canvas("canvas-layer", {
  width: 1000,
  height: 800,
  backgroundColor: "#ddd",
});
fabric.Object.prototype.transparentCorners = false;

fabric.Canvas.prototype.getElementState = function (object) {
  return {
    left: object.left + this._offset.left,
    top: object.top + this._offset.top,
    width: object.width * object.scaleX,
    height: object.height * object.scaleY,
  };
};

// Mapbox declarations
mapboxgl.accessToken =
  "pk.eyJ1IjoiY3VzdG9taWx5LWFsZXgiLCJhIjoiY2t6aTAxZGIzMDk0ODJ1bnliNmtqYno1dyJ9.-zFq2RrTNRrtIE5HpR91wA";
const map = new mapboxgl.Map({
  container: "map",
  style: "mapbox://styles/mapbox/streets-v11",
  center: [-74.5, 40],
  zoom: 9,
});
map.addControl(new mapboxgl.NavigationControl());
map.dragRotate.disable();
map.touchZoomRotate.disableRotation();

const geocoder = new MapboxGeocoder({
  accessToken: mapboxgl.accessToken,
  mapboxgl: mapboxgl,
});
document.getElementById("geocoder").appendChild(geocoder.onAdd(map));

// Create fabric element and update the position
const positionMap = (obj) => {
  const state = canvas.getElementState(obj);
  // map-container div is an absolute element
  const mapContainer = document.getElementById("map-container");

  mapContainer.style.left = state.left - (state.width - CONTAINER_PADDING) / 2 + "px";
  mapContainer.style.top = state.top - (state.height - CONTAINER_PADDING) / 2 + "px";
}

const scalingMap = (obj) => {
  const { width, height } = canvas.getElementState(obj);
  const mapContainer = document.getElementById("map-container");
  const mapCanvas = document.getElementsByClassName('mapboxgl-canvas')[0];

  mapContainer.style.height = height - CONTAINER_PADDING + 'px';
  mapContainer.style.width = width - CONTAINER_PADDING + 'px';
  mapCanvas.style.width = '100%'
  map.resize();
  positionMap(obj);
};

const createRect = () => {
  const center = canvas.getCenter();
  const rect = new fabric.Rect({
    width: 400,
    height: 400,
    fill: 'green',
    left: center.left,
    top: center.top,
    originX: 'center',
    originY: 'center',
    cornerColor: 'blue',
    lockRotation: true,
  });
  canvas.add(rect);
  canvas.renderAll();

  rect.on('moving', () => positionMap(rect));
  rect.on('scaling', () => scalingMap(rect));
  positionMap(rect);
  scalingMap(rect);
};

createRect();


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source