Skip to content

Commit

Permalink
Add Marker's "opacity" option and "setOpacity" method (#3620)
Browse files Browse the repository at this point in the history
* Add setOpacity method to a Marker

* Add tests for Marker.setOpacity

* Add opacityWhenCovered

* Change opacity props to numbers

* Reset opacity when setOpacity is called without args

* use setOpacity when initializing Marker

* add expectedBytes

* remove if

* Add changelog entry

* Change opacity to string

* Add comment on setOpacity
  • Loading branch information
sbachinin authored Jan 28, 2024
1 parent 0e27f8b commit 6f64913
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### ✨ Features and improvements

- Add "opacity" option and "setOpacity" method to Marker ([#3620](https://github.com/maplibre/maplibre-gl-js/pull/3620))
- _...Add new stuff here..._

### 🐞 Bug fixes
Expand Down
91 changes: 91 additions & 0 deletions src/ui/marker.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,44 @@ describe('marker', () => {
map.remove();
});

test('Sets default opacity if it\'s not provided as option', () => {
const map = createMap();
const marker = new Marker()
.setLngLat([0, 0])
.addTo(map);
expect(marker.getElement().style.opacity).toMatch('1');
map.remove();
});

test('Sets opacity according to options.opacity', () => {
const map = createMap();
const marker = new Marker({opacity: '0.7'})
.setLngLat([0, 0])
.addTo(map);
expect(marker.getElement().style.opacity).toMatch('.7');
map.remove();
});

test('Changes opacity to a new value provided by setOpacity', () => {
const map = createMap();
const marker = new Marker({opacity: '0.7'})
.setLngLat([0, 0])
.addTo(map);
marker.setOpacity('0.6');
expect(marker.getElement().style.opacity).toMatch('.6');
map.remove();
});

test('Resets opacity to default when setOpacity is called without arguments', () => {
const map = createMap();
const marker = new Marker({opacity: '0.7'})
.setLngLat([0, 0])
.addTo(map);
marker.setOpacity();
expect(marker.getElement().style.opacity).toBe('1');
map.remove();
});

test('Marker changes opacity behind terrain and when terrain is removed', () => {
const map = createMap();
map.transform.lngLatToCameraDepth = () => .95; // Mocking distance to marker
Expand Down Expand Up @@ -870,4 +908,57 @@ describe('marker', () => {

map.remove();
});

test('Applies options.opacity when 3d terrain is enabled and marker is in clear view', () => {
const map = createMap();
map.transform.lngLatToCameraDepth = () => .95; // Mocking distance to marker
const marker = new Marker({opacity: '0.7'})
.setLngLat([0, 0])
.addTo(map);

map.terrain = {
getElevationForLngLatZoom: () => 0,
depthAtPoint: () => .95
} as any as Terrain;
map.fire('terrain');

expect(marker.getElement().style.opacity).toMatch('.7');
map.remove();
});

test('Applies options.opacityWhenCovered when marker is hidden by 3d terrain', () => {
const map = createMap();
map.transform.lngLatToCameraDepth = () => .95; // Mocking distance to marker
const marker = new Marker({opacity: '0.7', opacityWhenCovered: '0.3'})
.setLngLat([0, 0])
.addTo(map);

map.terrain = {
getElevationForLngLatZoom: () => 0,
depthAtPoint: () => .92
} as any as Terrain;
map.fire('terrain');

expect(marker.getElement().style.opacity).toMatch('0.3');
map.remove();
});

test('Applies new "opacityWhenCovered" provided by setOpacity when marker is hidden by 3d terrain', async () => {
const map = createMap();
map.transform.lngLatToCameraDepth = () => .95; // Mocking distance to marker
const marker = new Marker({opacityWhenCovered: '0.15'})
.setLngLat([0, 0])
.addTo(map);

map.terrain = {
getElevationForLngLatZoom: () => 0,
depthAtPoint: () => .92
} as any as Terrain;
map.fire('terrain');

marker.setOpacity(undefined, '0.35');

expect(marker.getElement().style.opacity).toMatch('0.35');
map.remove();
});
});
45 changes: 42 additions & 3 deletions src/ui/marker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,16 @@ type MarkerOptions = {
* @defaultValue 'auto'
*/
pitchAlignment?: Alignment;
/**
* Marker's opacity when it's in clear view (not behind 3d terrain)
* @defaultValue 1
*/
opacity?: string;
/**
* Marker's opacity when it's behind 3d terrain
* @defaultValue 0.2
*/
opacityWhenCovered?: string;
};

/**
Expand Down Expand Up @@ -128,6 +138,8 @@ export class Marker extends Evented {
_pitchAlignment: Alignment;
_rotationAlignment: Alignment;
_originalTabIndex: string; // original tabindex of _element
_opacity: string;
_opacityWhenCovered: string;
_opacityTimeout: ReturnType<typeof setTimeout>;

/**
Expand All @@ -146,6 +158,8 @@ export class Marker extends Evented {
this._rotation = options && options.rotation || 0;
this._rotationAlignment = options && options.rotationAlignment || 'auto';
this._pitchAlignment = options && options.pitchAlignment && options.pitchAlignment !== 'auto' ? options.pitchAlignment : this._rotationAlignment;
this.setOpacity(); // set default opacity
this.setOpacity(options?.opacity, options?.opacityWhenCovered);

if (!options || !options.element) {
this._defaultMarker = true;
Expand Down Expand Up @@ -509,7 +523,7 @@ export class Marker extends Evented {
_updateOpacity(force: boolean = false) {
const terrain = this._map.terrain;
if (!terrain) {
if (this._element.style.opacity === '0.2') { this._element.style.opacity = '1'; }
if (this._element.style.opacity !== this._opacity) { this._element.style.opacity = this._opacity; }
return;
}
if (force) {
Expand All @@ -531,7 +545,7 @@ export class Marker extends Evented {

const forgiveness = .006;
if (markerDistance - terrainDistance < forgiveness) {
this._element.style.opacity = '1';
this._element.style.opacity = this._opacity;
return;
}
// If the base is obscured, use the offset to check if the marker's center is obscured.
Expand All @@ -540,7 +554,8 @@ export class Marker extends Evented {
const terrainDistanceCenter = map.terrain.depthAtPoint(new Point(this._pos.x, this._pos.y - this._offset.y));
const markerDistanceCenter = map.transform.lngLatToCameraDepth(this._lngLat, elevation + elevationToCenter);
// Display at full opacity if center is visible.
this._element.style.opacity = (markerDistanceCenter - terrainDistanceCenter > forgiveness) ? '0.2' : '1.0';
const centerIsInvisible = markerDistanceCenter - terrainDistanceCenter > forgiveness;
this._element.style.opacity = centerIsInvisible ? this._opacityWhenCovered : this._opacity;
}

_update = (e?: { type: 'move' | 'moveend' | 'terrain' | 'render' }) => {
Expand Down Expand Up @@ -797,4 +812,28 @@ export class Marker extends Evented {
getPitchAlignment(): Alignment {
return this._pitchAlignment;
}

/**
* Sets the `opacity` and `opacityWhenCovered` properties of the marker.
* When called without arguments, resets opacity and opacityWhenCovered to defaults
* @param opacity - Sets the `opacity` property of the marker.
* @param opacityWhenCovered - Sets the `opacityWhenCovered` property of the marker.
* @returns `this`
*/
setOpacity(opacity?: string, opacityWhenCovered?: string): this {
if (opacity === undefined && opacityWhenCovered === undefined) {
this._opacity = '1';
this._opacityWhenCovered = '0.2';
}
if (opacity !== undefined) {
this._opacity = opacity;
}
if (opacityWhenCovered !== undefined) {
this._opacityWhenCovered = opacityWhenCovered;
}
if (this._map) {
this._updateOpacity(true);
}
return this;
}
}
2 changes: 1 addition & 1 deletion test/build/min.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ describe('test min build', () => {
const decreaseQuota = 4096;

// feel free to update this value after you've checked that it has changed on purpose :-)
const expectedBytes = 772222;
const expectedBytes = 772550;

expect(actualBytes - expectedBytes).toBeLessThan(increaseQuota);
expect(expectedBytes - actualBytes).toBeLessThan(decreaseQuota);
Expand Down

0 comments on commit 6f64913

Please sign in to comment.