Update: I have put my answer. Please see it.
Original Question:
Can you tell me why leaflet map doesn't show on the full map area?
.html
<ion-content padding>
<div style="height: 100%;width:100%" leaflet [leafletOptions]="options" *ngIf="options" [leafletCenter]="center">
</div>
</ion-content>
.ts
init(): void {
this.options = {
layers: [
tileLayer("http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { maxZoom: 18, attribution: "..." })
],
zoom: 10,
center: this.center = latLng(Number(this.activatedRoute.snapshot.paramMap.get("latitude")), Number(this.activatedRoute.snapshot.paramMap.get("longitude"))),
attributionControl: false,
};
}
UI
Please see the image. It seems a problem with transform. Default it was 0,0,0. I have changed it manually on the browser like so. So how can I set it to the map?
I had the same issue in an ionic 4 project. Like suggested in comment with link I needed to use map.invalidateSize() when I rendered the map, as well as when adding/removing markers or any other action for that matter. This didn't help completely. I also had to manually trigger change detection after that. Here's a sample from our code:
this.map.setView([this.myLocation.latitude, this.myLocation.longitude], 13);
this.map.invalidateSize();
this.ref.detectChanges();
where ref refers to ChangeDetectorRef.
Also make sure that you have the the leaflet css imported in your angular.json file:
"styles": [
// ....
"./node_modules/leaflet/dist/leaflet.css"
]
In my opinion the leaflet is filled 100% in its parents space, can you use the browsers inspect tool to see what is the parents width and height. You can also try using
position: absolute; left: 0;
to ignore parents width
Try removing the padding and changing height to 100vh
Try this:
<ion-content>
<div style="height: 100vh; width:100%" leaflet [leafletOptions]="options" *ngIf="options" [leafletCenter]="center">
</div>
</ion-content>
If you want to set the transform do it like so:
<ion-content padding>
<div style="height: 100%; width:100%; transform: translate3D(250px, 200px, 100px)" leaflet [leafletOptions]="options" *ngIf="options" [leafletCenter]="center">
</div>
</ion-content>
Here I'm using this with Ionic 4 app. I have tried many solutions given by different devs. But none of them worked for me. The problem here is the life cycle event which I have used earlier. So now I use ionViewDidEnter() and no issues.
ionViewDidEnter() {
this.center = latLng(Number(this.activatedRoute.snapshot.paramMap.get("latitude")), Number(this.activatedRoute.snapshot.paramMap.get("longitude")));
this.options = {
layers: [
tileLayer("http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { maxZoom: 18, attribution: "..." })
],
zoom: 17,
center: this.center,
attributionControl: false,
};
this.layers.push(this.mapService.getMarker(this.center, "assets/icon/map/placeholder.png"));
}
I had the same problem, even with the import of leaflet.css and the "invalidateSize()" trick.
It was because I wrote in "myComponent.page.html":
<ion-content>
<div id="map" style="height:400px; width: 100%;"></div>
</ion-content>
It seems Ionic4 css conflicts with leaflet.
You need to get the <div> outside of <ion-content>.
Correct is just :
<div id="map" style="height:400px; width: 100%;"></div>
Hope this help someone
map.invalidateSize() runs after window resize event.
So after height change, just write the code :
window.dispatchEvent(new Event('resize'));
this.map.invalidateSize();
It works like a charm in my case.
Dont' forget to replace map with your map variable.
My it helps someone
Related
I'm trying to achieve a slide transition between two tabs. One tab is supposed to come from the left pushing the other one to the right and the opposite for the other one.
The leave transition goes well but the tab just pop in straight away without starting where it is supposed to...
I have made a CodePen to reproduce what I've tried : Slide transition test on CodePen
Here is the HTML, it is just a div containting 2 buttons that change the visibility of two div that represents my tabs content.
<div id="transition-test" class="demo">
<div class="tabs">
<button v-for="tab in tabs" class="tab" :key="tab.id" #click="selectedTab = tab.id"> {{tab.text}}</button>
<transition name="slide-right">
<div v-show="1 === selectedTab" class="tab1" key="tab1"></div>
</transition>
<transition name="slide-left">
<div v-show="2 === selectedTab" class="tab2" key="tab2"></div>
</transition>
</div>
</div>
In order to do the transition I do have the following css :
.slide-left,
.slide-right{
position: absolute;
}
.slide-right-enter-to,
.slide-right-leave {
opacity: 1;
transform: translateX(0);
}
.slide-right-enter,
.slide-right-leave-to {
opacity: 0;
transform: translateX(100%);
}
.slide-left-enter-active,
.slide-left-leave-active,
.slide-right-enter-active,
.slide-right-leave-active {
transition: all 500ms ease-in-out;
}
.slide-left-enter-to,
.slide-left-leave {
opacity: 1;
transform: translateX(0);
}
.slide-left-enter,
.slide-left-leave-to {
opacity: 0;
transform: translateX(-100%);
}
Does anyone have an idea about what I'm missing here ?
I found the issue. I don't know why in the Vue transition documentation the css class added at enter is v-enter but the class applied in reality is v-enter-from...
this css class :
.slide-left-enter
becomes :
.slide-left-enter-from
Instead of coding it by yourself, you can use npm version of the transition. It will also help you with its API, Guides and Examples, so that you don't have to worry about those.
I have a transition-group that renders a div, and within that is a component with a v-for attribute that renders several items. I then have a button that adds a new item to the beginning of the array. That transition works perfectly.
The only thing I don't like, is that the entire list loads with the transition on page load, and I'd like to disable it only on page load. I've searched Stack and Google but couldn't find a way. Is there a way to do this, so that transitions still works on button click, but is disabled for page load?
<transition-group
name="item-list"
tag="div">
<item-row
v-for="item in items"
:key="item.id"
:item="item" />
</transition-group>
.item-list-enter-active,
.item-list-leave-active,
.item-list-move {
transition : 250ms cubic-bezier(0.59, 0.12, 0.34, 0.95);
transition-property: opacity, transform;
}
.item-list-enter {
opacity : 0;
transform: translateX(50px) scaleY(0.5);
}
.item-list-enter-to {
opacity : 1;
transform: translateX(0) scaleY(1);
}
.item-list-leave-active {
position: absolute;
}
.item-list-leave-to {
opacity : 0;
transform : scaleY(0);
transform-origin: center top;
}
I wish I could've found a more "Vue-y" way of handling this, however I ended up going this route. Essentially I added a class to the body and removed all transitions. Then on the created lifecycle of my component, I remove that class. This removes the transition on page load, but still keeps the transition on click of the button like I want.
You can dynamically change the name value of the transition-group. Maybe on page load have a value different from the value that has the correct class name that the CSS targets. Then in the mounted lifecycle hook you can change it back to the correct class name.
You need to bind the duration for transition-group
template:
<transition-group
:duration="duration"
name="item-list"
tag="div">
<item-row
v-for="item in items"
:key="item.id"
:item="item" />
</transition-group>
script:
data() {
return {
duration: 0,
items: [
{id: 1},
{id: 2}
]
}
},
methods: {
add() {
if(this.duration===0) this.duration = 250
this.items.push({id: 'xxx'})
}
}
In case anyone comes across this like I did.
I ended up achieving this by having a transitionsOn flag added to the data (didn't seem to matter what it was initialised to), and a computed name for the transition, i.e.
<transition-group :name="transitionName">
in computed, I then had, for a transition called 'flash'
computed: {
transitionName() {
return this.transitionsOn ? 'flash' : 'disabled';
},
},
I would then set this.transitionsOn = true when I wanted it to fire.
Took a lot of fiddling about to figure this out but it seems to work OK
I'm using the amazing transition to slide router pages in vue.js
<template>
<div>
<header-comp></header-comp>
<transition
name="custom-classes-transition"
mode="out-in"
enter-active-class="animated slideInLeft"
leave-active-class="animated slideOutRight"
>
<router-view></router-view>
</transition>
<footer-comp></footer-comp>
</div>
</template>
<style>
#import 'https://cdn.jsdelivr.net/npm/animate.css#3.5.1';
</style>
It works very nice and smooth, but... the new coming page enter when the first one is totally gone. This made a gap between transition.
In Vue manual: Transition-Modes there are a few examples. I need to replicate the third button example but I'm missing the mode I have to use.
Any suggestion?
The main problem with your transitioning elements is that you want them to occupy the same space in DOM at the same time (even if, visually, one enters and one exists - that's only done through transforms but the two elements need to occupy the same space in DOM).
Therefore you need to give one of them position:absolute and use CSS to size and position it correctly, to match the exact position and size it would have when not having position:absolute (which is what it will have when not trasitioning).
Here's a working example. Note yours might need different styles applied to a different element.
Since you haven't provided a minimal, reproducible example with your own markup, there's no way to know.
In the example above, I gave the subsequent <div> (the entering one)
position: absolute;
width: 100%;
top: 60px;
left: 0;
If you choose to wrap all your <router-view>s into a common wrapper element with position:relative, top would need to be 0 (in the example 60px is accounting for <nav>'s height).
Note: and yes, as others already pointed, you don't need mode="in-out". But that still leaves you with the positioning issue.
Edit: I've played with two more examples.
one using a flexbox container of height:100vh where top and bottom elements don't grow and middle one does. When middle element is too big, it becomes scrollable.
another one where I played with the transition effects and Bootstrap Vue.
Actually since you don't need any special behaviour and actually want both transitions to happen at the same time, you shouldn't be using the mode at all. Just remove it and it should work as you described. From the docs link you pasted:
Simultaneous entering and leaving transitions aren’t always desirable though, so Vue offers some alternative transition modes
in-out: New element transitions in first, then when complete, the current element transitions out.
out-in: Current element transitions out first, then when complete, the new element transitions in.
mode="in-out": New element transitions in first, then when complete, the current element transitions out.
new Vue({
el: '#app',
data: {
message: 'Hello Vue!',
showOn: true
},
methods: {
handleClick() {
console.log(this.message);
}
}
})
.slide-fade-enter-active {
transition: all .3s ease;
position: absolute;
left: 0;
top: 0;
}
.slide-fade-leave-active {
position: absolute;
left: 0;
top: 0;
}
.slide-fade-enter, .slide-fade-leave-to {
transform: translateX(10px);
opacity: 0;
}
#app {
position: relative;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<body>
<div id="app">
<transition name="slide-fade" mode="in-out">
<button v-if="showOn"
key="on"
type="button"
#click="showOn=false">On</button>
<button v-else type="button"
key="off"
#click="showOn=true">Off</button>
</transition>
</div>
</body>
I'm trying to use the Vue transitions located on the vue docs (specifically the "Slide Fade") in order to animate the changing of text on a component. I have this particular tag set to render when a watched computed property returns from a vuex store. In order to transition the text through the various options, i have a recursive timeout function that sets the "showAnnouncement" property to false, sets the new text, then makes it true again. This works perfectly with or without the transition.
Here is a snippet from my template, showing it in action.
<div class="announcements">
<h2> Announcements </h2>
<transition name="slide-fade">
<h3 v-if="showAnnouncement">
<a :href="announcementCurrent.link" class="announcementLink">
{{announcementCurrent.title}}
</a>
- {{announcementCurrent.author}}
</h3>
</transition>
<!-- -->
</div>
One of the troubleshooting steps I took was to completely forgo the vue transition component, and set CSS transitions directly on the class. Which failed, so I've decided to go back to square one with vue transitions.
Here is the CSS for the transition, pulled directly from the documentation I've linked above:
.slide-fade-enter-active {
transition: all .3s ease;
}
.slide-fade-leave-active {
transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
}
.slide-fade-enter, .slide-fade-leave-to
/* .slide-fade-leave-active below version 2.1.8 */ {
transform: translateX(10px);
opacity: 0;
}
Sorry in advance if the spacing is a little off, it didn't seem to want to paste indented properly.
I'm a bit at a loss right now, and thank you in advance for your help.
P.S. I should also mention, that this project uses Vuetify, I don't think it would affect anything, but it might be good to know.
I am trying MapBox with Vue 2 and I cannot make the map take the full width of the container. It only renders on 50% of the width of the container.
I have included the files in the head of my index.html as follows:
<script src='https://api.mapbox.com/mapbox-gl-js/v0.40.0/mapbox-gl.js'></script>
<link href='https://api.mapbox.com/mapbox-gl-js/v0.40.0/mapbox-gl.css' rel='stylesheet' />
I want the map in a component (Map.vue, I am using vue-router), so here is the code in Map.vue:
Script:
import Mapbox from 'mapbox-gl-vue';
export default {
components: {
'mapbox': Mapbox
}
}
Template:
<mapbox access-token="pk.eyJ1Ijoic3BlZW5pY3Q....."
:map-options="{
style: 'mapbox://styles/mapbox/streets-v9',
center: [-96, 37.8],
zoom: 3
}"
:geolocate-control="{
show: true,
position: 'top-left'
}"
:scale-control="{
show: true,
position: 'top-left'
}"
:fullscreen-control="{
show: true,
position: 'top-left'
}">>
</mapbox>
Style:
#map {
width: 100%;
height: 600px;
position: absolute;
margin:0;
z-index:1;
}
I have tried everything I know in the CSS id but it only renders the map in the right half of the width of the container, in the left one only the logo and the controls are displayed while the rest of the area is empty.
To solve the problem, I just had to delete "text-align: center;" from #app in App.vue.
For more details, check the issue I had opened here:
https://github.com/phegman/vue-mapbox-gl/issues/11
It looks like to me, there is something dynamic with the div or the div is rendered later after the instantiation. I have not used vue, however.
I have had this problem with tabs and div rendered after the page load such as in tabs or triggered by JavaScript.
If you use map.invalidateSize(); where map is the object instantiated. This will redraw the map. Try and put this after the window is loaded to test the code. Then perhaps it can be converted into the correct Vue implementation.
window.addEventListener("load", function(){
map.invalidateSize();
});;