UNPKG

4.07 kBPlain TextView Raw
1import { LngLat, Point, Map } from 'mapbox-gl';
2import { Props } from '../projected-layer';
3import { Anchor, AnchorsOffset } from './types';
4
5export interface PointDef {
6 x: number;
7 y: number;
8}
9
10export interface OverlayParams {
11 anchor?: Anchor;
12 offset?: Point;
13 position?: Point;
14}
15
16export const anchors = [
17 'center',
18 'top',
19 'bottom',
20 'left',
21 'right',
22 'top-left',
23 'top-right',
24 'bottom-left',
25 'bottom-right'
26] as Anchor[];
27
28export const anchorTranslates = {
29 center: 'translate(-50%, -50%)',
30 top: 'translate(-50%, 0)',
31 left: 'translate(0, -50%)',
32 right: 'translate(-100%, -50%)',
33 bottom: 'translate(-50%, -100%)',
34 'top-left': 'translate(0, 0)',
35 'top-right': 'translate(-100%, 0)',
36 'bottom-left': 'translate(0, -100%)',
37 'bottom-right': 'translate(-100%, -100%)'
38};
39
40// Hack /o\
41const defaultElement = { offsetWidth: 0, offsetHeight: 0 };
42const defaultPoint = [0, 0];
43
44const projectCoordinates = (map: Map, coordinates: [number, number]) =>
45 map.project(LngLat.convert(coordinates));
46
47const calculateAnchor = (
48 map: Map,
49 offsets: AnchorsOffset,
50 position: PointDef,
51 { offsetHeight, offsetWidth } = defaultElement
52) => {
53 let anchor: string[] = [];
54
55 if (position.y + offsets.bottom.y - offsetHeight < 0) {
56 anchor = [anchors[1]];
57 } else if (
58 position.y + offsets.top.y + offsetHeight >
59 // tslint:disable-next-line:no-any
60 (map as any).transform.height
61 ) {
62 anchor = [anchors[2]];
63 }
64
65 if (position.x < offsetWidth / 2) {
66 anchor.push(anchors[3]);
67 // tslint:disable-next-line:no-any
68 } else if (position.x > (map as any).transform.width - offsetWidth / 2) {
69 anchor.push(anchors[4]);
70 }
71
72 if (anchor.length === 0) {
73 return anchors[2];
74 }
75
76 return anchor.join('-') as Anchor;
77};
78
79const normalizedOffsets = (
80 offset?: number | Point | AnchorsOffset | [number, number]
81): AnchorsOffset => {
82 if (!offset) {
83 return normalizedOffsets(new Point(0, 0));
84 }
85
86 if (typeof offset === 'number') {
87 // input specifies a radius from which to calculate offsets at all positions
88 const cornerOffset = Math.round(Math.sqrt(0.5 * Math.pow(offset, 2)));
89 return {
90 center: new Point(offset, offset),
91 top: new Point(0, offset),
92 bottom: new Point(0, -offset),
93 left: new Point(offset, 0),
94 right: new Point(-offset, 0),
95 'top-left': new Point(cornerOffset, cornerOffset),
96 'top-right': new Point(-cornerOffset, cornerOffset),
97 'bottom-left': new Point(cornerOffset, -cornerOffset),
98 'bottom-right': new Point(-cornerOffset, -cornerOffset)
99 };
100 }
101
102 if (offset instanceof Point || Array.isArray(offset)) {
103 // input specifies a single offset to be applied to all positions
104 return anchors.reduce(
105 (res, anchor) => {
106 res[anchor] = Point.convert(offset);
107 return res;
108 },
109 // tslint:disable-next-line:no-object-literal-type-assertion
110 {} as AnchorsOffset
111 );
112 }
113
114 // input specifies an offset per position
115 return anchors.reduce(
116 (res, anchor) => {
117 res[anchor] = Point.convert(offset[anchor] || defaultPoint);
118 return res;
119 },
120 // tslint:disable-next-line:no-object-literal-type-assertion
121 {} as AnchorsOffset
122 );
123};
124
125export const overlayState = (
126 props: Props,
127 map: Map,
128 container: HTMLElement
129) => {
130 const position = projectCoordinates(map, props.coordinates);
131 const offsets = normalizedOffsets(props.offset);
132 const anchor =
133 props.anchor || calculateAnchor(map, offsets, position, container);
134
135 return {
136 anchor,
137 position,
138 offset: offsets[anchor]
139 };
140};
141
142const moveTranslate = (point: Point) =>
143 point ? `translate(${point.x.toFixed(0)}px, ${point.y.toFixed(0)}px)` : '';
144
145export const overlayTransform = ({
146 anchor,
147 position,
148 offset
149}: OverlayParams) => {
150 const res = [];
151
152 if (position) {
153 res.push(moveTranslate(position));
154 }
155
156 if (offset && offset.x !== undefined && offset.y !== undefined) {
157 res.push(moveTranslate(offset));
158 }
159
160 if (anchor) {
161 res.push(anchorTranslates[anchor]);
162 }
163
164 return res;
165};