Vue: whitespace when carousel is sliding - javascript

I've encountered a problem when I want to create a carousel using VueJs. my problem is, there seems to be whitespace when the slider is running. When one image went to other images, there seems to be whitespace in an instant that fade after one second.
here's my code :
Vue.component('kangaroo-slider', {
template: `
<div id="carousel">
<div class="carousel__inner" >
<div class="carousel__item">
<a href="#">
<transition tag="div" :name="transitionName">
<img :src="image[current]" class="carousel__image" :key="current">
</transition>
</a>
</div>
</div>
</div>
`,
data: function() {
return {
image: [
'https://www.pocarisweat.id//assets/uploads/2019/09/54ad7ba3a1892e0bcc365d021507b713.png',
'https://www.pocarisweat.id//assets/uploads/2019/08/09b43d7b3fb60d5acf782f9510cb87a0.jpg',
'https://www.pocarisweat.id//assets/uploads/2019/08/00a75c18203defa69bc8ad7aace5f60b.jpg'
],
current: 0,
show: false,
transitionName: 'fade',
show: true
}
},
methods: {
slide() {
let maxSlide = this.image.length
setInterval(() => {
if (this.current < maxSlide - 1) {
this.transitionName = 'slide-next'
this.current++
} else {
this.transitionName = 'slide-prev'
this.current = 0
}
}, 2000)
}
},
created () {
this.slide()
},
})
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
you can see how it works here: https://codepen.io/learningfrontendweb/pen/ZEEvRRZ

You just need to make some changes in your css file
.carousel__inner {
width: 100%;
position: relative;
overflow: hidden;
height: 400px;
}
.carousel__image {
width: 100%;
position: absolute;
left: 0;
top: 0;
}
Anyway here is the link where I've fixed it:
https://codepen.io/Nevados/pen/mddXWYy

Related

How to convert the following JS code to VueJS

I have a JS code and I am trying to convert it to Vue. Basically, I am trying to use this code: https://codepen.io/kimdontdoit/pen/wvdKLJo
And how I am trying to implement this code into mine is:
<template>
<section id="successful-order" class="container">
<div class="row office-banner">
<div class="col-12">
<img :src="successful" alt="Popper Image"/>
<div class="sub-title">Your order is complete!</div>
<div>You will be receiving a confirmation email with your order details.</div>
<div class="button-area">
<button class="btn button">Checkout your tickets</button>
<button class="btn button-secondary">Go to homepage!</button>
</div>
</div>
</div>
</section>
</template>
<script>
import successful from "../../../../img/poppers.png";
export default {
data() {
return {
successful: successful,
color: ["#8b5642", "#6a696b"],
};
},
methods: {
frame() {
}
}
};
</script>
Basically, I also need to create frame() function but I am quite new in Vue so I couldn't figure it out.
Here's an example of a Vue app with the canvas-confetti example you listed with the codepen: https://codesandbox.io/s/canvas-confetti-vue2-psh9k?file=/src/components/HelloWorld.vue
You just need to instal canvas-confetti using npm.
Here's the code:
<template>
<h1 class="office-banner">IT IS YOUR BIRTHDAY.</h1>
</template>
<script>
import confetti from "canvas-confetti";
export default {
name: "HelloWorld",
data() {
return {
colors: ["#8b5642", "#6a696b"],
};
},
methods: {
frame() {
confetti({
particleCount: 2,
angle: 60,
spread: 55,
origin: { x: 0 },
colors: this.colors,
});
confetti({
particleCount: 2,
angle: 120,
spread: 55,
origin: { x: 1 },
colors: this.colors,
});
if (Date.now() < Date.now() + 15000) {
requestAnimationFrame(this.frame);
}
},
},
mounted() {
this.frame();
},
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.office-banner {
background-color: #e2ddd8;
font-family: "Arial";
padding: 0.125em;
font-size: 4em;
text-align: center;
white-space: nowrap;
transform: rotate(-10deg);
position: fixed;
top: 40%;
left: -5%;
right: -5%;
z-index: 100;
margin-top: 0;
}
</style>
Here's a good article with the title "Use Any JavaScript Library With Vue.js
" that you'd probably be interested in:
https://vuejsdevelopers.com/2017/04/22/vue-js-libraries-plugins/
You can copy the content of frame() from Codepen and in the mounted life cycle, call this frame() method
mounted: function () {
this.frame()
}
You would want your <script> block to look somewhat like this :
export default {
data() {
return {
successful: successful,
color: ["#8b5642", "#6a696b"],
};
},
methods: {
frame() {
confetti({
particleCount: 2,
angle: 60,
spread: 55,
origin: { x: 0 },
colors: this.data.color,
});
confetti({
particleCount: 2,
angle: 120,
spread: 55,
origin: { x: 1 },
colors: this.data.color,
});
if (Date.now() < Date.now() + 15000) {
requestAnimationFrame(frame);
}
}
},
mounted: function() {
this.frame();
}
};
The idea is you first declare the function frame(), then in the mounted hook you call that function. You can read more about the Vue instance and its lifecycle hooks here

Vue multiple components and access to Vuex properties

I'm learning Vue, using it with Vuex (without Webpack), but I have several questions when implementing this simple example, it's not clear for me in the docs.
Don't know why, but, I can't access the Vuex store using this pointer inside component computed property, for example:
this.$store.state.nav.title, leading me to use global app variable instead. Also, this.$parent and $root do not work.
Is it correct to initialize multiple Vue components at one time such as this, and shouldn't they have been mounted automatically when I pass components property to the Vue construct object? What is the right way to initialize, for example, the header, footer and body components at the same time?
var app = new Vue({
el: document.getElementById('app'),
data: {
title:store.state.nav.title
},
computed: {},
methods:{},
mounted:function(){},
updated:function(){},
store:store,
components:{
componentheader,
componentnavbar,
componentbody,
componentfooter
}
});
for (var companent_name in app.$root.$options.components) {
if(typeof app.$root.$options.components[companent_name] === 'function') {
var MyComponent = Vue.extend(app.$root.$options.components[companent_name]);
var component = new MyComponent().$mount();
document.getElementById('app').appendChild(component.$el);
}
}
Here is the full example:
var store = new Vuex.Store({
state: {
nav: {
title: 'my site'
}
},
mutations: {
changeTitle: function(t, a) {
this.state.nav.title = a;
}
}
});
var componentheader = Vue.component('componentheader', {
computed: {
title() {
return app.$store.state.nav.title
}
},
template: '#header_tpl',
mounted: function() {},
updated: function() {}
});
var componentnavbar = Vue.component('componentnavbar', {
computed: {
title() {
return app.$store.state.nav.title
}
},
template: '#navbar_tpl',
mounted: function() {},
updated: function() {}
});
var componentbody = Vue.component('componentbody', {
computed: {
title() {
return app.$store.state.nav.title
}
},
template: '#body_tpl',
mounted: function() {},
updated: function() {}
});
var componentfooter = Vue.component('componentfooter', {
computed: {
title() {
return app.$store.state.nav.title
}
},
template: '#footer_tpl',
mounted: function() {},
updated: function() {}
});
var app = new Vue({
el: document.getElementById('app'),
data: {
title: store.state.nav.title
},
computed: {},
methods: {},
mounted: function() {},
updated: function() {},
store: store,
components: {
componentheader,
componentnavbar,
componentbody,
componentfooter
}
});
Vue.use(Vuex);
for (var companent_name in app.$root.$options.components) {
if (typeof app.$root.$options.components[companent_name] === 'function') {
var MyComponent = Vue.extend(app.$root.$options.components[companent_name]);
var component = new MyComponent().$mount();
document.getElementById('app').appendChild(component.$el);
}
}
Vue.config.devtools = false;
Vue.config.productionTip = false;
* {
margin: 0;
padding: 0;
color: #fff;
text-align: center;
font-size: 19px;
}
html,
body,
.container {
height: 100%;
}
#app {
position: relative;
height: 100%;
min-height: 100%;
}
header {
width: 100%;
height: 80px;
}
nav.navbar {
box-sizing: border-box;
min-height: 100%;
padding-bottom: 90px;
width: 80px;
height: 100%;
position: absolute;
}
.container {
box-sizing: border-box;
min-height: 100%;
padding-bottom: 90px;
color: #000;
}
footer {
height: 80px;
margin-top: -80px;
}
footer,
nav,
header {
background: #000;
}
header div,
footer div {
padding: 15px;
}
nav ul {
list-style-type: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.5.1/vuex.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="app">
</div>
<script type="text/x-template" id="header_tpl">
<header class="header">
<div>
header {{ title }}
</div>
</header>
</script>
<script type="text/x-template" id="navbar_tpl">
<nav class="navbar">
<ul>
<li>navbar {{ title }}</li>
</ul>
</nav>
</script>
<script type="text/x-template" id="body_tpl">
<div class="container">
<div>
body {{ title }}
</div>
</div>
</script>
<script type="text/x-template" id="footer_tpl">
<footer class="footer">
<div>
footer {{ title }}
</div>
</footer>
</script>
</body>
</html>
You seem confused about Vue Instance and Vue Component. Basically you only need just one Vue instance with multiple components to create your app.
To answer your first question, it does not work because you didn't install the store to each Vue instance that you are created (you only install just 1 instance called app).
for (var companent_name in app.$root.$options.components) {
if (typeof app.$root.$options.components[companent_name] === 'function') {
var MyComponent = Vue.extend(app.$root.$options.components[companent_name]);
var component = new MyComponent({
store // <-- install here
}).$mount();
document.getElementById('app').appendChild(component.$el);
}
}
Working example here
Actually you can just use store since all store and app.$store and this.$store is the same object. The advantage of this.$store is you have no need to import store to every component file for Single File Components.
To answer your second question,
You are mixing about Global Registration and Local Registration. You should use only one for a component.
For render components you can define your template inside <div id="app"> just like:
<div id="app">
<componentheader></componentheader>
<componentnavbar></componentnavbar>
<componentbody></componentbody>
<componentfooter></componentfooter>
</div>
Working example here

vue gallery adding loading spinner

I created a gallery using Vue, Nuxt.
The full code of my gallery you can find on GitHub,
and you can see a live demo here vue gallery demo
Most of the logic is in the vue-lighbox component.
<script>
export default {
props: {
thumbnails: {
type: Array,
required: true,
},
images: {
type: Array,
required: true,
},
thumbnailPath: {
type: String,
required: true,
},
imagePath: {
type: String,
required: true,
},
},
data() {
return {
visible: false,
currentImage: 0,
}
},
methods: {
Toggle(index) {
this.currentImage = index
this.visible = !this.visible
},
Next() {
if (this.currentImage < this.images.length - 1) {
this.currentImage++
} else {
this.currentImage = 0
}
},
Prev() {
if (this.currentImage > 0) {
this.currentImage--
} else {
this.currentImage = this.images.length - 1
}
},
},
}
</script>
<template>
<div class="thumb_container">
<div v-for="(thumbnail, index) in thumbnails" :key="thumbnail" class="thumbnail" #click="Toggle(index)">
<img :src="thumbnailPath + thumbnail" />
<div class="plus">
<i class="icon icon-plus" />
</div>
<div class="color-overlay"></div>
</div>
<div v-if="visible" class="lightbox">
<i class="icon-cancel" #click="Toggle()" />
<i class="icon-left" #click="Prev()" />
<i class="icon-right" #click="Next()" />
<img :key="currentImage" :src="imagePath + [currentImage + 1] +'.jpg'" />
</div>
</div>
</template>
A quick break down of my component:
I have small thumbnail images and big images.
The thumbnail images are displayed with for loop. Whenever I click at some of the thumbnails the corresponding big image appears.
The gallery is working fine as intended, however, I have a problem with loading the photos from user experience aspect. So I need to implement a loading spinner while the big image is loading.
I really don't know the right approach to this. Does someone have an example to share or some hint to give me?
How can I check when a fallowing image is loaded?
You can use vue's v-on:load directive to determine whether to show an image or a spinner. In my app I've made this into a custom component:
<template>
<div>
<img :src="src"
:alt="alt"
#load="onLoaded"
#error="onError"
v-show="loaded && !error"
key="image"/>
<spinner-component v-show="loaded == false || error"/>
</div>
</template>
export default {
mounted() {
},
data() {
return {
loaded: false,
error: false
}
},
props: {
src: {
type: String
},
alt: {},
width: {},
height: {}
},
computed: {
style({ width, height }) {
return {
width: width,
height: height,
objectFit: "contain"
}
}
},
methods: {
onLoaded() {
this.loaded = true;
},
onError() {
this.error = true;
}
}
}

Watching for div width changes in vue

I have an nuxt app where I have two sidebars, one on the left and one on the right.
Both are fixed and body has padding from right and left.
In the middle I have <nuxt/> that loads pages.
Left sidebar can be minimized to 60px so I cannot use media queries for this and I need to watch for <nuxt/> width changes, in case that width is < 500px I would add some other classes. Something like media queries for element instead of viewport.
Is there a way to do this without additional javascript libraries, plugins etc..?
updated with two slider example:
A way to achieve the desired behavior will be to bind a dynamic width to your sliders and watch that width prop then bind the classes using the element ref upon changes :
Vue.config.devtools = false;
Vue.config.productionTip = false;
new Vue({
el: "#app",
data() {
return {
silderWidth: {
first: '100',
second: '100'
}
}
},
computed: {
first() {
return this.silderWidth.first
},
second() {
return this.silderWidth.second
}
},
methods: {
toggleWidth(ele) {
this.silderWidth[ele] === '100' ?
this.silderWidth[ele] = "200" :
this.silderWidth[ele] = "100"
}
},
watch: {
first() {
this.$nextTick(() => {
this.silderWidth.first === '200' ?
this.$refs.silderWidth1.classList.add('background') :
this.$refs.silderWidth1.classList.remove('background')
})
},
second() {
this.$nextTick(() => {
this.silderWidth.second === '200' ?
this.$refs.silderWidth2.classList.add('background') :
this.$refs.silderWidth2.classList.remove('background')
})
}
}
})
.silder {
background-color: red;
height: 200px;
}
.silder--1 {
position: fixed;
left: 0;
top: 0;
}
.silder--2 {
position: fixed;
right: 0;
top: 0;
}
.background {
background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class="silder silder--1" ref="silderWidth1" :style="{ width : first + 'px'}" #click="toggleWidth('first')"></div>
<div class="silder silder--2" ref="silderWidth2" :style="{ width : second + 'px'}" #click="toggleWidth('second')"></div>
</div>
Hope this helps:
window.onresize = () => {
let width = document.querySelector('nuxt').offsetWidth;
console.log(width);
};

Vue v-for: iterate one element individually in an array

I'm looking to loop through an array of span tags and add is-active to the next one in line, every 3 seconds. I have it working but after the first one, it adds all the rest. How do I just pull that class from the active one and add it to the next array item?
I've read through the official documentation several times and there doesn't seem to be any mention of iterating individual items, just listing them all or pushing an item onto the list.
I'm not sure if 'index' comes in to play here, and how to grab the index of the span element to add/subtract is-active. what am I doing wrong?
var firstComponent = Vue.component('spans-show', {
template: `
<h1>
<span class="unset">Make</span>
<br>
<span class="unset">Something</span>
<br>
<span v-for="(span, index) of spans" :class="{ 'is-active': span.isActive, 'red': span.isRed, 'first': span.isFirst }" :key="index">{{ index }}: {{ span.name }}</span>
</h1>
`,
data() {
return {
spans: [
{
name: 'Magical.',
isActive: true,
isRed: true,
isFirst: true
},
{
name: 'Inspiring.',
isActive: false,
isRed: true,
isFirst: true
},
{
name: 'Awesome.',
isActive: false,
isRed: true,
isFirst: true
}
]
};
},
methods: {
showMe: function() {
setInterval(() => {
// forEach
this.spans.forEach(el => {
if (el.isActive) {
el.isActive = false;
} else {
el.isActive = true;
}
});
}, 3000);
}
},
created() {
window.addEventListener('load', this.showMe);
},
destroyed() {
window.removeEventListener('load', this.showMe);
}
});
var secondComponent = Vue.component('span-show', {
template: `
<span v-show="isActive"><slot></slot></span>
`,
props: {
name: {
required: true
}
},
data() {
return {
isActive: false
};
}
});
new Vue({
el: "#app",
components: {
"first-component": firstComponent,
"second-component": secondComponent
}
});
.container {
position: relative;
overflow: hidden;
width: 100%;
}
.wrapper {
position: relative;
margin: 0 auto;
width: 100%;
padding: 0 40px;
}
h1 {
font-size: 48px;
line-height: 105%;
color: #4c2c72;
letter-spacing: 0.06em;
text-transform: uppercase;
font-family: archia-semibold, serif;
font-weight: 400;
margin: 0;
height: 230px;
}
span {
position: absolute;
clip: rect(0, 0, 300px, 0);
}
span.unset {
clip: unset;
}
span.red {
color: #e43f6f;
}
span.is-active {
clip: rect(0, 900px, 300px, -300px);
}
<div id="app">
<div class="container">
<div class="wrapper">
<spans-show>
<span-show></span-show>
</spans-show>
</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
To achieve desired result, I'd suggest to change the approach a bit.
Instead of changing value of isActive for individual items, we can create a variable (e.g. activeSpan, that will be responsible for current active span and increment it over time.
setInterval(() => {
// Increment next active span, or reset if it is the one
if (this.activeSpan === this.spans.length - 1) {
this.activeSpan = 0
} else {
this.activeSpan++
}
}, 3000);
In component's template, we make class is-active conditional and dependent on activeSpan variable:
:class="{ 'is-active': index === activeSpan, 'red': span.isRed, 'first': span.isFirst }"
If you still need to update values inside spans array, it can be done in more simple way, via map for example. Also included such case as optional in solution below.
Working example:
JSFiddle
Sidenote: there is no need to add window listeners for load event, as application itself is loaded after DOM is ready. Instead, method can be invoked inside created hook. It is included in solution above.

Categories

Resources