Vue 2 animation issue on hover - javascript

I have two pictures, when hovering over the first picture, a certain component is displayed and the second picture works in a similar way, which displays another component, my problems began when I decided to apply animation when hovering over the picture, that is, I wanted to make it appear a certain component with animation, but the problem is that the animation works only for the first component, and for the second component, not only the animation does not work, it is also not displayed, you can see my code in the sandbox
If you looked at my code, you might have noticed that I display components using v-for, but when I tried to display components manually (that is, without a loop), everything worked fine for me, that is, like this
<div style="margin-top: 200px;">
<slide-y-up-transition>
<RedExperience v-show="img1" key="img1"/>
</slide-y-up-transition>
<slide-y-up-transition>
<OrangeExperience v-show="img2" key="img2"/>
</slide-y-up-transition>
<slide-y-up-transition>
<GreenExperience v-show="img4" key="img4"/>
</slide-y-up-transition>
</div>
Here is my code from sandbox
<template>
<div>
<div style="display: flex; justify-content: center">
<div v-bind:key="index" v-for="(girl, index) in girls">
<img
style="width: 200px; height: 200px; margin: 5px"
#mouseover="mouseOver(girl)"
#mouseout="mouseout(girl)"
v-bind:src="girl.imgSrc"
alt="Snow"
/>
</div>
</div>
<slide-y-up-transition>
<component
v-for="(girl, index) in girls"
v-show="girl.hovered"
v-bind:key="index"
v-bind:is="girl.componentName"
></component>
</slide-y-up-transition>
</div>
</template>
<script>
import { SlideYUpTransition } from "vue2-transitions";
import MyFirstComponent from "./colors/myycomponent";
import myOtherComponent from "./colors/myothercomponent";
export default {
name: "HelloWorld",
components: {
MyFirstComponent,
myOtherComponent,
SlideYUpTransition,
},
data() {
return {
componentNames: ["MyFirstComponent", "myOtherComponent"],
girls: [
{
imgSrc:
"https://www.gettyimages.com/gi-resources/images/500px/983794168.jpg",
hovered: false,
hoverColor: "#337700",
componentName: "MyFirstComponent",
},
{
imgSrc:
"https://www.gettyimages.com/gi-resources/images/500px/983794168.jpg",
hovered: false,
hoverColor: "#123456",
componentName: "myOtherComponent",
},
],
};
},
methods: {
mouseOver: function (girl) {
girl.hovered = true;
},
mouseout: function (girl) {
girl.hovered = false;
},
},
};
</script>

the problem is in your loop, transitions can only be used on a single element.
So do your loop this way to solve it.
<div v-for="(girl, index) in girls" v-bind:key="index">
<slide-y-up-transition>
<component
v-show="girl.hovered"
v-bind:is="girl.componentName"
></component>
</slide-y-up-transition>
</div>

your full code has to be like this.
<template>
<div>
<div style="display: flex; justify-content: center">
<div v-bind:key="index" v-for="(girl, index) in girls">
<img
style="width: 200px; height: 200px; margin: 5px"
#mouseover="mouseOver(girl, index)"
#mouseout="mouseout(girl, index)"
v-bind:src="girl.imgSrc"
alt="Snow"
/>
</div>
</div>
<div v-for="(girl, index) in girls" v-bind:key="index">
<slide-y-up-transition >
<component
v-show="girl.hovered"
v-bind:is="girl.componentName"
></component>
</slide-y-up-transition>
</div>
</div>
</template>
<script>
import { SlideYUpTransition } from "vue2-transitions";
import MyFirstComponent from "./colors/myycomponent";
import myOtherComponent from "./colors/myothercomponent";
export default {
name: "HelloWorld",
components: {
MyFirstComponent,
myOtherComponent,
SlideYUpTransition,
},
data() {
return {
componentNames: ["MyFirstComponent", "myOtherComponent"],
girls: [
{
imgSrc:
"https://www.gettyimages.com/gi-resources/images/500px/983794168.jpg",
hovered: false,
hoverColor: "#337700",
componentName: "MyFirstComponent",
},
{
imgSrc:
"https://i.pinimg.com/originals/a9/76/af/a976af5c7bf3cc5b05a1b301334e0f68.jpg",
hovered: false,
hoverColor: "#123456",
componentName: "myOtherComponent",
},
],
};
},
methods: {
mouseOver: function (girl, index) {
this.girls[index].hovered = true;
},
mouseout: function (girl, index) {
this.girls[index].hovered = false;
},
},
};
</script>

Related

How to pre-load images into nuxt-dropzone

I'm working with nuxt-dropzone https://www.npmjs.com/package/nuxt-dropzone to upload images. I would also like to use it as an image library, so users can view their images in the same way they uploaded them. I'm not finding a lot of documentation on nuxt-dropzone, so I'm wondering how to pre-load the images into the dropzone.
Here's my code:
<template>
<v-card style="border-radius: 0; box-shadow: none" class="px-10">
<v-row justify="center">
<div class="pt-8" style="width: 1050px">
<v-tabs
:centered="false"
:hide-slider="true"
:fixed-tabs="false"
class="hls-tabs py-1"
height="100%"
>
<v-tab
:ripple="false"
class="hls-tab background py-3 tabs-0"
active-class="primary tab-active"
>
<div class="background px-1 tab-container">
<p class="tab-content py-4 subheading">Upload Image</p>
</div>
</v-tab>
<v-tab
:ripple="false"
class="hls-tab background py-3 tabs-1"
active-class="primary tab-active"
>
<div class="background px-1 tab-container">
<p class="tab-content py-4 subheading">Image Library</p>
</div>
</v-tab>
<v-tab-item class="tab-item tab-item-0">
<div class="tab-item-content px-15 py-15" style="min-height: 400px">
<dropzone
id="imageUpload"
ref="imageUpload"
v-model="testImages"
:options="options"
:destroy-dropzone="true"
:style="
isUploaded
? 'border: 0; border-radius: 25px'
: 'border: 4px dashed rgb(111, 118, 167); border-radius: 25px'
"
#vdropzone-file-added="onFileAdded"
#vdropzone-removed-file="onFileRemove"
>
<v-icon size="60" color="rgb(176, 173, 173)" class="pb-5"
>mdi-file-upload-outline</v-icon
>
<h3 style="color: rgb(111, 118, 167)" class="pb-5">
To Upload Media, drag files here
</h3>
<h3 style="color: rgb(111, 118, 167)" class="pb-5">OR</h3>
<h3 style="color: rgb(111, 118, 167)">click to select file</h3>
</dropzone>
</div>
</v-tab-item>
<v-tab-item class="tab-item tab-item-0">
<div class="tab-item-content" style="min-height: 400px">
<dropzone
id="imageLibrary"
ref="imageLibrary"
:destroy-dropzone="true"
:options="libraryOptions"
>
<div
v-for="image in testImages"
:key="image.dataURL"
class="dropzone-previews dropzone"
>
<img :src="image.dataUrl" :height="200" :width="200" />
</div>
</dropzone>
</v-tabs>
</div>
</v-row>
</v-card>
</template>
<script>
import dropzone from 'nuxt-dropzone';
import 'nuxt-dropzone/dropzone.css';
import defaultImage from '~/assets/empty_photo.png';
import second from '~/assets/google_mic.png';
export default {
name: 'ImageUploadDialog',
components: { dropzone },
data() {
return {
images: [],
youtubeUrl: '',
isUploaded: false,
testImages: [defaultImage, second],
options: {
url: 'https://httpbin.org/post',
addRemoveLinks: true
},
libraryOptions: {
url: 'https://httpbin.org/post'
}
};
},
methods: {
handleClose() {
this.$nuxt.$emit('close-image-upload');
},
handleSave() {
for (const file of this.$refs.imageUpload.dropzone.files) {
this.images.push(file);
}
const lastIndex = this.youtubeUrl.lastIndexOf('/');
const youtubeIdentifier = this.youtubeUrl.slice(lastIndex + 1);
},
onFileAdded() {
this.isUploaded = true;
},
onFileRemove() {
if (this.$refs.imageUpload.dropzone.files.length === 0) {
this.isUploaded = false;
}
}
}
};
</script>
I've tried to use the "instance" to emit on load, but I keep getting an error that it cannot read the property of dropzone.
mounted() {
const instance = this.$refs.imageLibrary.dropzone;
for (const image of this.testImages) {
const mockFile = { name: image, size: 12345 };
instance.emit('addedFile', mockFile);
}
},
Use dropzone.js manuallyAddFile() function, with nextTick to render uploaded pictures correctly
mounted(){
this.$nextTick(() => {
for(const image of this.testImages){
let url = `${imageLink}`;
let file = { size: 123, name: image, type: "image/png" };
this.$refs.myVueDropzone1.manuallyAddFile(file, url);
}
});
}
Okay so what was happening was because the dropzones are in a drawer, they were being loaded as soon as the page loads. The refs were only being recognized when the dropzone was actually displayed on the screen. When I did a 'window.setTimeout', the refs worked. So the way I solved it was to put the second dropzone in its own component.
The second component then looked like this:
<template>
<div class="tab-item-content" style="min-height: 400px">
<dropzone
id="imageLibrary"
ref="imageLibrary"
:destroy-dropzone="true"
:options="libraryOptions"
>
</dropzone>
</div>
</template>
<script>
import dropzone from 'nuxt-dropzone';
import 'nuxt-dropzone/dropzone.css';
import defaultImage from '~/assets/empty_photo.png';
import second from '~/assets/google_mic.png';
export default {
name: 'ImageLibrary',
components: { dropzone },
data() {
return {
testImages: [defaultImage, second],
libraryOptions: {
url: 'https://httpbin.org/post'
}
};
},
mounted() {
for (const image of this.testImages) {
console.log('image', image);
const url = `${image}`;
const file = { size: 123, name: `${image}`, type: 'image/png' };
this.$refs.imageLibrary.manuallyAddFile(file, url);
}
}
};
</script>

Vue JS - How to bind two event #mouseover and #click at the same time

I have four pictures, when I hover the mouse over them, a certain component is displayed from below, but I still need to bind the click event, that is, when I click on the picture, the component should be displayed; when the component is clicked again, the problem is that I cannot bind two events at once at the same time
I understand that when the user hovers over the component, it will be immediately displayed and the click event will be useless, but I will need it in the mobile version
You can also look at my code in codesandbox
<template>
<div>
<div class="enjoy_headline_container">
<div class="EnjoyGirlsContainer">
<div>
<h3 style="margin: 0"></h3>
</div>
<div class="EnjoyGirlsList">
<div
v-for="(chunk, index) in Math.ceil(EnjoyGirlsList.length / 2)"
:key="'chunk-' + index"
:class="'wrap-' + index"
>
<div
v-for="(item, index) in EnjoyGirlsList.slice(
(chunk - 1) * 2,
chunk * 2
)"
:key="'img-' + index"
class="EnjoyCard"
:class="'EnjoyCard-' + index"
>
<div v-on:click="isHidden = !isHidden">
<img
#mouseover="mouseOver(item, (hover = true))"
:src="item.imagePath"
alt="Snow"
/>
</div>
<div class="EnjoyCardContainer">
<div
:style="{ background: item.textColor }"
class="EnjoyCardChildContainer"
>
<h3 class="EnjoyCardChildContainerTitleName">
{{ item.titleName }}
</h3>
</div>
</div>
<div
v-if="selected === item && !isHidden"
class="below-all-description EnjoyGirlsHoverEffect"
>
<div class="next-to-description EnjoyGirlsHoverEffect">
<div
style="width: 100%; display: flex; justify-content: center"
v-for="(enjoy, index) in EnjoyGirlsList"
:key="index"
>
<div
#mouseleave="mouseout(enjoy, (hover = false))"
class="EnjoyGirlsChildHoverEffect"
>
<component
v-show="enjoy.hovered"
v-bind:is="enjoy.componentName"
></component>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div v-if="!isHidden" class="below-all-description">
<template v-if="selected === null"></template>
<template v-else>
<div
style="width: 100%; display: flex; justify-content: center"
v-for="(enjoy, index) in EnjoyGirlsList"
:key="index"
>
<div
#mouseleave="mouseout(enjoy, (hover = false))"
class="EnjoyGirlsChildHoverEffect"
>
<component
v-show="enjoy.hovered"
v-bind:is="enjoy.componentName"
></component>
</div>
</div>
</template>
</div>
</div>
</div>
</div>
</template>
<script>
import EnjoyBlue from "./components/EnjoyBlue";
import EnjoyGreen from "./components/EnjoyGreen";
import EnjoyYellow from "./components/EnjoyYellow";
import EnjoyRed from "./components/EnjoyRed";
export default {
name: "HomePage",
components: {
EnjoyRed,
EnjoyYellow,
EnjoyGreen,
EnjoyBlue,
},
data() {
return {
isHidden: false,
selected: null,
hover: false,
sectionGirlsListComponentsNames: [
"EnjoyRed",
"EnjoyYellow",
"EnjoyGreen",
"EnjoyBlue",
],
EnjoyGirlsList: [
{
imagePath: "https://i.ibb.co/mCpNXhG/IMG-6061-min.png",
titleName: "TEENS",
textColor: "#74C8C5",
hovered: false,
componentName: "EnjoyBlue",
},
{
imagePath: "https://i.ibb.co/WvJjwsN/Rectangle-2.png",
titleName: "MINXES",
textColor: "#76ED00",
hovered: false,
componentName: "EnjoyGreen",
},
{
imagePath: "https://i.ibb.co/7khc5f0/Rectangle-3.png",
titleName: "MILFS",
textColor: "#FFE600",
hovered: false,
componentName: "EnjoyYellow",
},
{
imagePath: "https://i.ibb.co/6nz97Bw/Rectangle-4.png",
titleName: "COURGARS",
textColor: "#CC003D",
hovered: false,
componentName: "EnjoyRed",
},
],
};
},
methods: {
mouseOver: function (enjoy) {
this.EnjoyGirlsList.forEach((enjoy) => (enjoy.hovered = false));
this.selected = enjoy;
enjoy.hovered = true;
if (this.hover) {
console.log("4949494");
}
},
mouseout: function (enjoy) {
this.selected = null;
enjoy.hovered = false;
},
mouseEnter: function () {},
Prev() {
this.$refs.carousel.prev();
},
showNext() {
this.$refs.carousel.next();
},
},
};
</script>
And so if you looked at this code in codesandbox (I left the link at the top), then you might have noticed that hover only works the first time after that it does not work, only the click event works
Your click event toggles isHidden. When you click on it you set isHidden to true. After that it won't show, if you hover over it since you are hiding it with v-if:
<div v-if="!isHidden" class="below-all-description">
...
</div>
Solution:
In your mouseOver function you explicitly have to set isHidden to false.
methods: {
mouseOver: function (enjoy) {
this.isHidden = false;
this.EnjoyGirlsList.forEach((enjoy) => (enjoy.hovered = false));
...
}
...
}

Slot contents are merged between Tabs in VueJS

I have this TabPanel where I am rendering multiple tabs. Each Panel inside TabPanel uses/extends Panel where I am using slots. However, during tab switch content of Tabs are getting merged.
<template>
<div class>
<div class>
<ul class>
<li
style="display: inline-block; padding:10px; margin: 10px; border: 1px solid #ccc"
v-for="(panel, index) in panels"
:key="index"
#click="selectTab(index)"
:ref="'panel' + index"
>{{ panel.title }}</li>
</ul>
</div>
<div class>
<slot></slot>
</div>
</div>
</template>
<script>
export default {
name: "TabPanel",
data() {
return {
panels: [],
selectedIndex: 0
};
},
methods: {
selectTab(panelIndex) {
this.selectedIndex = panelIndex;
this.panels.forEach((panel, index) => {
panel.isActive = index === panelIndex;
});
}
},
created() {
this.panels = this.$children;
},
mounted() {
this.selectTab(0);
}
};
</script>
Panel code
<template>
<div v-show="isActive">
<slot></slot>
</div>
</template>
<script>
export default {
name: "Panel",
props: {
title: {
type: String,
required: true
},
panelId: {
type: String,
required: true
}
},
data() {
return {
isActive: true
};
}
};
</script>
Is there anyway to fix this issue. This is the CodeSandbox link to demonstrate this issue since its easier to visualize problem that way I think.
Dashboard.vue and RosePanel.vue changes isActive data but they are not doing nothing whit this.
One option is to set a v-show/v-if to each own <Panel>:
<Panel v-show="isActive" :title="title" :panelId="panelId">Content from Dashboard</Panel>
CodeSandbox: https://codesandbox.io/s/charming-hamilton-jz1og
The simplest way to fix the issue is to wrap all components in <Panel>-s:
<TabPanel>
<Panel title="Dashboard" panel-id="dashboard">
<Dashboard></Dashboard>
</Panel>
<Panel title="Test" panel-id="test">Content from Panel</Panel>
<Panel title="Rose Panel" panel-id="rose-panel">
<RosePanel></RosePanel>
</Panel>
</TabPanel>
https://codesandbox.io/s/eager-brown-6ethk?file=/src/App.vue
But this is probably not what you had in mind.
From what I see, you simply forgot to propagate the "is-active" property to the embedded panel:
<Panel :title="title" :panelId="panelId" :is-isActive="isActive">Content from Dashboard</Panel>
(e.x in the Dashboard Component)

How to render dynamic content "component" with custom tabs VueJs

i have a costume made Tabs and Tab from Laracasts Tabs Tutorial , and it work fine, but i need to load data when the tab is changed and i did that but when the data is loaded,
i need to render another component which have accordion and inside each accordion tab their some charts components need to be render also
so how can i render the accordion with the charts components when the Tabs tab is changed
Tabs Component:
<template>
<div class="tab-container -mt-px w-full">
<div class="tabs">
<ul class="list-reset flex border-b">
<li class="" v-for="(tab, index) in tabs" role="tab">
<a class="bg-white inline-block font-semibold hover:no-underline"
:class="[
{
'active-tab-link text-blue-dark border-l border-r active-tab-link-p': tab.isActive,
'text-blue hover:text-blue-darker non-active-tab-link-p': !tab.isActive
},
]"
:href="tab.href" #click="selectedTab(tab)">
{{tab.name}}
</a>
</li>
</ul>
</div>
<div class="tabs-details px-4 w-full">
<slot></slot>
</div>
</div>
</template>
<script>
export default {
name: "Tabs",
data() {
return {
tabs: []
};
},
created() {
this.tabs = this.$children;
// this.selectFirstTab();
},
methods: {
selectedTab(selectedTab) {
this.$emit('onTabChange', selectedTab);
this.tabs.map((tab, index) => {
tab.isActive = (tab.name === selectedTab.name)
});
},
}
}
</script>
Tab Component:
<template>
<div class="w-full" role="tabpanel" v-show="isActive">
<slot></slot>
</div>
</template>
<script>
import {isEmpty} from "../helpers/Helper";
export default {
name: "Tab",
props: {
name: {
type: String,
required: true
},
selected: {
type: Boolean,
default: false,
},
isFirst: {
type: Boolean,
default: false,
},
},
data() {
return {
// isActive: false,
isActive: true,
// isFirst: this.isFirst
};
},
computed: {
href() {
return this.formatHref(this.name);
},
},
mounted() {
this.selectTabFromURL();
},
methods: {
selectTabFromURL() {
let hash = this.$route.hash;
if (this.selected) {
this.isActive = this.selected;
} else if (!isEmpty(hash)) {
this.isActive = (this.formatHref(this.name) === hash);
} else if (this.isFirst) {
this.isActive = this.isFirst;
} else if (!this.isFirst && this.isActive) {
this.isActive = !this.isActive;
}
},
formatHref(id) {
return `#${id.toLowerCase().replace(/ /g, '-')}`;
}
}
}
</script>
the main component:
<template>
<!--components tabs start-->
<div class="flex flex-col">
<div class="mt-3 border-t-4 border-primary-color border-6 bg-white">
<div class=" border border-gray-400 lg:border-l-0 lg:border-t lg:border-gray-400 rounded-b lg:rounded-b-none lg:rounded-r leading-normal">
<Tabs #onTabChange="handleTabChange">
<!--:name="`${tab.name} - ${tab.component.type}`"-->
<Tab v-for="(tab, index) in page.tabs"
:key="tab.id"
:id="tab.slug"
:name="tab.name"
:slug="tab.slug"
:isFirst="index === 0"
>
<div>
How to render the dynamic accordion with the charts one time only no need to re-render
</div>
</Tab>
</Tabs>
</div>
</div>
</div>
<!--components tabs end-->
</template>
in normal HTML and JQuery, i can load the data and the render the result and append it to the wanted tab how can we do this with vue, dose the dynamic component help in this case ?
I searched for a solution and found and implement the "Creating Vue.js Component Instances Programmatically"

Laravel + Quasar #click to show modal not working

I'm trying to use quasar for the first time and I am trying to show a modal with a button but somehow the value won't turn to true which triggers my modal to show.
Home.vue
<template>
<div>
<q-layout>
<q-list highlight class="bg-white">
<q-list-header>Details
<q-btn color="green-5" #click="openAdd">Add New</q-btn>
</q-list-header>
<!-- <q-search/> -->
<q-item>
<q-item-side>
<q-item-tile avatar>
<img src="https://cdn.quasar.dev/logo/svg/quasar-logo.svg">
</q-item-tile>
</q-item-side>
<q-item-main label="John Doe" />
<q-item-side right>
<div>
<q-btn flat round icon="edit" color="yellow-8" small/>
<q-btn flat round icon="delete" color="red-8" small/>
<q-btn flat round icon="visibility" color="green-6" small/>
</div>
</q-item-side>
</q-item>
</q-list>
</q-layout>
<Add></Add>
</div>
</template>
Home.vue(script)
<script>
let Add = require('./Add.vue');
export default {
name: 'app',
components: {Add},
data(){
return{
addActive : ''
}
},
methods: {
openAdd() {
this.addActive = '';
}
}
}
</script>
Add.vue(modal):
<template>
<div>
<q-modal v-model="addActive" ref="layoutModal" :content-css="{minWidth: '50vw', minHeight: '50vh'}">
<q-modal-layout>
<q-toolbar slot="header" color="green-7">
<div class="q-toolbar-title">
Header
</div>
</q-toolbar>
<q-toolbar slot="footer" color="green-7">
<div class="q-toolbar-title">
Footer
</div>
<q-btn color="green-10" label="Save">Save</q-btn>
<q-btn color="red-9" #click="open = false" label="Close">Cancel</q-btn>
</q-toolbar>
</q-modal-layout>
</q-modal>
</div>
</template>
<script>
import {
QToolbar,
QToolbarTitle,
QBtn,
QModal,
QModalLayout
} from 'quasar-framework'
export default {
name: 'app',
components: {
QToolbar,
QToolbarTitle,
QBtn,
QModal,
QModalLayout
},
data() {
return {
layoutStore: {
view: 'lHh Lpr lFf',
reveal: false,
leftScroll: true,
rightScroll: true,
leftBreakpoint: 996,
rightBreakpoint: 1200,
hideTabs: false
}
}
},
data () {
return {
addActive: false
}
}
}
</script>
<style lang="stylus">
</style>
Any help is appreciated! Cheers and thanks!!
I tried turning the Add.vue's open value to return true and the modal shows up. But when I try to use the button to turn its value to true it does not work somehow.
You're defining
addActive as empty string in Home.vue
Have you tried
addActive: false (or true if you want it to open right away)
but you need to define in props as Boolean and pass those accordingly and not in data()

Categories

Resources