How to convert the following JS code to VueJS - javascript

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

Related

Is this the right way to import gsap in vue.js (it works but is it the "right" way?)

I'm quite new to Vue.js and have had some problems getting libraries to work without getting the "error 'X' is not defined no-undef" message.
In this case it is 'Back' that is not defined (which is a part of GSAP)
I figured the only place to "define" Back would be in the import.
Is this just the way to import libraries?
Do I have to write every undefined part in the import like this?
It works but it just seems unnecessary.
<template>
<div id="mainTemplate">
<h2>This is the MainTemplaye.vue Component</h2>
<div ref="box" class="box"></div>
</div>
</template>
<script>
import { TimelineLite, Back } from "gsap";
export default {
name: "MainTemplate",
mounted() {
const { box } = this.$refs;
const timeline = new TimelineLite();
timeline.to(box, 1, { x: 200, rotation: 90, ease: Back.easeInOut, })
timeline.to(box, 0.5, { background: 'green' },'-=0.5')
},
};
</script>
<style>
.box {
height: 60px;
width: 60px;
background: red;
}
</style>
I'm not sure where you're learning from, but you're using the old syntax of GSAP. If you use the new syntax of GSAP you don't have to import anything other than gsap in your case:
import { gsap } from "gsap";
export default {
name: "MainTemplate",
mounted() {
const { box } = this.$refs;
const timeline = gsap.timeline();
timeline.to(box, { duration: 1, x: 200, rotation: 90, ease: 'back.inOut' })
timeline.to(box, { background: 'green' }, '-=0.5')
},
};
The best place to start learning is the official GSAP Getting Started article.

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: whitespace when carousel is sliding

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

DOM element becomes Null in Vue project

I'm working on a code typer, but for some reason the element becomes Null with no reason I can make out. I am new to Vue & know this code works as I've previously completed this at https://CodeSpent.dev (live preview) in Django/Python until I determined it'd be valuable to learn more front end frameworks.
So I believe it has something to do with how Vue handles rendering, but I'm only a few hours into learning & have no idea where to even look with this.
Here is the code:
var codeBlock = document.getElementById('code')
console.log(codeBlock)
setTimeout(() => {
new TypeIt(codeBlock, {
strings: [codeSample],
speed: 20
}).go();
}, 1000)
setInterval(function () {
const code = Prism.highlight(codeBlock.innerText, Prism.languages.python, 'python');
document.getElementById('real-code').innerHTML = code;
}, 10);
If we look at console we can see on line 23 where codeBlock is clearly not null, but then when we try to use it it becomes null. Anything stand out?
Full Component:
<template>
<div id="code-block" class="bb">
<pre class="code-pre">
<code id="real-code"></code>
</pre>
<div id="code" class="language-py"></div>
</div>
</template>
<script>
import 'prismjs'
import 'prismjs/themes/prism.css'
import TypeIt from 'typeit';
export default {
name: 'CodeTyper'
}
var codeSample = '\x0a\x3E\x3E\x20\x6E\x61\x6E\x6F\x20\x63\x6F\x64\x65\x73\x70\x65\x6E\x74\x2E\x70\x79\x0A\x66\x72\x6F\x6D\x20\x70\x79\x74\x68\x6F\x6E\x20\x69\x6D\x70\x6F\x72\x74\x20\x44\x65\x76\x65\x6C\x6F\x70\x65\x72\x0A\x66\x72\x6F\x6D\x20\x70\x6F\x72\x74\x66\x6F\x6C\x69\x6F\x2E\x6D\x6F\x64\x65\x6C\x73\x20\x69\x6D\x70\x6F\x72\x74\x20\x50\x6F\x72\x74\x66\x6F\x6C\x69\x6F\x0A\x0A\x63\x6C\x61\x73\x73\x20\x43\x6F\x64\x65\x53\x70\x65\x6E\x74\x28\x44\x65\x76\x65\x6C\x6F\x70\x65\x72\x29\x3A\x0A\x20\x20\x20\x20\x6E\x61\x6D\x65\x20\x3D\x20\x27\x50\x61\x74\x72\x69\x63\x6B\x20\x48\x61\x6E\x66\x6F\x72\x64\x27\x0A\x20\x20\x20\x20\x6C\x6F\x63\x61\x74\x69\x6F\x6E\x20\x20\x3D\x20\x27\x50\x69\x74\x74\x73\x62\x75\x72\x67\x68\x2C\x20\x50\x41\x2C\x20\x55\x53\x27\x0A\x20\x20\x20\x20\x6C\x61\x6E\x67\x75\x61\x67\x65\x73\x20\x3D\x20\x5B\x27\x70\x79\x74\x68\x6F\x6E\x27\x2C\x20\x27\x6A\x61\x76\x61\x73\x63\x72\x69\x70\x74\x27\x2C\x20\x27\x63\x73\x73\x27\x2C\x27\x68\x74\x6D\x6C\x35\x27\x5D\x0A\x20\x20\x20\x20\x66\x61\x76\x6F\x72\x69\x74\x65\x73\x20\x3D\x20\x5B\x27\x64\x6A\x61\x6E\x67\x6F\x27\x2C\x20\x27\x74\x65\x6E\x73\x6F\x72\x66\x6C\x6F\x77\x27\x2C\x20\x27\x74\x77\x69\x74\x63\x68\x27\x2C\x20\x27\x64\x69\x73\x63\x6F\x72\x64\x27\x2C\x20\x27\x6F\x70\x65\x6E\x63\x76\x27\x5D\x0A\x0A\x20\x20\x20\x20\x64\x65\x66\x20\x5F\x5F\x73\x74\x72\x5F\x5F\x28\x73\x65\x6C\x66\x29\x3A\x0A\x20\x20\x20\x20\x20\x20\x72\x65\x74\x75\x72\x6E\x20\x73\x65\x6C\x66\x2E\x6E\x61\x6D\x65'
var codeBlock = document.getElementById('code')
console.log(codeBlock)
setTimeout(() => {
new TypeIt(codeBlock, {
strings: [codeSample],
speed: 20
}).go();
}, 1000)
setInterval(function () {
const code = Prism.highlight(codeBlock.innerText, Prism.languages.python, 'python');
document.getElementById('real-code').innerHTML = code;
}, 10);
</script>
<style>
#real-code {
color: #5c5edc;
}
#code-block {
background-color: #141D22;
color: #fff;
flex: 1;
height: 355px;
}
#code-block-sub {
background-color: rgb(34, 32, 35);
color: #fff;
width: 100%;
padding: 0 15px;
height: 150px;
}
#code,
#code-sub {
padding: 0px !important;
margin: 0px !important;
display: none;
color: #fff !important;
}
</style>
First a template that presents the partial string...
<template>
<div>
<pre>{{partialCode}}</pre>
<v-btn #click="startAppending()"></v-btn>
</div>
</template>
Then the partialCode string bound into data...
export default {
data () {
return {
partialCode: '',
// other data
}
},
You may want to start appending onCreate or some other lifecycle hook (or once you receive the code data asynchronously), but the key to the logic is that you can now just change the state of partialCode and let the DOM update itself...
methods: {
startAppending() {
this.partialCode = '' // start fresh each time
const code = Prism.highlight(codeBlock.innerText, Prism.languages.python, 'python')
let index = 0
let interval = setInterval(() => {
if (this.partialCode.length === code.length) {
clearInterval(interval)
} else {
this.partialCode = code.slice(0, index++)
}
}, 200);
},
// the other methods
}

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