поправил фотографии
Gitea Actions Demo / build_and_push (push) Successful in 49s
Details
Gitea Actions Demo / build_and_push (push) Successful in 49s
Details
This commit is contained in:
parent
76b93dbadc
commit
b08870a3ef
|
|
@ -70,7 +70,7 @@
|
|||
color: 'gray',
|
||||
}"
|
||||
/>
|
||||
<yandex-map-clusterer :grid-size="64" zoom-on-cluster-click>
|
||||
<yandex-map-clusterer :grid-size="64">
|
||||
<yandex-map-marker
|
||||
v-for="photo in photosWithCoords"
|
||||
:key="'photo-' + photo.id"
|
||||
|
|
@ -83,10 +83,19 @@
|
|||
}"
|
||||
position="bottom-center"
|
||||
>
|
||||
<img :src="photo.url" class="map-photo-marker" :alt="photo.id" />
|
||||
<div class="photo-single">1</div>
|
||||
</yandex-map-marker>
|
||||
<template #cluster="{ length }">
|
||||
<div class="photo-cluster">{{ length }}</div>
|
||||
<template #cluster="{ clusterer, length }">
|
||||
<div
|
||||
:class="
|
||||
isSamePointCluster(clusterer as ClustererInfo)
|
||||
? 'photo-cluster photo-cluster-same'
|
||||
: 'photo-cluster'
|
||||
"
|
||||
@click="(e) => handleClusterClick(e, clusterer)"
|
||||
>
|
||||
{{ length }}
|
||||
</div>
|
||||
</template>
|
||||
</yandex-map-clusterer>
|
||||
</yandex-map>
|
||||
|
|
@ -446,7 +455,7 @@ import {
|
|||
ftpToZones,
|
||||
type PowerZone,
|
||||
} from "./PowerZonesPlugin.js";
|
||||
import type { LngLat, YMap } from "@yandex/ymaps3-types";
|
||||
import type { LngLat, LngLatBounds, YMap } from "@yandex/ymaps3-types";
|
||||
import {
|
||||
YandexMap,
|
||||
YandexMapDefaultSchemeLayer,
|
||||
|
|
@ -1204,6 +1213,69 @@ const openPhoto = (photo: WorkoutPhoto) => {
|
|||
showLightbox.value = true;
|
||||
}
|
||||
};
|
||||
type ClusterFeature = {
|
||||
geometry: { coordinates: [number, number, ...number[]] };
|
||||
};
|
||||
type ClustererInfo = {
|
||||
lnglat: [number, number, ...number[]];
|
||||
features: ClusterFeature[];
|
||||
};
|
||||
const CLUSTER_SAME_POINT_EPS = 1e-4;
|
||||
function isSamePointCluster(c: ClustererInfo): boolean {
|
||||
if (!c.features || c.features.length === 0) return true;
|
||||
const coords = c.features.map(
|
||||
(f) => f.geometry.coordinates as [number, number],
|
||||
);
|
||||
const lats = coords.map(([_lng, lat]) => lat);
|
||||
const lngs = coords.map(([lng]) => lng);
|
||||
const spreadLat = Math.max(...lats) - Math.min(...lats);
|
||||
const spreadLng = Math.max(...lngs) - Math.min(...lngs);
|
||||
return (
|
||||
spreadLat < CLUSTER_SAME_POINT_EPS && spreadLng < CLUSTER_SAME_POINT_EPS
|
||||
);
|
||||
}
|
||||
function openClusterPhotos(c: ClustererInfo) {
|
||||
if (!c.features || c.features.length === 0) return;
|
||||
const [lng, lat] = c.lnglat as [number, number];
|
||||
const idx = photos.value.findIndex(
|
||||
(p) =>
|
||||
p.latitude !== null &&
|
||||
p.longitude !== null &&
|
||||
Math.abs(p.longitude - lng) < CLUSTER_SAME_POINT_EPS &&
|
||||
Math.abs(p.latitude - lat) < CLUSTER_SAME_POINT_EPS,
|
||||
);
|
||||
if (idx >= 0) {
|
||||
lightboxIndex.value = idx;
|
||||
showLightbox.value = true;
|
||||
}
|
||||
}
|
||||
function zoomToCluster(c: ClustererInfo) {
|
||||
if (!c.features || c.features.length < 2 || !map.value) return;
|
||||
const coords = c.features.map(
|
||||
(f) => f.geometry.coordinates as [number, number],
|
||||
);
|
||||
const lngs = coords.map(([lng]) => lng);
|
||||
const lats = coords.map(([, lat]) => lat);
|
||||
const minLng = Math.min(...lngs);
|
||||
const maxLng = Math.max(...lngs);
|
||||
const minLat = Math.min(...lats);
|
||||
const maxLat = Math.max(...lats);
|
||||
const lngPad = (maxLng - minLng) * 0.25 || 1e-5;
|
||||
const latPad = (maxLat - minLat) * 0.25 || 1e-5;
|
||||
const bounds: LngLatBounds = [
|
||||
[minLng - lngPad, maxLat + latPad],
|
||||
[maxLng + lngPad, minLat - latPad],
|
||||
];
|
||||
map.value.setLocation({ bounds, duration: 500 });
|
||||
}
|
||||
function handleClusterClick(_event: MouseEvent, clusterer: unknown) {
|
||||
const c = clusterer as ClustererInfo;
|
||||
if (isSamePointCluster(c)) {
|
||||
openClusterPhotos(c);
|
||||
} else {
|
||||
zoomToCluster(c);
|
||||
}
|
||||
}
|
||||
function focusPhotoOnMap(photo: WorkoutPhoto) {
|
||||
if (photo.latitude === null || photo.longitude === null) return;
|
||||
map.value?.update({
|
||||
|
|
@ -1500,6 +1572,24 @@ h3 {
|
|||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
|
||||
cursor: pointer;
|
||||
}
|
||||
.photo-single {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
background: #007afc;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 2px solid #fff;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
|
||||
cursor: pointer;
|
||||
}
|
||||
.photo-cluster-same {
|
||||
background: #f59e0b;
|
||||
}
|
||||
.workout-photo-loc {
|
||||
position: absolute;
|
||||
bottom: 4px;
|
||||
|
|
|
|||
Loading…
Reference in New Issue