@kiva/kv-components 3.47.0 → 3.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/package.json +2 -2
- package/tests/unit/specs/utils/mapAnimationUtils.spec.js +40 -0
- package/utils/mapAnimation.js +272 -0
- package/vue/KvMap.vue +107 -7
- package/vue/stories/KvMap.stories.js +31 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [3.48.0](https://github.com/kiva/kv-ui-elements/compare/@kiva/kv-components@3.47.0...@kiva/kv-components@3.48.0) (2023-11-20)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* map mega animation ([#315](https://github.com/kiva/kv-ui-elements/issues/315)) ([5adfe9c](https://github.com/kiva/kv-ui-elements/commit/5adfe9c5d7db3b05efa23962025ba5440e8a2e4a))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
6
17
|
# [3.47.0](https://github.com/kiva/kv-ui-elements/compare/@kiva/kv-components@3.46.3...@kiva/kv-components@3.47.0) (2023-10-23)
|
|
7
18
|
|
|
8
19
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kiva/kv-components",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.48.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -75,5 +75,5 @@
|
|
|
75
75
|
"optional": true
|
|
76
76
|
}
|
|
77
77
|
},
|
|
78
|
-
"gitHead": "
|
|
78
|
+
"gitHead": "d6d10fe82c51bb9bd2bab93e475dac025d1298e9"
|
|
79
79
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getCoordinatesBetween,
|
|
3
|
+
} from '../../../../utils/mapAnimation';
|
|
4
|
+
|
|
5
|
+
describe('mapAnimation', () => {
|
|
6
|
+
describe('getCoordinatesBetween', () => {
|
|
7
|
+
it('should return empty array if inputs are invalid', () => {
|
|
8
|
+
expect(getCoordinatesBetween([100, 88], undefined, 10)).toStrictEqual([]);
|
|
9
|
+
expect(getCoordinatesBetween(undefined, undefined, 10)).toStrictEqual([]);
|
|
10
|
+
expect(getCoordinatesBetween(undefined, undefined, 0)).toStrictEqual([]);
|
|
11
|
+
expect(getCoordinatesBetween(undefined, [-89, -89], 0)).toStrictEqual([]);
|
|
12
|
+
});
|
|
13
|
+
it('should return an array of steps', () => {
|
|
14
|
+
expect(getCoordinatesBetween([0, 0], [100, 100], 10)).toStrictEqual(
|
|
15
|
+
[[0, 0], [10, 10], [20, 20], [30, 30], [40, 40], [50, 50], [60, 60], [70, 70], [80, 80], [100, 100]],
|
|
16
|
+
);
|
|
17
|
+
expect(getCoordinatesBetween([0, 0], [100, 100], 3)).toStrictEqual(
|
|
18
|
+
[[0, 0], [33.333333333333336, 33.333333333333336], [100, 100]],
|
|
19
|
+
);
|
|
20
|
+
expect(getCoordinatesBetween([0, 0], [100, 101], 2)).toStrictEqual(
|
|
21
|
+
[[0, 0], [100, 101]],
|
|
22
|
+
);
|
|
23
|
+
});
|
|
24
|
+
it('should handle negative numbers', () => {
|
|
25
|
+
expect(getCoordinatesBetween([-10, -30], [-90, -45.123], 3)).toStrictEqual(
|
|
26
|
+
[[-10, -30], [-36.66666666666667, -35.041], [-90, -45.123]],
|
|
27
|
+
);
|
|
28
|
+
});
|
|
29
|
+
it('last item should be our exact endpoint', () => {
|
|
30
|
+
expect(getCoordinatesBetween([0, 0], [100, -180], 3)).toStrictEqual(
|
|
31
|
+
[[0, 0], [33.333333333333336, -60], [100, -180]],
|
|
32
|
+
);
|
|
33
|
+
});
|
|
34
|
+
it('should handle long decimals', () => {
|
|
35
|
+
expect(getCoordinatesBetween([0.123, 0.999], [100.123, 101.666], 3)).toStrictEqual(
|
|
36
|
+
[[0.123, 0.999], [33.45633333333333, 34.55466666666667], [100.123, 101.666]],
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code to generate random coordinates
|
|
3
|
+
* */
|
|
4
|
+
function getRandomInRange(from, to, fixed) {
|
|
5
|
+
return (Math.random() * (to - from) + from).toFixed(fixed) * 1;
|
|
6
|
+
// .toFixed() returns string, so ' * 1' is a trick to convert to number
|
|
7
|
+
}
|
|
8
|
+
const randomCoordinates = Array.from(
|
|
9
|
+
{ length: 20 },
|
|
10
|
+
() => [getRandomInRange(-180, 180, 3), getRandomInRange(-90, 90, 3)],
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Given 2 coordinates and the number of steps return an array of coordinates in between
|
|
15
|
+
* @param {Array} startCoordinates - starting coordinates in the format [latitude, longitude]
|
|
16
|
+
* @param {Array} endCoordinates - ending coordinates in the format [latitude, longitude]
|
|
17
|
+
* @param {Number} numberOfSteps - number of steps to take between the start and end coordinates
|
|
18
|
+
* @returns {Array} - array of coordinates in the format [[latitude, longitude], [latitude, longitude]]
|
|
19
|
+
*/
|
|
20
|
+
export function getCoordinatesBetween(startCoordinates, endCoordinates, numberOfSteps) {
|
|
21
|
+
// All invalid inputs should return an empty array
|
|
22
|
+
if (!startCoordinates
|
|
23
|
+
|| !endCoordinates
|
|
24
|
+
|| !numberOfSteps
|
|
25
|
+
|| numberOfSteps < 1
|
|
26
|
+
|| !Array.isArray(startCoordinates)
|
|
27
|
+
|| !Array.isArray(endCoordinates)
|
|
28
|
+
|| startCoordinates.length !== 2 || endCoordinates.length !== 2) {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
const diffX = endCoordinates[0] - startCoordinates[0];
|
|
32
|
+
const diffY = endCoordinates[1] - startCoordinates[1];
|
|
33
|
+
|
|
34
|
+
const sfX = diffX / numberOfSteps;
|
|
35
|
+
const sfY = diffY / numberOfSteps;
|
|
36
|
+
|
|
37
|
+
let i = 0;
|
|
38
|
+
let j = 0;
|
|
39
|
+
|
|
40
|
+
const lineCoordinates = [];
|
|
41
|
+
|
|
42
|
+
while (Math.abs(i) < Math.abs(diffX) || Math.abs(j) < Math.abs(diffY)) {
|
|
43
|
+
lineCoordinates.push([startCoordinates[0] + i, startCoordinates[1] + j]);
|
|
44
|
+
|
|
45
|
+
if (Math.abs(i) < Math.abs(diffX)) {
|
|
46
|
+
i += sfX;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (Math.abs(j) < Math.abs(diffY)) {
|
|
50
|
+
j += sfY;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Because of rounding errors, lets push the exact end coordinates
|
|
54
|
+
// as the last item in the array to make sure the line ends precisely
|
|
55
|
+
lineCoordinates[lineCoordinates.length - 1] = [endCoordinates[0], endCoordinates[1]];
|
|
56
|
+
return lineCoordinates;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* This function animates a series of lines from an array of starting coordinates to a single end point
|
|
61
|
+
* then animates removing the line from the origin to the end point
|
|
62
|
+
* returns a promise when the animation is complete
|
|
63
|
+
* @param {Map Instance} mapInstance - the map instance
|
|
64
|
+
* @param {Array} originPoints - array of starting coordinates in the format [[latitude, longitude], [latitude, longitude]]
|
|
65
|
+
* @param {Array} endPoint - single end point in the format [latitude, longitude]
|
|
66
|
+
* @returns {Promise} - promise that resolves when the animation is complete
|
|
67
|
+
*/
|
|
68
|
+
function animateLines(mapInstance, originPoints, endPoint) {
|
|
69
|
+
const speedFactor = 100; // number of frames per degree, controls animation speed
|
|
70
|
+
return new Promise((resolve) => {
|
|
71
|
+
// EndPoint
|
|
72
|
+
mapInstance.addSource('endpoint', {
|
|
73
|
+
type: 'geojson',
|
|
74
|
+
data: {
|
|
75
|
+
type: 'Point',
|
|
76
|
+
coordinates: [
|
|
77
|
+
endPoint[0], endPoint[1],
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const lineFlight = (startCoordinates, endCoordinates, index, lastLine = false) => {
|
|
83
|
+
const lineCoordinates = getCoordinatesBetween(startCoordinates, endCoordinates, speedFactor);
|
|
84
|
+
let animationCounter = 0;
|
|
85
|
+
|
|
86
|
+
// Create a GeoJSON source with an empty lineString.
|
|
87
|
+
const geojson = {
|
|
88
|
+
type: 'FeatureCollection',
|
|
89
|
+
features: [{
|
|
90
|
+
type: 'Feature',
|
|
91
|
+
geometry: {
|
|
92
|
+
type: 'LineString',
|
|
93
|
+
coordinates: [],
|
|
94
|
+
},
|
|
95
|
+
}],
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// Start Point
|
|
99
|
+
mapInstance.addSource(`startPoint${index}`, {
|
|
100
|
+
type: 'geojson',
|
|
101
|
+
data: {
|
|
102
|
+
type: 'Point',
|
|
103
|
+
coordinates: [
|
|
104
|
+
startCoordinates[0], startCoordinates[1],
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Line
|
|
110
|
+
mapInstance.addLayer({
|
|
111
|
+
id: `line-animation${index}`,
|
|
112
|
+
type: 'line',
|
|
113
|
+
source: {
|
|
114
|
+
type: 'geojson',
|
|
115
|
+
data: geojson,
|
|
116
|
+
},
|
|
117
|
+
layout: {
|
|
118
|
+
'line-cap': 'round',
|
|
119
|
+
'line-join': 'round',
|
|
120
|
+
},
|
|
121
|
+
paint: {
|
|
122
|
+
'line-color': '#277056',
|
|
123
|
+
'line-width': 2,
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
const animateLine = () => {
|
|
128
|
+
if (animationCounter < lineCoordinates.length) {
|
|
129
|
+
geojson.features[0].geometry.coordinates.push(lineCoordinates[animationCounter]);
|
|
130
|
+
mapInstance.getSource(`line-animation${index}`).setData(geojson);
|
|
131
|
+
|
|
132
|
+
requestAnimationFrame(animateLine);
|
|
133
|
+
animationCounter += 1;
|
|
134
|
+
} else {
|
|
135
|
+
// This else block is for animating line removal from start to end
|
|
136
|
+
const coord = geojson.features[0].geometry.coordinates;
|
|
137
|
+
// remove 2 points at a time so the removal is twice as fast as the line creation
|
|
138
|
+
coord.shift();
|
|
139
|
+
coord.shift();
|
|
140
|
+
|
|
141
|
+
if (coord.length > 0) {
|
|
142
|
+
geojson.features[0].geometry.coordinates = coord;
|
|
143
|
+
mapInstance.getSource(`line-animation${index}`).setData(geojson);
|
|
144
|
+
requestAnimationFrame(animateLine);
|
|
145
|
+
} else {
|
|
146
|
+
// remove all sources to allow for new animation
|
|
147
|
+
mapInstance.removeLayer(`line-animation${index}`);
|
|
148
|
+
mapInstance.removeSource(`line-animation${index}`);
|
|
149
|
+
mapInstance.removeSource(`startPoint${index}`);
|
|
150
|
+
|
|
151
|
+
if (lastLine) {
|
|
152
|
+
mapInstance.removeSource('endpoint');
|
|
153
|
+
resolve();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
animateLine();
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
originPoints.forEach((coordinate, index) => {
|
|
163
|
+
lineFlight(coordinate, endPoint, index, index === originPoints.length - 1);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* This function generates a map marker for each borrowerPoint
|
|
170
|
+
* @param {Map Instance} mapInstance - the map instance
|
|
171
|
+
* @param {Array} borrowerPoints - array of borrower objects
|
|
172
|
+
* @returns {void}
|
|
173
|
+
* */
|
|
174
|
+
export function generateMapMarkers(mapInstance, borrowerPoints) {
|
|
175
|
+
const geojson = {
|
|
176
|
+
type: 'FeatureCollection',
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
geojson.features = borrowerPoints.map((borrower) => ({
|
|
180
|
+
type: 'Feature',
|
|
181
|
+
properties: {
|
|
182
|
+
message: 'test',
|
|
183
|
+
image: borrower.image,
|
|
184
|
+
iconSize: [80, 80],
|
|
185
|
+
},
|
|
186
|
+
geometry: {
|
|
187
|
+
type: 'Point',
|
|
188
|
+
coordinates: borrower.location,
|
|
189
|
+
},
|
|
190
|
+
}));
|
|
191
|
+
// add markers to map
|
|
192
|
+
geojson.features.forEach((marker) => {
|
|
193
|
+
// create a DOM element for the marker
|
|
194
|
+
const el = document.createElement('div');
|
|
195
|
+
el.className = 'map-marker';
|
|
196
|
+
el.style.backgroundImage = `url(${marker.properties.image})`;
|
|
197
|
+
el.style.width = `${marker.properties.iconSize[0]}px`;
|
|
198
|
+
el.style.height = `${marker.properties.iconSize[1]}px`;
|
|
199
|
+
|
|
200
|
+
// Possible place to add an event listener
|
|
201
|
+
// el.addEventListener('click', () => {
|
|
202
|
+
// window.alert(marker.properties.message);
|
|
203
|
+
// });
|
|
204
|
+
|
|
205
|
+
// add marker to map
|
|
206
|
+
// maplibregl should be defined in the KvMap component
|
|
207
|
+
// eslint-disable-next-line no-undef
|
|
208
|
+
new maplibregl.Marker({ element: el })
|
|
209
|
+
.setLngLat(marker.geometry.coordinates)
|
|
210
|
+
.addTo(mapInstance);
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* This function animates a series of lines from an array of starting coordinates to a single end point
|
|
216
|
+
* then animates removing the line from the origin to the end point
|
|
217
|
+
* then flies to the next point in the array and repeats the animation
|
|
218
|
+
* returns a promise when the animation is complete
|
|
219
|
+
* @param {Map Instance} mapInstance - the map instance
|
|
220
|
+
* @param {Array} borrowerPoints - array of borrower objects
|
|
221
|
+
* @returns {Promise} - promise that resolves when the animation is complete
|
|
222
|
+
* */
|
|
223
|
+
export function animationCoordinator(mapInstance, borrowerPoints) {
|
|
224
|
+
return new Promise((resolve) => {
|
|
225
|
+
const destinationPoints = borrowerPoints.map((borrower) => borrower.location);
|
|
226
|
+
const totalNumberOfPoints = destinationPoints.length;
|
|
227
|
+
let currentPointIndex = 0;
|
|
228
|
+
|
|
229
|
+
const flyToPoint = (index) => {
|
|
230
|
+
mapInstance.flyTo({
|
|
231
|
+
// These options control the ending camera position: centered at
|
|
232
|
+
// the target, at zoom level 9, and north up.
|
|
233
|
+
center: destinationPoints[index],
|
|
234
|
+
zoom: 4,
|
|
235
|
+
bearing: 0,
|
|
236
|
+
|
|
237
|
+
// These options control the flight curve, making it move
|
|
238
|
+
// slowly and zoom out almost completely before starting
|
|
239
|
+
// to pan.
|
|
240
|
+
speed: 0.9, // make the flying slow
|
|
241
|
+
curve: 1, // change the speed at which it zooms out
|
|
242
|
+
|
|
243
|
+
// This can be any easing function: it takes a number between
|
|
244
|
+
// 0 and 1 and returns another number between 0 and 1.
|
|
245
|
+
easing(t) {
|
|
246
|
+
return t;
|
|
247
|
+
},
|
|
248
|
+
|
|
249
|
+
// this animation is considered essential with respect to prefers-reduced-motion
|
|
250
|
+
essential: true,
|
|
251
|
+
}, { flyEnd: true });
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
// This will trigger the next steps in the animation chain
|
|
255
|
+
mapInstance.on('moveend', (event) => {
|
|
256
|
+
if (event.flyEnd === true) {
|
|
257
|
+
animateLines(mapInstance, randomCoordinates, destinationPoints[currentPointIndex])
|
|
258
|
+
.then(() => {
|
|
259
|
+
if (currentPointIndex < totalNumberOfPoints - 1) {
|
|
260
|
+
currentPointIndex += 1;
|
|
261
|
+
flyToPoint(currentPointIndex);
|
|
262
|
+
} else {
|
|
263
|
+
resolve();
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// fly to point 1
|
|
270
|
+
flyToPoint(currentPointIndex);
|
|
271
|
+
});
|
|
272
|
+
}
|
package/vue/KvMap.vue
CHANGED
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
</template>
|
|
14
14
|
|
|
15
15
|
<script>
|
|
16
|
+
import { animationCoordinator, generateMapMarkers } from '../utils/mapAnimation';
|
|
17
|
+
|
|
16
18
|
export default {
|
|
17
19
|
name: 'KvMap',
|
|
18
20
|
props: {
|
|
@@ -87,6 +89,32 @@ export default {
|
|
|
87
89
|
type: Number,
|
|
88
90
|
default: 4,
|
|
89
91
|
},
|
|
92
|
+
/**
|
|
93
|
+
* Borrower points object.
|
|
94
|
+
* If this object is present, the advanced animation will be triggered
|
|
95
|
+
* Sample object:
|
|
96
|
+
* {
|
|
97
|
+
borrowerPoints: [
|
|
98
|
+
{
|
|
99
|
+
image: 'https://www-kiva-org.freetls.fastly.net/img/w80h80fz50/e60a3d61ff052d60991c5d6bbf4a45d3.jpg',
|
|
100
|
+
location: [-77.032, 38.913],
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
image: 'https://www-kiva-org.freetls.fastly.net/img/w80h80fz50/6101929097c6e5de48232a4d1ae3b71c.jpg',
|
|
104
|
+
location: [41.402, 7.160],
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
image: 'https://www-kiva-org.freetls.fastly.net/img/w80h80fz50/11e018ee3d8b9c5adee459c16a29d264.jpg',
|
|
108
|
+
location: [-73.356596, 3.501],
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
* }
|
|
112
|
+
*/
|
|
113
|
+
advancedAnimation: {
|
|
114
|
+
type: Object,
|
|
115
|
+
required: false,
|
|
116
|
+
default: () => ({}),
|
|
117
|
+
},
|
|
90
118
|
},
|
|
91
119
|
data() {
|
|
92
120
|
return {
|
|
@@ -174,10 +202,18 @@ export default {
|
|
|
174
202
|
this.wrapperObserver = this.createIntersectionObserver({
|
|
175
203
|
targets: [this.$refs?.[this.refString]],
|
|
176
204
|
callback: (entries) => {
|
|
205
|
+
// only activate autoZoom if we have an initialZoom set
|
|
177
206
|
entries.forEach((entry) => {
|
|
178
207
|
if (entry.target === this.$refs?.[this.refString] && !this.zoomActive) {
|
|
179
208
|
if (entry.intersectionRatio > 0) {
|
|
180
|
-
|
|
209
|
+
// activate zoom
|
|
210
|
+
if (this.initialZoom !== null) {
|
|
211
|
+
this.activateZoom();
|
|
212
|
+
}
|
|
213
|
+
// animate map
|
|
214
|
+
if (this.advancedAnimation?.borrowerPoints) {
|
|
215
|
+
this.animateMap();
|
|
216
|
+
}
|
|
181
217
|
}
|
|
182
218
|
}
|
|
183
219
|
});
|
|
@@ -291,13 +327,40 @@ export default {
|
|
|
291
327
|
dragRotate: false,
|
|
292
328
|
});
|
|
293
329
|
|
|
294
|
-
|
|
295
|
-
|
|
330
|
+
this.mapInstance.on('load', () => {
|
|
331
|
+
// signify map has loaded
|
|
332
|
+
this.mapLoaded = true;
|
|
333
|
+
// Create wrapper observer to watch for map entering viewport
|
|
334
|
+
if (this.initialZoom !== null || this.advancedAnimation?.borrowerPoints) {
|
|
335
|
+
this.createWrapperObserver();
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
},
|
|
339
|
+
animateMap() {
|
|
340
|
+
// remove country labels
|
|
341
|
+
this.mapInstance.style.stylesheet.layers.forEach((layer) => {
|
|
342
|
+
if (layer.type === 'symbol') {
|
|
343
|
+
this.mapInstance.removeLayer(layer.id);
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
// generate map markers for borrower points
|
|
347
|
+
generateMapMarkers(this.mapInstance, this.advancedAnimation.borrowerPoints);
|
|
296
348
|
|
|
297
|
-
//
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
349
|
+
// wait 500 ms before calling the animation coordinator promise
|
|
350
|
+
// to allow the map to scroll into view
|
|
351
|
+
setTimeout(() => {
|
|
352
|
+
animationCoordinator(this.mapInstance, this.advancedAnimation.borrowerPoints)
|
|
353
|
+
.then(() => {
|
|
354
|
+
// when animation is complete reset map to component properties
|
|
355
|
+
this.mapInstance.dragPan.enable();
|
|
356
|
+
this.mapInstance.scrollZoom.enable();
|
|
357
|
+
this.mapInstance.scrollZoom.enable();
|
|
358
|
+
this.mapInstance.easeTo({
|
|
359
|
+
center: [this.long, this.lat],
|
|
360
|
+
zoom: this.initialZoom || this.zoomLevel,
|
|
361
|
+
});
|
|
362
|
+
});
|
|
363
|
+
}, 500);
|
|
301
364
|
},
|
|
302
365
|
checkIntersectionObserverSupport() {
|
|
303
366
|
if (typeof window === 'undefined'
|
|
@@ -348,3 +411,40 @@ export default {
|
|
|
348
411
|
},
|
|
349
412
|
};
|
|
350
413
|
</script>
|
|
414
|
+
|
|
415
|
+
<style>
|
|
416
|
+
/* Styles for animation map markers defined in @kiva/kv-components/utils/mapAnimation.js */
|
|
417
|
+
.map-marker {
|
|
418
|
+
margin-top: -77px;
|
|
419
|
+
margin-left: 35px;
|
|
420
|
+
display: block;
|
|
421
|
+
border: none;
|
|
422
|
+
border-radius: 50%;
|
|
423
|
+
cursor: pointer;
|
|
424
|
+
padding: 0;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
.map-marker::after {
|
|
428
|
+
content: '';
|
|
429
|
+
position: absolute;
|
|
430
|
+
top: -8px;
|
|
431
|
+
left: -8px;
|
|
432
|
+
right: -8px;
|
|
433
|
+
bottom: -8px;
|
|
434
|
+
border-radius: 50%;
|
|
435
|
+
border: 4px solid #000;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.map-marker::before {
|
|
439
|
+
content: "";
|
|
440
|
+
width: 0;
|
|
441
|
+
height: 0;
|
|
442
|
+
left: -13px;
|
|
443
|
+
bottom: -32px;
|
|
444
|
+
border: 9px solid transparent;
|
|
445
|
+
border-left: 40px solid #000;
|
|
446
|
+
transform: rotate(114deg);
|
|
447
|
+
position: absolute;
|
|
448
|
+
z-index: -1;
|
|
449
|
+
}
|
|
450
|
+
</style>
|
|
@@ -13,6 +13,7 @@ export default {
|
|
|
13
13
|
useLeaflet: false,
|
|
14
14
|
width: null,
|
|
15
15
|
zoomLevel: 4,
|
|
16
|
+
advancedAnimation: {},
|
|
16
17
|
},
|
|
17
18
|
};
|
|
18
19
|
|
|
@@ -30,6 +31,7 @@ const Template = (args, { argTypes }) => ({
|
|
|
30
31
|
:use-leaflet="useLeaflet"
|
|
31
32
|
:width="width"
|
|
32
33
|
:zoom-level="zoomLevel"
|
|
34
|
+
:advanced-animation="advancedAnimation"
|
|
33
35
|
/>`,
|
|
34
36
|
});
|
|
35
37
|
|
|
@@ -67,3 +69,32 @@ Leaflet.args = {
|
|
|
67
69
|
useLeaflet: true,
|
|
68
70
|
zoomLevel: 6,
|
|
69
71
|
};
|
|
72
|
+
|
|
73
|
+
export const AdvancedAnimation = Template.bind({});
|
|
74
|
+
const advancedAnimation = {
|
|
75
|
+
borrowerPoints: [
|
|
76
|
+
{
|
|
77
|
+
image: 'https://www-kiva-org.freetls.fastly.net/img/w80h80fz50/e60a3d61ff052d60991c5d6bbf4a45d3.jpg',
|
|
78
|
+
location: [-77.032, 38.913],
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
image: 'https://www-kiva-org.freetls.fastly.net/img/w80h80fz50/6101929097c6e5de48232a4d1ae3b71c.jpg',
|
|
82
|
+
location: [41.402, 7.160],
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
image: 'https://www-kiva-org.freetls.fastly.net/img/w80h80fz50/11e018ee3d8b9c5adee459c16a29d264.jpg',
|
|
86
|
+
location: [-73.356596, 3.501],
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
};
|
|
90
|
+
AdvancedAnimation.args = {
|
|
91
|
+
initialZoom: null,
|
|
92
|
+
mapId: 5,
|
|
93
|
+
useLeaflet: false,
|
|
94
|
+
zoomLevel: 2,
|
|
95
|
+
height: 600,
|
|
96
|
+
width: 1000,
|
|
97
|
+
lat: 21.096,
|
|
98
|
+
long: -31.690,
|
|
99
|
+
advancedAnimation,
|
|
100
|
+
};
|