MapBox (mapbox-gl-vue) renders the map on only 50% of the width of the container - vue.js

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();
});;

Related

Vue transition on router - but transition effects specific html Element

I have a page transition for VUE js that I have implemented. I did this manually because I could not find how to do this using VUES transition.
(I am using gridsome framework for vue js - I have added a custom App.vue page - which should allow transitions of gridsome to act like normal Vue js transitions)
I feel like what I have done is bloated for its use case so wanted to see if anyone knew how to implement this using vue transtions.
#1
Users click component (which has a #click - triggering a this.$router.push() to the route)
#2
A div pops over the screen in the color of that component, creating a nice fade to hide the transition
#3
On the new page, another div identical to the transition one, now exits the screen.
I have this working here for reference, just click on clients (please try not to judge me to much, its still in development) -
https://wtwd.ninjashotgunbear.com/
MY METHOD:
Index.html
Each component is a SectionTitle when the user clicks on one of them they $emit the specific obj with the data for that page (such as the color && the name of the page to be routed to) - this is the #routeChange="reRoute($event) seen below:
<template>
<Layout>
<div class="navs" v-for="section in sections" :key="section.sectionTitle">
<!-- On click delay for screen to come ove top -->
<!-- router to be put here -->
<SectionTitle :data="section" #routeChange="reRoute($event)"/> <<<< COMPONENT that $emits on click
</div>
<!-- fullpage div to slide in and cover up no leave transition -->
<div class="leaveScreen"></div> <<<<< DIV that covers the screen
</Layout>
</template>
This triggers my method that moves the div over the UI view and creates the transition effect:
methods:{
reRoute(value){
console.log(value)
// 1) animate the loading screen
let screen = document.querySelector('.leaveScreen');
screen.style.cssText=`background: ${value.backgroundColor}; left: 0%`;
// 2) re-route the page
setTimeout(()=>{
this.$router.push(value.sectionLink)
}, 700)
}
}
CSS FOR DIV :
.leaveScreen {
position: absolute;
top: 0;
bottom: 0;
left: -100%;
width: 100%;
z-index: 11;
// background color added by the fn reRoute()
transition: all 0.7s;
}
The on the page, I use the mounted hook to remove the div from the users view (in the same, but other way around, way that I added it above.
mounted(){
let screen = document.querySelector('.fadeOutScreen');
// set timeout works to delay
setTimeout(()=>{
screen.style.cssText='left: 100%;'
},700)
}
If you know how to do this in a cleaner code / or by using VUES transition property then your help is very welcomed. I figured that VUE would have a specific way of doing this, but have not found it yet.
Thanks in advance -
W
If you wrap .leave-screen in a transition element you can do something like this:
new Vue({
el: "#app",
data: {
leaveScreen: false
}
})
body {
margin: 0;
}
.click-me {
cursor: pointer;
font-size: 30px;
}
.leave-screen {
position: absolute;
height: 100vh;
width: 100vw;
top: 0;
background-color: rgb(0, 0, 0);
}
.leave-screen-enter-active,
.leave-screen-leave-active {
background-color: rgba(0, 0, 0, 1);
transform: translateX(0);
transition: all 1s ease-in-out;
}
.leave-screen-leave-to,
.leave-screen-enter {
background-color: rgba(0, 0, 0, 0);
transform: translateX(-100%);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div #click="leaveScreen = true" class="click-me">
Click Me
</div>
<transition name="leave-screen">
<div v-if="leaveScreen" class="leave-screen" #click="leaveScreen = false"></div>
</transition>
</div>
.leave-screen-enter-active and .leave-screen-leave-active define the state of the element during transition.
.leave-screen-leave-to is the state the element leaves to (surprisingly) and .leave-screen-enter is the state of the element before it enters.
The styles you set on the element itself are where the transition starts/ends (depending on whether it's entering/leaving).
Vue's definitions:
v-enter: Starting state for enter. Added before element is inserted, removed one frame after element is inserted.
v-enter-active: Active state for enter. Applied during the entire entering phase. Added before element is inserted, removed when transition/animation finishes. This class can be used to define the duration, delay and easing curve for the entering transition.
v-leave-active: Active state for leave. Applied during the entire leaving phase. Added immediately when leave transition is triggered, removed when the transition/animation finishes. This class can be used to define the duration, delay and easing curve for the leaving transition.
v-leave-to: Only available in versions 2.1.8+. Ending state for leave. Added one frame after a leaving transition is triggered (at the same time v-leave is removed), removed when the transition/animation finishes.

Barba.js & GSAP new element appears before old element is gone

I'm trying to implement the basic GSAP fade-in / fade-out demo from the barber.js site.
The markup of test page one is as follows:
<body style="background-color: red; color: white;" data-barba="wrapper" data-barba="page1">
<h3>Constant</h3>
<main data-barba="container" data-barba-namespace="home">
<h1>Page 1</h1>
go to page 2
</main>
The markup of test page 2 is as follows:
<body style="background-color: white; color: red;" data-barba="page2">
<h3>Constant</h3>
<main data-barba="container" data-barba-namespace="home">
<h1>Page 2</h1>
go to page 1
</main>
With the following JS at each the bottom of each page:
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.3.4/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/#barba/core"></script>
<script>
barba.init({
//sync: true,
transitions: [{
name: 'opacity-transition',
leave(data) {
return gsap.to(data.current.container, {
opacity: 0
});
},
enter(data) {
return gsap.from(data.next.container, {
opacity: 0
});
}
}]
});
</script>
When leaving the current page the old element fades out OK, however the new element appears underneath a fraction early meaning I have two elements the new one jumping up as the old finishes disappearing?
Is there a way for the new one only to start appearing after the old one has finished?
I agree, the basic example is kinda failed. It turns out it depends on the styles a bit.
I managed to make it work adding display: 'none' to the leave transition, that forces the previous container to disappear before the next starts displaying:
// ...
leave(data) {
return gsap.to(data.current.container, {
opacity: 0,
display: 'none',
});
}
// ...
My best guess: the transition is meant to allow container overlapping. So you could get away with css (position: relative or something like that).

azure map will not render markers in correct position

I am trying to render a simple Azure Map in a vue.js single-file component. I can get the map to draw at a specified center and zoom. And draw a line segment exactly where I want it.
But I cannot draw a marker properly. It does draw, but it is seriously south-west from the specified coordinate (which is on the an endpoint of a line segment drawn previously).
Here's a single page Vue.js 'App.vue':
<template>
<div id="myMap"></div>
</template>
<script>
import * as atlas from "azure-maps-control";
export default {
mounted: function() {
this.map = new atlas.Map("myMap", {
center: [-113.666783, 53.806008],
zoom: 7,
view: "Auto",
authOptions: {
authType: "subscriptionKey",
subscriptionKey: "<redacted>",
},
});
let self = this;
//Wait until the map resources are ready.
this.map.events.add("ready", function() {
//Create a data source and add it to the map.
var dataSource = new atlas.source.DataSource();
self.map.sources.add(dataSource);
//Create a line and add it to the data source.
dataSource.add(
new atlas.data.LineString([
[-112.926043, 53.803],
[-113.666783, 53.806],
])
);
//Create a line layer to render the line to the map.
self.map.layers.add(
new atlas.layer.LineLayer(dataSource, null, {
strokeColor: "blue",
strokeWidth: 5,
})
);
//Create an HTML marker and add it to the map.
var marker1 = new atlas.HtmlMarker({
color: "DodgerBlue",
position: [-112.926043, 53.803],
anchor: "bottom",
htmlContent: '<div class="pulseIconNormal"></div>',
popup: new atlas.Popup({
content:
'<div style="padding:10px">Sensor</div>',
pixelOffset: [0, -30],
}),
});
self.map.markers.add(marker1);
//Add a click event to toggle the popup.
self.map.events.add("click", marker1, () => {
marker1.togglePopup();
});
});
}
}
</script>
<style>
#myMap {
height: 100vh;
width: 100vw;
}
.pulseIconNormal {
display: block;
width: 10px;
height: 10px;
border-radius: 50%;
background: blue;
}
</style>
When I looked at DOM for the marker (in Firefox dev tools), this is the style that I see:
transform: translate(-50%, -100%) translate(737px, 235px) rotateX(0deg) rotateZ(0deg);
This isn't coming from CSS, but is in inline. That's the reason, but not the explanation why. It appears the control itself is generating this.
I found the problem. I am using NPM to load azure-maps-control and I had to explicitly add
<style src='azure-maps-control/dist/atlas.min.css'></style>
to the .vue file.
The map div in your code isn't closed properly. Instead of <div id="myMap" /> it should be <div id="myMap"></div>. HTML standards say self closing div's are invalid. Give that a try and see if it helps.
If it doesn't try inspecting the HTML marker DOM to see if any CSS is being appended to it by your app and try adjusting to see if it addresses the issue.
Looking at your code, the HTML marker should be anchored bottom center to its position.
For the same problem with Angular (11), I had to add the azure css file to my angular.json like so:
"styles": [
"src/styles/styles.scss",
"node_modules/azure-maps-control/dist/atlas.min.css"
],

How to do width transition in Tailwind CSS?

When I try to do a transition using the default "w-#" options in Tailwind, my transitions don't apply. When I hard code in my own classes for width, it works fine. Is there something weird with Tailwinds CSS and how it handles width that would cause this?
Here's the HTML text. The main part here is the dynamic class "sidebarWidth" which switches when the button is clicked. The transition-all, slowest and ease are all things I extended in Tailwind.
<nav class="text-white absolute md:relative flex-col min-h-full bg-black mt-24 md:mt-12 transition-all transition-slowest ease" :class="sidebarWidth">
Here's the JS code in the computed properties of the Vue component
sidebarWidth: function() {
if (this.$store.getters.isSidebarCollapsed) {
return "w-14 invisible md:visible";
} else {
return "w-64";
}
}
If I swap out w-14 and w-64 for the following classes, it works great.
<style scoped>
.width1 {
width: 100px;
}
.width2 {
width: 400px;
}
</style>
I basically want my sidebar nav to slide in when I click a button. In mobile, the sidebar nav is hidden and I want it to slide out. In the desktop, it should be a small nav and then slide out to a full screen nav. It works, but the slide transition doesn't work. Also, the margin change between mobile and desktop does animate properly.
You need to perform a few steps to activate the behaviour you are looking for.
The first one is about extending you Tailwind theme via tailwind.config.js. You need to add the transitionProperty for width.
module.exports = {
...
theme: {
...
extend: {
...
transitionProperty: {
'width': 'width'
},
},
},
}
The changes above create the transition-width class. Simply apply this one to your nav tag. In your specific case you can overwrite the transition-all class.
<nav class="text-white absolute md:relative flex-col min-h-full bg-black mt-24 md:mt-12 transition-width transition-slowest ease" :class="sidebarWidth">
The last step is quite easy: ensure that Tailwind is recreating the CSS. Afterwards you should see a smooth width transition in your project.
Background to the Problem
By default, the width and height transitions are not activated in Tailwind CSS. Here is the CSS that will be applied when using transition class.
transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
Like you can see width and height are missing in transition-property.
You can find more information about it in the Tailwind documentation.
You can make your own transition property like in tailwind.config.js :
Multiple properties :
module.exports = {
theme: {
extend: {
transitionProperty: {
multiple: "width , height , backgroundColor , border-radius"
}
}
}
One property :
module.exports = {
theme: {
extend: {
transitionProperty: {
width: "width"
}
}
}

Scroll bar below fixed header with Vuetify + Electron

I am using Vuetify and Electron to make an app to help me with certain tasks at my job. I have disable the browserWindow frame and made my header the draggable area with a button to close the window. I am using the electron vuetify template
vue init vuetifyjs/electron
My problem is the scrollbar reaches all the way to the top but I would like it below my fixed header.
I have tried playing with overflow properties on the html, body, app div, and content div tags but i have not been successful.
How would I accomplish this?
This is purely a CSS question really as you can see this behaviour in the browser too with similar layouts. The easiest way to fix this is using a flex layout:
HTML:
<div class="container">
<div class="titlebar"></div>
<div class="content">
<h1>So much content we scroll</h1>
<h1>So much content we scroll</h1>
<!-- etc -->
</div>
</div>
CSS:
body {
margin: 0;
padding: 0;
overflow: hidden;
}
.container {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
}
.titlebar {
background-color: blue;
height: 35px;
flex-shrink: 0;
}
.content {
flex-grow: 1;
overflow-x: auto;
}
Check out this out in this CodePen
I'd like to offer a Vuetify specific answer for this question, this should apply whether or not Electron is involved.
Vuetify's default styles make this a bit more difficult than a simple CSS solution can give you, especially when the layout gets more complex.
For this example I'm using the complex layout from Vuetify's pre-defined themes here
Vuetify ships with an overflow-y: scroll on the html element so the first step is adding an override for this.
html {
overflow: hidden;
}
This will get rid of the bar on the right side that spans the whole height of the app.
Next you will want to set your v-content area as the scrollable area. There are a few gotchas to watch out for when you're setting this area:
Display flex is already declared
Vuetify sets padding in the style attribute so you'll need to override depending on your case
You'll need a margin the height of your header(only matters if you're changing header height from 64px)
You'll need to remove the header height from the height of the content container using calc(Same as above)
If you have a nav drawer on the right side you'll need to bind a class to take care of this.
My CSS for v-content looks like this, you will need an important to override the padding since it is set by Vuetify through style binding:
main.v-content {
width: 100vw;
height: calc(100vh - 64px);
flex-direction: column;
overflow: scroll;
margin-top: 64px;
padding-top: 0 !important;
}
I also have a class bound to the state of the temporary right drawer on the v-content tag in the template, this makes sure that the scroll bar doesn't disappear underneath the right nav drawer when it's open:
<v-content :class="{ draweropen: drawerRight }">
And the CSS for that bound class, once again you'll need an important to remove the default right padding Vuetify puts on v-content when the drawer is open:
.draweropen {
width: calc(100vw - 300px) !important;
padding-right: 0 !important;
}
You can optionally set the flex-direction to column-reverse if your content is bottom loaded like a chat which is what I'm doing in this CodePen Example
I built a little component that wraps the v-main and moves the scrollbar to the main container instead of the default (the entire html).
Simply replace v-main with this and you're done.
<template>
<v-main class="my-main">
<div class="my-main__scroll-container">
<slot />
</div>
</v-main>
</template>
<script>
export default {
mounted: function() {
let elHtml = document.getElementsByTagName('html')[0]
elHtml.style.overflowY = 'hidden'
},
destroyed: function() {
let elHtml = document.getElementsByTagName('html')[0]
elHtml.style.overflowY = null
},
}
</script>
<style>
.my-main
height: 100vh
.my-main__scroll-container
height: 100%
overflow: auto
</style>