toggle animations

This commit is contained in:
Valentin 2024-11-14 02:31:10 +01:00
parent f2680fc65a
commit 9bf84290ac
8 changed files with 258 additions and 79 deletions

View File

@ -10,23 +10,32 @@ export const useMapStore = defineStore('mapState', {
currentPlace: Object, currentPlace: Object,
maxZoom: Number, maxZoom: Number,
currentZoom: Number, currentZoom: Number,
duration: 3, animationsAreEnabled: true,
animationDuration: 3,
}), }),
actions: { actions: {
zoomToPlace(lat, long) { zoomToPlace(lat, long) {
if (useLayoutStore().isDesktop) long = long - 0.03; if (useLayoutStore().isDesktop) long = long - 0.03;
this.map.flyTo([lat, long], this.maxZoom, { duration: this.duration }); this.map.flyTo(
[lat, long],
this.maxZoom,
{ animate: this.animationsAreEnabled, animationDuration: this.animationDuration });
this.currentZoom = this.maxZoom; this.currentZoom = this.maxZoom;
}, },
resetMap() { resetMap() {
this.map.flyTo(this.defaultMapCenter, useLayoutStore().isDesktop ? this.defaultZoomDesktop : this.defaultZoomMobile, { duration: this.duration }); console.log(this.defaultMapCenter);
this.map.flyTo(
this.defaultMapCenter,
useLayoutStore().isDesktop ? this.defaultZoomDesktop : this.defaultZoomMobile,
{ animate: this.animationsAreEnabled, animationDuration: this.animationDuration });
this.currentZoom = useLayoutStore().isDesktop ? this.defaultZoomDesktop : this.defaultZoomMobile; this.currentZoom = useLayoutStore().isDesktop ? this.defaultZoomDesktop : this.defaultZoomMobile;
}, },
lockMap() { lockMap() {
setTimeout(() => { setTimeout(() => {
this.map.options.minZoom = this.currentZoom; this.map.options.minZoom = this.currentZoom;
this.map.options.maxZoom = this.currentZoom; this.map.options.maxZoom = this.currentZoom;
}, this.duration * 1000 + 100); }, this.animationDuration * 1000 + 100);
this.map.dragging.disable(); this.map.dragging.disable();
this.map.touchZoom.disable(); this.map.touchZoom.disable();
this.map.doubleClickZoom.disable(); this.map.doubleClickZoom.disable();
@ -34,17 +43,28 @@ export const useMapStore = defineStore('mapState', {
this.map.boxZoom.disable(); this.map.boxZoom.disable();
this.map.keyboard.disable(); this.map.keyboard.disable();
// map.tap.disable(); // map.tap.disable();
}, },
unlockMap() { unlockMap() {
this.map.options.minZoom = useLayoutStore().isDesktop ? this.defaultZoomDesktop : this.defaultZoomMobile; this.map.options.minZoom = useLayoutStore().isDesktop ? this.defaultZoomDesktop : this.defaultZoomMobile;
this.map.options.maxZoom = this.maxZoom; this.map.options.maxZoom = this.maxZoom;
this.map.dragging.enable(); this.map.dragging.enable();
this.map.touchZoom.enable(); this.map.touchZoom.enable();
this.map.doubleClickZoom.enable(); this.map.doubleClickZoom.enable();
this.map.scrollWheelZoom.enable(); this.map.scrollWheelZoom.enable();
this.map.boxZoom.enable(); this.map.boxZoom.enable();
this.map.keyboard.enable(); this.map.keyboard.enable();
// map.tap.enable(); // map.tap.enable();
}, },
toggleAnimation() {
this.animationsAreEnabled = !this.animationsAreEnabled;
},
checkReducedMotion() {
const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
this.animationsAreEnabled = !mediaQuery.matches;
mediaQuery.addEventListener('change', (event) => {
this.animationsAreEnabled = !event.matches;
});
},
}, },
}); });

View File

@ -2,6 +2,7 @@ import { createApp } from 'vue';
import { createPinia } from 'pinia'; import { createPinia } from 'pinia';
import router from '../router/router'; import router from '../router/router';
import Modale from '../vuejs/Modale.vue'; import Modale from '../vuejs/Modale.vue';
import AnimationToggle from '../vuejs/AnimationToggle.vue';
import VueImageZoomer from 'vue-image-zoomer'; import VueImageZoomer from 'vue-image-zoomer';
import 'vue-image-zoomer/dist/style.css'; import 'vue-image-zoomer/dist/style.css';
@ -9,14 +10,20 @@ import { useContentStore } from '../stores/content';
import { useMapStore } from '../stores/map'; import { useMapStore } from '../stores/map';
export function initVueContentModale() { export function initVueContentModale() {
const pinia = createPinia();
const app = createApp(Modale) const app = createApp(Modale)
.use(createPinia()) .use(pinia)
.use(router) .use(router)
.use(VueImageZoomer); .use(VueImageZoomer);
const store = useContentStore(); const store = useContentStore();
const mapStore = useMapStore(); const mapStore = useMapStore();
app.mount('#content-modale'); app.mount('#content-modale');
const animationToggle = createApp(AnimationToggle)
.use(pinia)
.mount('#animation-toggle');
return { store, mapStore, router }; return { store, mapStore, router };
} }

View File

@ -0,0 +1,37 @@
<template>
<div @click="handleClick" class="animation-toggle-container">
<div><p>Activer les animations</p></div>
<label class="switch">
<input type="checkbox" v-model="animationsAreEnabled" @click.stop @change="toggleAnimation" />
<span class="slider round"></span>
</label>
</div>
</template>
<script>
import { useMapStore } from '../stores/map';
import { storeToRefs } from 'pinia';
export default {
setup() {
const mapStore = useMapStore();
const { animationsAreEnabled } = storeToRefs(mapStore);
const toggleAnimation = () => {
mapStore.toggleAnimation();
};
const handleClick = () => {
toggleAnimation();
};
return {
handleClick,
toggleAnimation,
animationsAreEnabled,
};
},
};
</script>

View File

@ -1,5 +1,8 @@
<template> <template>
<Transition> <Transition
:enter-active-class="animationsAreEnabled ? 'v-enter-active' : 'no-transition'"
:leave-active-class="animationsAreEnabled ? 'v-leave-active' : 'no-transition'"
>
<div v-if="!loading && (contentType === 'etape' || contentType === 'static')"> <div v-if="!loading && (contentType === 'etape' || contentType === 'static')">
<div class="content-wrapper"> <div class="content-wrapper">
<ModaleHeader <ModaleHeader
@ -81,7 +84,7 @@ const {
error, error,
} = storeToRefs(store); } = storeToRefs(store);
const { map, duration } = storeToRefs(mapState); const { defaultMapCenter, animationDuration, animationsAreEnabled } = storeToRefs(mapState);
let isModaleEtape, wasModaleEtape; let isModaleEtape, wasModaleEtape;
@ -105,30 +108,56 @@ const handleMapMovement = () => {
if (!loading.value) { if (!loading.value) {
isModaleEtape = contentType.value === 'etape'; isModaleEtape = contentType.value === 'etape';
if (!wasModaleEtape && isModaleEtape) { // Define helper functions in variables
// national -> détail const disableModaleTransition = () => {
document.documentElement.style.setProperty('--modale-enter-delay', `${duration.value}s`); document.documentElement.style.setProperty('margin-top', '0');
mapState.zoomToPlace(content.value.coordinates.lat, content.value.coordinates.lon); document.documentElement.style.setProperty('transition', 'none');
} else if (wasModaleEtape && isModaleEtape) {
// détail -> détail
document.documentElement.style.setProperty('--modale-leave-delay', 0);
document.documentElement.style.setProperty('--modale-enter-delay', `${duration.value * 2}s`);
mapState.resetMap(map.value);
setTimeout(() => {
mapState.zoomToPlace(content.value.coordinates.lat, content.value.coordinates.lon);
}, duration.value * 1000);
} else if (wasModaleEtape && !isModaleEtape) {
// détail -> national
document.documentElement.style.setProperty('--modale-leave-delay', 0);
document.documentElement.style.setProperty('--modale-enter-delay', `${duration.value}s`);
mapState.resetMap();
} else if (!wasModaleEtape && !isModaleEtape) {
// national -> national
console.log('national -> national');
document.documentElement.style.setProperty('--modale-leave-delay', 0);
document.documentElement.style.setProperty('--modale-enter-delay', '0.5s');
} }
const setModaleTransition = (enterDelay) => {
document.documentElement.style.setProperty('--modale-enter-delay', `${enterDelay}s`);
};
const zoomToContentPlace = () => {
mapState.zoomToPlace(
content.value.coordinates.lat ? content.value.coordinates.lat : defaultMapCenter.value.lat,
content.value.coordinates.lon ? content.value.coordinates.lon : defaultMapCenter.value.lng
);
};
if (animationsAreEnabled.value) {
if (isModaleEtape) {
if (!wasModaleEtape) {
console.log('national -> détail');
setModaleTransition(animationDuration.value);
zoomToContentPlace();
} else {
console.log('détail -> détail');
setModaleTransition(animationDuration.value);
//mapState.resetMap();
zoomToContentPlace();
//setTimeout(zoomToContentPlace, animationDuration.value * 1000);
}
} else {
if (wasModaleEtape) {
console.log('détail -> national');
setModaleTransition(animationDuration.value);
mapState.resetMap();
} else {
console.log('national -> national');
setModaleTransition(0);
}
}
} else {
if (isModaleEtape) {
zoomToContentPlace();
} else {
mapState.resetMap();
}
disableModaleTransition();
}
scrollTo(0, 0);
wasModaleEtape = isModaleEtape; wasModaleEtape = isModaleEtape;
} }
@ -150,7 +179,7 @@ onMounted(() => {
} }
.v-leave-active { .v-leave-active {
transition: margin-top 0.5s ease-in var(--modale-leave-delay); transition: margin-top 0.5s ease-in;
} }
.v-enter-from, .v-enter-from,
@ -160,6 +189,12 @@ onMounted(() => {
.v-enter-to, .v-enter-to,
.v-leave-from { .v-leave-from {
margin-top: 0vh; margin-top: 0vh;
}
/* This class will disable transitions */
.no-transition {
margin-top: 0 !important;
transition: none !important;
} }
</style> </style>

View File

@ -5,7 +5,7 @@
</div> </div>
<div v-if="contentType === 'etape' && (content.previous || content.next)" class="related-etape-links"> <div v-if="contentType === 'etape' && (content.previous || content.next)" class="related-etape-links">
<div v-if="content.previous" class="card previous" @click="displayRelatedElement(content.previous.url)"> <div v-if="content.previous" class="card previous" @click="clickRelatedElement(content.previous.url)">
<div class="icon"> <div class="icon">
<div :style="{ backgroundColor: content.previous.couleur }"></div> <div :style="{ backgroundColor: content.previous.couleur }"></div>
<div :style="{ backgroundColor: content.previous.couleur }"></div> <div :style="{ backgroundColor: content.previous.couleur }"></div>
@ -21,7 +21,7 @@
</div> </div>
</div> </div>
</div> </div>
<div v-if="content.next" class="card next" @click="displayRelatedElement(content.next.url)"> <div v-if="content.next" class="card next" @click="clickRelatedElement(content.next.url)">
<div class="icon"> <div class="icon">
<div :style="{ backgroundColor: content.next.couleur }"></div> <div :style="{ backgroundColor: content.next.couleur }"></div>
<div :style="{ backgroundColor: content.next.couleur }"></div> <div :style="{ backgroundColor: content.next.couleur }"></div>
@ -63,4 +63,13 @@ async function displayRelatedElement(href) {
await store.fetchContentData(baseUrl + href); await store.fetchContentData(baseUrl + href);
document.title = store.pageTitle; document.title = store.pageTitle;
} }
import { setActiveNavItem } from '../../utils/set-active-nav-item.js';
function clickRelatedElement(href) {
const baseUrl = window.location.protocol + "//" + window.location.host;
if (href.startsWith(baseUrl)) href = href.replace(baseUrl, '');
setActiveNavItem('etape', href);
displayRelatedElement(href);
}
</script> </script>

View File

@ -597,6 +597,7 @@ body{
margin: 30px 0; margin: 30px 0;
transform: scale(1); transform: scale(1);
opacity: 1; opacity: 1;
padding-right: 0.5rem;
transition: transform 0.3s ease-out, opacity 0.3s ease-out; transition: transform 0.3s ease-out, opacity 0.3s ease-out;
cursor: pointer; cursor: pointer;
> .infos-arret { > .infos-arret {
@ -1178,6 +1179,75 @@ body{
} }
} }
} }
> #animation-toggle > div {
cursor: pointer;
position: fixed;
bottom: $body-margin-bottom;
right: $body-margin-x;
z-index: 999;
display: flex;
align-items: center;
padding: 0.4rem 1.2rem;
border-radius: 10rem;
background-color: white;
transition: background-color 0.3s ease-out;
> div {
margin-right: 1rem;
> p {
font-size: $sm-font-size-mobile;
@media screen and (min-width: $desktop-min-width) {
font-size: $sm-font-size-desktop;
}
}
}
> .switch {
position: relative;
display: inline-block;
width: 2.2rem;
height: 1.2rem;
> input {
opacity: 0;
width: 0;
height: 0;
&:checked + .slider {
background-color: $brand-color;
}
&:focus + .slider {
box-shadow: 0 0 1px $brand-color;
}
&:checked + .slider::before {
-webkit-transform: translateX(1rem);
-ms-transform: translateX(1rem);
transform: translateX(1rem);
}
}
> .slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: 34px;
background-color: $main-color-light;
-webkit-transition: .4s;
transition: .4s;
&::before {
position: absolute;
content: "";
height: 1rem;
width: 1rem;
border-radius: 50%;
left: 0.1rem;
bottom: 0.1rem;
background-color: $light-color;
-webkit-transition: .4s;
transition: .4s;
}
}
}
}
} }
> .user-login-form { > .user-login-form {
height: 90vh; height: 90vh;

View File

@ -52,5 +52,6 @@
</div> </div>
{% endif %} {% endif %}
<div id="content-modale"></div> <div id="content-modale"></div>
<div id="animation-toggle"></div>
</div> </div>
{% endif %} {% endif %}