diff --git a/dev-demos/features/customLayer/marker-layer.md b/dev-demos/features/customLayer/marker-layer.md
new file mode 100644
index 0000000000..ecc11b2eb9
--- /dev/null
+++ b/dev-demos/features/customLayer/marker-layer.md
@@ -0,0 +1,103 @@
+### markerLayer
+
+```tsx
+import { Marker, MarkerLayer, Scene } from '@antv/l7';
+import { GaodeMap, GaodeMapV2, Mapbox } from '@antv/l7-maps';
+import React, { useEffect } from 'react';
+
+export default () => {
+ useEffect(() => {
+ const gaodeV1Scene = new Scene({
+ id: 'gaodeV1',
+ map: new GaodeMap({
+ center: [105, 30.258134],
+ zoom: 2,
+ }),
+ });
+ // const gaodeV2Scene = new Scene({
+ // id: 'gaodeV2',
+ // map: new GaodeMapV2({
+ // center: [105, 30.258134],
+ // zoom: 3,
+ // }),
+ // });
+ const mapboxScene = new Scene({
+ id: 'mapbox',
+ map: new Mapbox({
+ center: [120, 30],
+ zoom: 2,
+ }),
+ });
+
+ addMarkers(gaodeV1Scene);
+ // addMarkers(gaodeV2Scene);
+ addMarkers(mapboxScene);
+ }, []);
+
+ const addMarkers = (s) => {
+ fetch(
+ 'https://gw.alipayobjects.com/os/basement_prod/d3564b06-670f-46ea-8edb-842f7010a7c6.json',
+ )
+ .then((res) => res.json())
+ .then((nodes) => {
+ const markerLayer = new MarkerLayer();
+ for (let i = 0; i < 400; i++) {
+ const { coordinates } = nodes.features[i].geometry;
+ const el = document.createElement('label');
+ el.textContent = coordinates[1];
+ el.style.background = getColor(coordinates[1]);
+ el.style.borderColor = getColor(coordinates[1]);
+
+ const marker = new Marker({
+ element: el,
+ }).setLnglat({
+ lng: coordinates[0],
+ lat: coordinates[1],
+ });
+ markerLayer.addMarker(marker);
+ }
+ s.addMarkerLayer(markerLayer);
+ });
+ };
+
+ const getColor = (v) => {
+ const colors = [
+ '#ffffe5',
+ '#f7fcb9',
+ '#d9f0a3',
+ '#addd8e',
+ '#78c679',
+ '#41ab5d',
+ '#238443',
+ '#005a32',
+ ];
+ return v > 50
+ ? colors[7]
+ : v > 40
+ ? colors[6]
+ : v > 30
+ ? colors[5]
+ : v > 20
+ ? colors[4]
+ : v > 10
+ ? colors[3]
+ : v > 5
+ ? colors[2]
+ : v > 0
+ ? colors[1]
+ : colors[0];
+ };
+
+ return (
+ <>
+
400 个节点测试
+
+ 高德V1
+
+
+ Mapbox
+
+ >
+ );
+};
+```
diff --git a/dev-demos/features/customLayer/marker.md b/dev-demos/features/customLayer/marker.md
new file mode 100644
index 0000000000..90a383dcff
--- /dev/null
+++ b/dev-demos/features/customLayer/marker.md
@@ -0,0 +1,78 @@
+### marker
+
+```tsx
+import { Marker, PointLayer, PolygonLayer, Popup, Scene } from '@antv/l7';
+import { GaodeMap, Mapbox } from '@antv/l7-maps';
+import React, { useEffect } from 'react';
+
+export default () => {
+ useEffect(() => {
+ const scene = new Scene({
+ id: 'map-marker',
+ map: new GaodeMap({
+ center: [120.184824, 30.248341],
+ pitch: 0,
+ zoom: 18,
+ }),
+ });
+
+ const popup = new Popup({
+ offsets: [0, 20],
+ }).setHTML('111111111111
');
+
+ const el = document.createElement('h1');
+ el.innerHTML = '111111111111
';
+
+ // const marker = new Marker({
+ // element: el,
+ // // offsets: [0, -20],
+ // })
+ const marker = new Marker()
+ .setLnglat({
+ lng: 120.184824,
+ lat: 30.248341,
+ })
+ .setPopup(popup);
+
+ scene.addMarker(marker);
+
+ const arr = [
+ {
+ lng: 120.184824,
+ lat: 30.248341,
+ count: 40,
+ },
+ ];
+
+ scene.on('loaded', () => {
+ // @ts-ignore
+ // marker.on('click', (e) => {
+ // });
+ // const marker1 = new AMap.Marker({
+ // map: scene.map,
+ // position: [120.184824, 30.248341],
+ // shadow: '#000',
+ // label: {
+ // content: '站点',
+ // direction: 'top',
+ // },
+ // });
+ // marker1.on('click', () => {
+ // console.log(this.scene.getZoom());
+ // console.log('选中的点', 1111);
+ // });
+ // this.scene = scene;
+ });
+ }, []);
+
+ return (
+
+ );
+};
+```
diff --git a/packages/component/__tests__/marker.spec.tsx b/packages/component/__tests__/marker.spec.tsx
new file mode 100644
index 0000000000..7b02d57c03
--- /dev/null
+++ b/packages/component/__tests__/marker.spec.tsx
@@ -0,0 +1,61 @@
+import Marker from '../src/marker';
+import Popup from '../src/popup';
+import { TestScene } from '@antv/l7-test-utils'
+
+const popup = new Popup({ offsets: [0, 20] })
+ .setHTML('111
');
+
+const marker = new Marker()
+ .setLnglat({ lng: 120, lat: 30 })
+ .setPopup(popup);
+
+TestScene().addMarker(marker)
+
+describe('Marker', () => {
+ it('render and remove correctly', () => {
+ expect(document.querySelector('.l7-marker')).toBeTruthy();
+ expect(marker.getDefault().draggable).toEqual(false);
+ expect(marker.getDefault().color).toEqual('#5B8FF9');
+ expect(marker.getOffset()).toEqual([0, 0]);
+ expect(marker.isDraggable()).toEqual(false);
+ marker.remove()
+ expect(document.querySelector('.l7-marker')).toBeFalsy();
+ });
+
+ it('open popup and close popup correctly', () => {
+ marker.openPopup();
+ expect(marker.getPopup().isOpen()).toBeTruthy();
+
+ marker.getPopup().close();
+ expect(marker.getPopup().isOpen()).toBeFalsy();
+
+ marker.togglePopup();
+ expect(marker.getPopup().isOpen()).toBeTruthy();
+
+ marker.closePopup();
+ expect(marker.getPopup().isOpen()).toBeFalsy();
+ })
+
+ it('longitude and latitude', () => {
+ const { lng, lat } = marker.getLnglat();
+ expect(lng).toEqual(120);
+ expect(lat).toEqual(30);
+ marker.setLnglat({ lng: 121, lat: 31 })
+ const { lng: newLng, lat: newLat } = marker.getLnglat();
+ expect(newLng).toEqual(121);
+ expect(newLat).toEqual(31);
+ });
+
+ it('element', () => {
+ const el = document.createElement('div');
+ el.id = 'markerDom';
+ el.innerHTML = '111
';
+ marker.setElement(el);
+ expect(marker.getElement()).toBeTruthy();
+ });
+
+ it('extData', () => {
+ marker.setExtData({ test: 1 })
+ expect(marker.getExtData()).toEqual({ test: 1 })
+ });
+});