Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Marker's "opacity" option and "setOpacity" method #3620

Merged
merged 11 commits into from
Jan 28, 2024
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();
});
});
42 changes: 39 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?: number;
/**
* Marker's opacity when it's behind 3d terrain
* @defaultValue 0.2
*/
opacityWhenCovered?: number;
};

/**
Expand Down Expand Up @@ -128,6 +138,8 @@ export class Marker extends Evented {
_pitchAlignment: Alignment;
_rotationAlignment: Alignment;
_originalTabIndex: string; // original tabindex of _element
_opacity: number;
_opacityWhenCovered: number;
_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._opacity = options && options.opacity !== undefined ? options.opacity : 1;
HarelM marked this conversation as resolved.
Show resolved Hide resolved
this._opacityWhenCovered = options && options.opacityWhenCovered !== undefined ? options.opacityWhenCovered : 0.2;

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 !== String(this._opacity)) { this._element.style.opacity = String(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 = String(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 ? String(this._opacityWhenCovered) : String(this._opacity);
}

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

/**
* Sets the `opacity` and `opacityWhenCovered` properties of the marker.
HarelM marked this conversation as resolved.
Show resolved Hide resolved
* @param opacity - Sets the `opacity` property of the marker.
* @param opacityWhenCovered - Sets the `opacityWhenCovered` property of the marker.
* @returns `this`
*/
setOpacity(opacity?: number, opacityWhenCovered?: number): this {
HarelM marked this conversation as resolved.
Show resolved Hide resolved
if (opacity === undefined && opacityWhenCovered === undefined) {
this._opacity = 1;
this._opacityWhenCovered = 0.2;
}
if (opacity !== undefined) {
this._opacity = opacity;
}
if (opacityWhenCovered !== undefined) {
this._opacityWhenCovered = opacityWhenCovered;
}
this._updateOpacity(true);
return this;
}
}
Loading