Vuetify dynamic height for v-img - javascript

I have a container that is not 100% height of the page, it is 80% plus 60px on top and on bottom.
So the image inside should inherit the height of the container.
Using the <img /> and a little of CSS it is easy to achieve but I wish to add a template with loading spinner and so I started using the <v-img> for it.
I can see I can set the height or the max-height and so I did a method to calculate the parent height minus the 120px to get the exact image height, and it is working as expected.
The issue is when the user resize the window, I can see the method to be called and updating the value but the vuetify element is not responsive to the new values and so the image is bigger or smaller than container:
<div id="photoWrapper" class="photo-wrapper">
<v-img
:src="photo.url"
:contain="true"
:max-height="wrapperHeight()"
>
<template v-slot:placeholder>
<v-layout fill-height align-center justify-center ma-0>
<v-progress-circular
indeterminate
color="tertiary"
></v-progress-circular>
</v-layout>
</template>
</v-img>
</div>
Code:
data() {
return {
mounted: false
}
},
mounted() {
this.mounted = true
window.addEventListener('resize', this.wrapperHeight)
this.wrapperHeight()
},
methods: {
wrapperHeight() {
if (!this.mounted) return
console.log(document.getElementById('photoWrapper').offsetHeight - 120)
const height = document.getElementById('photoWrapper').offsetHeight - 120
return height
}
}
The method is always called on screen resize, but the image is not responsive.
How can I do it? I also tried to move the wrapperHeight to be a computed property.

You need to make your wrapperHeight is a computed property. That is correct and more performance than create it like method.
computed: {
wrapperHeight() {
if (!this.mounted) return
console.log(document.getElementById('photoWrapper').offsetHeight - 120)
const height = document.getElementById('photoWrapper').offsetHeight - 120
return height
}
}
And
:max-height="wrapperHeight" not :max-height="wrapperHeight()"

The solution is to do a mix between computed and methods like so:
data() {
return {
mounted: false,
containerHeight:
document.getElementById('photoWrapper').offsetHeight - 120
}
},
computed: {
wrapperHeight() {
if (!this.mounted) return
const height = this.containerHeight
return height
}
},
mounted() {
this.mounted = true
window.addEventListener('resize', _.debounce(this.handleResize, 100))
},
beforeDestroy() {
window.removeEventListener('resize', this.handleResize)
},
methods: {
handleResize() {
this.containerHeight =
document.getElementById('photoWrapper').offsetHeight - 120
}
}

I think you have to return the ${height}px in the wrapperHeight method

Related

React - How to remove image when the max width (mobile version)

I wouldike to remove or hide an image when I run the application in mobile version or if max-width is set :
<ImageWrapper key={image.src}>
<img src={getImageUrl(path, image.src)} srcSet={getSrcSet(path, image.src)} alt={image.alt} />
</ImageWrapper>
try these two steps:
in your js file add className to your component as follow:
<ImageWrapper key={image.src} className="YourClass">
<img src={getImageUrl(path, image.src)} srcSet={getSrcSet(path, image.src)} alt={image.alt} />
</ImageWrapper>
Then, set display parameter based on screen size in your CSS file:
.YourClass {
display:block;
/* other properties */
}
#media only screen and (max-width: 768px) {
.YourClass {
display:none;
}
}
You can get the width of the content in javascript if you like to do this in javascript for some reason.
cons: you have to populate your code with aditional javascript
pro: if the user is using only mobile, it will not request the image which is good to prevent aditional requests. If you use css display:none it will request the image anyway.
import { useState, useEffect } from 'react';
//
const [width, setWidth] = useState(window.innerWidth);
// method to update the width size
const handleWindowSizeChange = () => {
setWidth(window.innerWidth);
};
// create a eventListener to update the width every time the user resize the window
useEffect(() => {
handleWindowSizeChange();
window.addEventListener('resize', handleWindowSizeChange);
return () => {
window.removeEventListener('resize', handleWindowSizeChange);
};
}, []);
Now you can use the width to check the size and check if the size is mobile or not.
if(width < 700) { // isMobile }
TIP: Hide the image in the render:
{width < 700 && <img src="image.jpg" />}

Vue CSS how to move object across the screen when scrolling

I have a project where I try to hover an element from right to left in the window while scrolling. So when the user is scrolling down the element goes from right to left and when the user is scrolling up it goes from left to right.
I have tried to do this by adding a scroll event listener to the window and then check when this component is visible on the browser(This component is one of multiple components which are shown underneath each other in the application)
If some one knows a better way to achieve this please feel free to comment. I just thought of using the absolute positioning to move the object across the screen.
This is the code I am using at this moment but it doens't want to work. The object doesn't move and stays put.
data () {
return {
position: 0
}
},
created () {
window.addEventListener('scroll', (event) => {
this.runOnScroll()
})
},
mounted () {
const navbar = this.$refs.neonComponent
this.navbarOffset = navbar.offsetTop
},
methods: {
runOnScroll () {
if ((this.navbarOffset - 600) < window.pageYOffset) {
this.position = Math.round((window.pageYOffset - (this.navbarOffset - 600)) / 10)
this.$refs.neonText.style.right = this.position
} else {
if (this.position > 0) this.position = 0
}
// console.log(this.position)
}
}
<div ref="neonComponent" class="relative w-full min-h-screen bg-black flex justify-center items-center">
<div ref="neonText" class="absolute max-w-3xl text-center position-text">
<h2 class="text-7xl">This is an example text</h2>
</div>
</div>
Try adding a unit (px, %, vh, vw) to style.right, like so:
this.$refs.neonText.style.right = this.position + 'px';

Smooth expand/collapse transition using vue transition considering dynamic content heights

I’m facing some trouble with vue transitions, maybe some of you could help me out?! What I want to realise is a simple, lightweight collapse component using vue transitions.
Therefore in my current approach each time the collapse is collapsing the height of it should be set to contentHeight and then immediately after this to 0 (as well as the other way round when expanding). Or to speak in vue transition’s JavaScript hooks:
before-enter: set height to 0
enter: set height to contentHeight
after-enter: set height to auto
before-leave: set height to contentHeight
leave: set height to 0
This is the current approach:
<template>
<transition
#before-enter="beforeEnter"
#enter="enter"
#after-enter="afterEnter"
#before-leave="beforeLeave"
#leave="leave"
#after-leave="afterLeave"
>
<div
v-if="isCollapsed === false"
:style="componentStyles"
class="UiCollapse"
>
<div
ref="content"
class="UiCollapse-content"
>
<slot/>
</div>
</div>
</transition>
</template>
<script>
export default {
name: "UiCollapse",
props: {
isCollapsed: {
type: Boolean,
default: false
}
},
data() {
return {
componentStyles: {
height: undefined
}
};
},
methods: {
beforeEnter() {
this.setHeight(0);
},
enter() {
this.setHeight(this.getContentHeight());
},
afterEnter() {
this.setHeight(undefined);
},
beforeLeave() {
this.setHeight(this.getContentHeight());
},
leave() {
this.setHeight(0);
},
afterLeave() {
this.setHeight(0);
},
setHeight(height) {
this.componentStyles.height = height;
},
getContentHeight() {
return this.$refs.content.scrollHeight + "px";
}
}
};
</script>
<style scoped>
.UiCollapse {
display: block;
height: auto;
overflow: hidden;
}
.UiCollapse.v-enter-active,
.UiCollapse.v-leave-active {
transition: height 0.3s ease;
}
.UiCollapse-content {
display: block;
}
</style>
With this approach dynamic heights of the content and content changes affecting its height should be considered.
I also transferred this to a codesandbox: https://codesandbox.io/s/yq1nw51yq9
What I would expect here was a smooth opening and closing transition (as defined in css), but as you can see in this project this is working only partially and I do not understand why. Does any of you have any ideas?
Maybe this is not the correct approach, I’m open minded for alternative approaches or already existing libs (though I would prefer to understand vue transitions here), but the solutions I found so far were using fix values for max-height or other workarounds I’d like to avoid.
Thanks in advance for your help!
What about an expansion panel component ? I'm not sure what you really wanna do but it may help you at least. https://vuetifyjs.com/en/components/expansion-panels

HTML offsetLeft delays to change

I have four canvas in my screen, positioned two above and two below.
Each one have a button that makes possible to maximize the canvas, hiding the others.
This button is positioned above each canvas, with absolute position based on offsetTop and offsetLeft of the canvas.
However, when I maximize or minimize a canvas, the button formula updates only the width property.
The strange thing is that if I resize the screen, which also calls resize function, everything goes to the right place.
EDIT: Additional information: I am using VueJS and, in order to hide the other canvas, I apply v-show="false" to them parent divs, which only applies display: none.
Some snippets:
Initial resize and listener:
window.onload = function () {
resizeAll();
window.addEventListener('resize', resizeAll, false);
};
The resize hub:
function resizeAll () {
vue.$refs.panelOne.resizeDefault();
// ...
vue.$refs.panelN.resizeDefault();
}
The panel's resize default and resize method. The "expandStyles" is the css styles applied to the button:
resizeDefault() {
let dimensions;
if (this.expanded) {
dimensions = getScreenDimensions();
} else {
dimensions = getHalfScreenDimensions();
}
this.resize(dimensions.width, dimensions.height);
}
resize (width, height) {
this.canvas.width = width;
this.canvas.height = height;
this.expandStyles.top = (this.canvas.offsetTop + 10) + 'px';
this.expandStyles.left = (this.canvas.offsetLeft + this.canvas.width - 40) + 'px';
drawInterface.redraw();
}
And finally, the dimension getters:
function getScreenDimensions () {
return {
width: window.innerWidth - 310,
height: window.innerHeight * 0.92
};
}
function getHalfScreenDimensions () {
return {
width: (window.innerWidth - 310) / 2,
height: (window.innerHeight * 0.92) / 2
};
}
I agree with S.P.H.I.N.X , it seems like the problem lies with propagation of reactive properties in this.expandStyles to actual DOM. Would be useful if you can share the part of the template which has your canvases and positioning application.
Meanwhile, if setTimeOut doesn't feel right to you, you may do things more "Vue-way" by using Vue: nextTick to set order of operations (styles recalculation, redraw).

Function is not being executed within setTimeout

I am trying to use following code with React Native:
...
_getContentHeight() {
if (this.refs.AccordionContent) {
this.refs.AccordionContent.measure((ox, oy, width, height, px, py) => {
// Sets content height in state
this.setState({
height: this.props.expanded ? height : 0,
content_height: height
});
});
}
},
componentDidMount() {
// Gets content height when component mounts
// without setTimeout, measure returns 0 for every value.
// See https://github.com/facebook/react-native/issues/953
setTimeout(this._getContentHeight);
// tried setTimeout(this._getContentHeight.bind(this);
},
...
Now, when I debug the app in chrome, I see, that I never get to the _getContentHeight(). Now I am pretty sure that it has to do something with asynchronous calls etc. But is there a way to debug this, and see what values I get for height and content_height?
Help is needed, especially in understanding setTimeout/asynchronous function calls.
I'd suggest moving the call to measure inside componentDidMount to make sure your functionality works before abstracting it and dealing with any scoping or object reference issues that you might be facing now.
componentDidMount() {
this.refs.AccordionContent.measure((ox, oy, width, height, px, py) => {
// Sets content height in state
this.setState({
height: this.props.expanded ? height : 0,
content_height: height
});
});
}
Update:
I've wrapped your code in a react class and got the measure object to error out # https://jsfiddle.net/x7w62w99/
var Hello = React.createClass({
_getContentHeight() {
console.log(this.refs)
if (this.refs.AccordionContent) {
this.refs.AccordionContent.measure((ox, oy, width, height, px, py) => {
// Sets content height in state
this.setState({
height: this.props.expanded ? height : 0,
content_height: height
});
});
}
},
componentDidMount() {
setTimeout(this._getContentHeight);
},
render: function() {
return <div ref='AccordionContent'>
Hello {this.props.name}
<div id='AccordionContent'>
some stuff
</div>
</div>;
}
});
ReactDOM.render(
<Hello name="World" />,
document.getElementById('container')
);
Try this
setTimeout(function(){
this._getContentHeight.bind(this)
})
Use This:
setTimeout({
this._getContentHeight
}, the delay you want);
SetTimeout takes two parameteres.
http://www.w3schools.com/jsref/met_win_settimeout.asp

Categories