поправил фотографии
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',
|
color: 'gray',
|
||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
<yandex-map-clusterer :grid-size="64" zoom-on-cluster-click>
|
<yandex-map-clusterer :grid-size="64">
|
||||||
<yandex-map-marker
|
<yandex-map-marker
|
||||||
v-for="photo in photosWithCoords"
|
v-for="photo in photosWithCoords"
|
||||||
:key="'photo-' + photo.id"
|
:key="'photo-' + photo.id"
|
||||||
|
|
@ -83,10 +83,19 @@
|
||||||
}"
|
}"
|
||||||
position="bottom-center"
|
position="bottom-center"
|
||||||
>
|
>
|
||||||
<img :src="photo.url" class="map-photo-marker" :alt="photo.id" />
|
<div class="photo-single">1</div>
|
||||||
</yandex-map-marker>
|
</yandex-map-marker>
|
||||||
<template #cluster="{ length }">
|
<template #cluster="{ clusterer, length }">
|
||||||
<div class="photo-cluster">{{ length }}</div>
|
<div
|
||||||
|
:class="
|
||||||
|
isSamePointCluster(clusterer as ClustererInfo)
|
||||||
|
? 'photo-cluster photo-cluster-same'
|
||||||
|
: 'photo-cluster'
|
||||||
|
"
|
||||||
|
@click="(e) => handleClusterClick(e, clusterer)"
|
||||||
|
>
|
||||||
|
{{ length }}
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</yandex-map-clusterer>
|
</yandex-map-clusterer>
|
||||||
</yandex-map>
|
</yandex-map>
|
||||||
|
|
@ -446,7 +455,7 @@ import {
|
||||||
ftpToZones,
|
ftpToZones,
|
||||||
type PowerZone,
|
type PowerZone,
|
||||||
} from "./PowerZonesPlugin.js";
|
} from "./PowerZonesPlugin.js";
|
||||||
import type { LngLat, YMap } from "@yandex/ymaps3-types";
|
import type { LngLat, LngLatBounds, YMap } from "@yandex/ymaps3-types";
|
||||||
import {
|
import {
|
||||||
YandexMap,
|
YandexMap,
|
||||||
YandexMapDefaultSchemeLayer,
|
YandexMapDefaultSchemeLayer,
|
||||||
|
|
@ -1204,6 +1213,69 @@ const openPhoto = (photo: WorkoutPhoto) => {
|
||||||
showLightbox.value = true;
|
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) {
|
function focusPhotoOnMap(photo: WorkoutPhoto) {
|
||||||
if (photo.latitude === null || photo.longitude === null) return;
|
if (photo.latitude === null || photo.longitude === null) return;
|
||||||
map.value?.update({
|
map.value?.update({
|
||||||
|
|
@ -1500,6 +1572,24 @@ h3 {
|
||||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
|
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
|
||||||
cursor: pointer;
|
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 {
|
.workout-photo-loc {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 4px;
|
bottom: 4px;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue