Responsive canvas vuejs - javascript

I'm trying to make my canvas fit its container, in a vue component.
I fire resizeCanvas() in mounted, but the container height and width are 0.
How can I have a canvas that fits its container, so that I can change the container size using css and it will update the canvas ?
(Sorry for my english, i'm not fluent)
<template lang="html">
<div class="container" ref="container">
<canvas ref="canvas" :width="width" :height="height"></canvas>
</div>
</template>
<script>
export default {
name: "monster",
data ()
{
return {
width: 512,
height: 512
}
}
methods: {
resizeCanvas(){
console.log(this.$refs.container.offsetWidth); // logs 0
this.width = this.$refs.container.offsetWidth
this.height = this.$refs.container.offsetWidth
}
},
mounted ()
{
console.log(this.$refs.container.offsetWidth) // logs 0
window.addEventListener("resize", this.resizeCanvas);
this.resizeCanvas()
},
unmounted() {
window.removeEventListener("resize", this.resizeCanvas);
},
}
</script>
<style lang="css" scoped>
.container {
width: 100%; height: 100%
}
</style>

The mounted lifecycle hook in VueJS does not guarantee that the DOM is ready: you will need to wait for this.$nextTick() and then check the dimensions of your container element.
You can do it by moving the logic into the callback of nextTick, i.e.:
mounted () {
this.$nextTick(() => {
console.log(this.$refs.container.offsetWidth);
window.addEventListener("resize", this.resizeCanvas);
this.resizeCanvas();
});
},
If you're familiar with the async/await way of doing things, then you can also do this:
async mounted () {
await this.$nextTick();
console.log(this.$refs.container.offsetWidth);
window.addEventListener("resize", this.resizeCanvas);
this.resizeCanvas();
},
Another possibility is to delegate this "wait for DOM to be ready" responsibility to the resizeCanvas function, so you have full abstraction and separation of concerns:
methods: {
async resizeCanvas(){
await this.$nextTick();
this.width = this.$refs.container.offsetWidth
this.height = this.$refs.container.offsetWidth
}
},
mounted () {
window.addEventListener("resize", this.resizeCanvas);
this.resizeCanvas();
},

Related

Hammerjs is not detecting swipe movement

I'm using Hammerjs 2.0.8 with Nuxt, and I can't seem to make it detect swipe motion. Here's how I've done the setup. If I change the recognizer to [Hammer.Pan, { direction: Hammer.DIRECTION_HORIZONTAL }] and on pan event hammer.on('pan', this.handleTouchEvents); it runs this.handleTouchEvents method.
<template>
<v-app>
<v-main>
<v-container id="touch-container" fluid>
<div class="items">
...
</div>
</v-container>
</v-main>
</v-app>
</template>
<script>
export default {
mounted () {
const Hammer = require('hammerjs')
const touchContainer = document.getElementById('touch-container')
const hammer = new Hammer.Manager(touchContainer, {
recognizers: [
[Hammer.Swipe, { direction: Hammer.DIRECTION_HORIZONTAL }]
]
})
hammer.on('swipe', this.handleTouchEvents)
},
methods: {
handleTouchEvents (e) {
alert(e.type)
}
}
}
</script>
<style scoped>
#touch-container {
height: 100%;
min-width: 100%;
overflow-x: hidden;
}
#items {
touch-action: pan-y;
}
</style>
Any idea how to resolve this issue?
touchContainer is probably undefined.
2 Things:
Check if its on client side with process.client
Wait until Vue created the elements on the UI with this.$nextTick()
Here try this:
async mounted () {
if(process.client) {
await this.$nextTick();
const Hammer = require('hammerjs')
const touchContainer = document.getElementById('touch-container')
const hammer = new Hammer.Manager(touchContainer, {
recognizers: [
[Hammer.Swipe, { direction: Hammer.DIRECTION_HORIZONTAL }]
]
})
hammer.on('swipe', this.handleTouchEvents)
}
},

How to change vue.js directive depending on viewport?

I have an element with the v-rellax directive, which is used to enable prallax scrolling in this div:
<div id="image" v-rellax="{ speed: -5 }"></div>
Now I need to change the speed property to -3 to adapt it to a different screen width. Is there a way to change speed:
1. in a `media query`
2. in `vue.js`
3. in `javascript`
Is there a simple way to do that with vue?
Edit:
I've tried implementing offered solution but the parallax scroll stops working. Have I done it wrong?
<script>
export default {
name: "home",
data: { rellax_speed: -5 },
created() {
window.addEventListener('resize', (event) => {
if (event.target.innerWidth >= 576) {
this.rellax_speed = -3;
return;
}
this.rellax_speed = -5;
})
}
}
</script>
template:
<div id="image" v-rellax="{ speed: rellax_speed }"></div>
Edit2:
<script>
export default {
name: "home",
data() {
return {
r_speed: -5
}
},
methods: {
onresize(event) {
if (event.target.innerWidth >= 576) {
this.r_speed = -3;
return;
}
this.r_speed = -5
}
},
created() {
window.addEventListener("resize", this.onresize)
},
beforeDestroy() {
window.removeEventListener("resize", this.onresize, true)
}
}
</script>
template:
<div id="image" v-rellax="{ speed: r_speed }"></div>
Okay I believe to understand you.
EDIT:
Thank you tony19 :)
Create vrellaxSpeed in data. Initial value: -5, use it in your directive.
data() {
return {
vrellaxSpeed: -5
}
},
methods:{
onResize(event) {
if (event.target.innerWidth > 1280) {
this.vrellaxSpeed = -3;
return;
}
this.vrellaxSpeed = -5;
}
},
created() {
window.addEventListener('resize', this.onResize)
},
beforeDestroy() {
window.removeEventListener('resize', this.onResize, true);
}
Create a computed in your vue component.
computed: {
getVrellaxSpeed() {
return window.innerWidth > 1280 ? -3 : -5;
}
}
Then, use it in your directive prop.
<div id="image" v-rellax="{ speed: getVrellaxSpeed }"></div>
I did not test it, please try this and give me feedback to help you :)

Set the dynamic height and width to Konva Stage in Vue.js

I want to set the height and width of the Konva based on the width of its parent container. As I was using static value for the stage which is
stageConfiguration:{ height: innerWidth * .5, width:innerWidth * 0.5}
And have to refresh every time I resize the screen. Is there a way to set the dynamic height and width to stageConfiguration. Current I am stuck on this and looking for a way to set dynamic height and width.
<template>
<div class="konvaContainer">
<v-stage :config="stageConfiguration" >
<v-layer>
</v-layer>
</v-stage>
</div>
</template>
<script>
export default {
components: {
Loading
},
data: () => ({
stageConfiguration: {}
)},
methods:
{
responsiveStage : function() {
var container = document.querySelector('.konvaContainer');
var width = container.clientWidth;
const stageWidth= width*.5;
const stageHeight= width *.5;
this.stageConfiguration ={
width:stageWidth,
height:stageHeight
}
}}
}
</script>
You can use ResizeObserver or resize event from the window to detect size changes.
Here is a solution with ResizeObserver:
<template>
<div class="container">
<v-stage ref="stage" :config="stageSize">
<v-layer>
<v-circle
:config="{
x: stageSize.width / 2,
y: stageSize.height / 2,
radius: stageSize.width / 2,
fill: 'green',
}"
/>
</v-layer>
</v-stage>
</div>
</template>
<style>
.container {
width: 100vw;
height: 100vh;
}
</style>
<script>
export default {
data() {
return {
stageSize: {
width: 10,
height: 10,
},
};
},
mounted() {
const container = document.querySelector(".container");
const observer = new ResizeObserver(() => {
this.stageSize.width = container.offsetWidth;
this.stageSize.height = container.offsetHeight;
});
observer.observe(container);
},
};
</script>
Demo: https://codesandbox.io/s/vue-konva-change-stage-size-or-resize-tbwec?file=/src/App.vue

How to change style of an element based on window width in Vue

I am trying to change the styling on an HTML element when the window size is smaller than 500px. Currently I have a computed property that defines a background-image for the element. I've tried using an if statement inside the computed property to check whether window.innerWidth < 500, but it seems like I can't do logic inside computed properties. My latest attempt was to create a method that checks the window width and if it is less than 500, it reasigns the computed property with the updated value. However, this does not seem to work.
Any help is appreciated. Thanks!
<template>
<div class="hero" :style="bgImage">
</template
data() {
return {
window: {
width: 0
}
}
},
created() {
window.addEventListener('resize', this.handleResize)
this.handleResize()
},
destroyed() {
window.removeEventListener('resize', this.handleResize)
},
methods: {
handleResize() {
if (window.width < 500) {
this.bgImage = `backgroundImage: linear-gradient(to bottom,rgba(245, 246, 252, 0) 45%,rgb(0, 0, 0) 100%), url(${this.hero.image})`
}
}
},
computed: {
bgImage() {
return {
backgroundImage: `url(${this.hero.image})`
}
}
}
Try to use computed to check your width data changes.
I changed your code this way
export default {
data() {
return {
window: {
width: 0,
},
},
computed: {
bgImage() {
if (this.width < 500) {
return {
backgroundImage: `linear-gradient(to bottom,rgba(245, 246, 252, 0) 45%,rgb(0, 0, 0) 100%), url(${this.hero.image})`
}
} else {
return {
backgroundImage: `url(${this.hero.image})`
}
}
}
}
created: function() {
window.addEventListener('resize', this.handleResize);
},
destroyed: function() {
window.removeEventListener('resize', this.handleResize);
},
methods: {
handleResize() {
this.width = window.innerWidth;
}
},
};

Vue - Setting the width of a component dynamically

I can't figure out how to dynamically set the width of a component.
I am using the component 'vue-burger-menu' -> https://github.com/mbj36/vue-burger-menu.
To set the width one needs to set the prop width to a number. As per the example below:
<Slide
ref="slideToggle"
disable-esc
class="slideToggle"
width="470"
right
:burger-icon="false"
:cross-icon="false"
disable-outside-click
>
It then sets a helper class - bm-menu width to the width. I have spent quite a while trying to figure out how to either set the prop's attribute dynamically or dynamically update the style.
For example I tried setting: this.$refs.slideToggle.$attrs.width = 1000 to no avail.
I can't bind a style to the bm-menu class as it's not visible.
How do I set the width so on the click of a button it changes (as per the button example below)?
Thanks for your help!
setDrawWidth() {
this.lineDrawWidth = "200px";
}
You just need binding which uses : before props:
<Slide
ref="slideToggle"
disable-esc
class="slideToggle"
:width="width"
right
:burger-icon="false"
:cross-icon="false"
disable-outside-click
>
And then in your data in js part:
export default {
data:() => ({ width: '470' }),
}
Now you just need to change width variable. For example:
export default {
data:() => ({ width: '470' }),
methods:{
changeWidth(){
this.width = '1000';
}
}
}
You can read more about binding variables from the doc: Vue Props
Listen on window width event:
data: () => ({
width: 470
})
created() {
window.addEventListener("resize", this.changeWidth);
},
destroyed() {
window.removeEventListener("resize", this.changeWidth);
},
methods: {
changeWidth(e) {
const screenWidth = window.innerWidth;
this.width = screenWidth
}
}
and set width in Slide component:
<Slide
ref="slideToggle"
disable-esc
class="slideToggle"
:width="width"
right
:burger-icon="false"
:cross-icon="false"
disable-outside-click
>

Categories