How to get VueJS transitioning Divs beside eachother? - vue.js

When using Vue transitions with a slide left/right animation, how can I get the Divs beside eachother?
Take a look at this pen;
https://codepen.io/anon/pen/jeBBaB
HTML
<div class="heading">
<h1>Transition demo</h1>
<h4>Why this no work?</h4>
</div>
<div class="container" id="app">
<transition :enter-active-class="enterAnimation" :leave-active-class="leaveAnimation" mode="">
<div key="one" v-if="currentStep == 1">
This is Step One
<button class="btn btn-primary" #click="currentStep = 2; previousStep=1">Next</button>
</div>
<div key="two" v-else>
This is Step Two
<button class="btn btn-primary" #click="currentStep = 1; previousStep=2">Back</button>
</div>
</transition>
</div>
CSS
$purple: #5c4084;
body {
background-color: $purple;
padding: 50px;
}
.container {
padding: 40px 80px 15px 80px;
background-color: #fff;
border-radius: 8px;
max-width: 800px;
}
.heading {
text-align: center;
h1 {
background: -webkit-linear-gradient(#fff, #999);
-webkit-text-fill-color: transparent;
-webkit-background-clip: text;
text-align: center;
margin: 0 0 5px 0;
font-weight: 900;
font-size: 4rem;
color: #fff;
}
h4 {
color: lighten(#5c3d86,30%);
text-align: center;
margin: 0 0 35px 0;
font-weight: 400;
font-size: 24px;
}
}
.btn{
outline: none !important;
}
.btn.btn-primary {
background-color: $purple;
border-color: $purple;
outline: none;
&:hover {
background-color: darken($purple, 10%);
border-color: darken($purple, 10%);
}
&:active, &:focus {
background-color: lighten($purple, 5%);
border-color: lighten($purple, 5%);
}
& .fa {
padding-right: 4px;
}
}
.form-group {
margin-bottom: 25px;
}
JS
new Vue({
el: '#app',
data: {
currentStep: 1,
previousStep: 0
},
computed:{
enterAnimation() {
if (this.currentStep < this.previousStep) {
return "animated slower fadeInLeft";
} else {
return "animated slower fadeInRight";
}
},
leaveAnimation() {
if (this.currentStep > this.previousStep) {
return "animated slower fadeOutLeft";
} else {
return "animated slower fadeOutRight";
}
}
}
});
When using no mode, the "entering" div appears on the line below the "leaving" div until then end, then it pops upwards.
I can use mode="out-in" but then there's a noticable gap between the Divs. I'd like to just have one sliding in, right next to the one sliding out. Any way to achieve this?

You could make use of absolute positioning on the div elements, you need to adjust your css a bit though.
But as a starting point, change your .container rules to this (adding position: relative;):
.container {
padding: 40px 80px 15px 80px;
background-color: #fff;
border-radius: 8px;
max-width: 800px;
position:relative;
}
and add this as a new rule below it:
.container div {position:absolute;top:0;left:0;}
The flexbox way:
Change your container rules to this:
.container {
padding: 40px 80px 15px 80px;
background-color: #fff;
border-radius: 8px;
max-width: 800px;
display:flex;
}
After this you can use a css translate rule to position the content. You can see a working example here:
Vue transitions

Related

Animate width of div bound to data property in Vue

I have this progress bar div, whichs width is bound to the data property result and changes accordingly. At the moment it still jumps, but I want to animate it. I thought of tracking the old and the new Value and injecting it in the css with css variables or just using a setInterval method, but tracking the 2 values seems to get quite complicated and it seemed like a overkill for me. Does anyone have an easier idea?
<template>
<div class="progress">
<div class="progress-value" :style="{ 'width': result + '%' }">
<h2>{{ result }}%</h2>
</div>
</div>
</template>
<script>
export default {
props: ["result"],
};
</script>
<style scoped>
.progress {
background: #ccc;
border-radius: 100px;
position: relative;
padding: 5px 5px;
margin: 5px 5px;
height: 40px;
width: auto;
}
.progress-value {
animation: load 3s normal forwards;
border-radius: 100px;
background: #fff;
height: 30px;
text-align: center;
}
/* #keyframes load {
0% {
width:
}
100% {
width:
}
} */
</style>
Add css transition like this:
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 1s;
And fix the binding:
<div class="progress-value" :style="'width: ' + result + '%'">
See this example
<template>
<div class="progress">
<div class="progress-value" :style="'width: ' + result + '%'">
<h2>{{ result }}%</h2>
</div>
</div>
</template>
<script>
export default {
data () {
return {
result: 5
}
},
mounted() {
this.increment()
},
methods: {
increment() {
this.result += 10
if (this.result < 95) {
setTimeout(this.increment, 1000)
}
}
}
}
</script>
<style scoped>
.progress {
background: #ccc;
border-radius: 100px;
position: relative;
padding: 5px 5px;
margin: 5px 5px;
height: 40px;
width: auto;
}
.progress-value {
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 1s;
background: #fff;
height: 30px;
text-align: center;
}
</style>

With Swiper.js, navigation via bullets sometimes doesn't go to correct slide

I'm using swiper.js in my project and sometimes (maybe 1 out of 10 times), navigation by clicking the associated bullet does not work - it instead goes to the previous slide. My swiper config object does contain clickable: true for the pagination.
Check this code. I think someone had the same problem here
HTML
<h1>Swipe 2</h1>
<div id='slider' style='max-width:500px;margin:0 auto' class='swipe'>
<div class='swipe-wrap'>
<div><b>1</b></div>
<div><b>2</b></div>
<div><b>3</b></div>
</div>
</div>
<div class="counter content" style='text-align:center;padding-top:20px;'>
<ul id='position'>
<li class="on"></li>
<li ></li>
<li ></li>
</ul>
</div>
<div style='text-align:center;padding-top:20px;'>
<button onclick='slider.prev()'>prev</button>
<button onclick='slider.next()'>next</button>
</div>
<script src='swipe.js'></script>
CSS
html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, del, dfn, em, img, ins, kbd, q, samp, small, strong, b, i, dl, dt, dd, ol, ul, li, fieldset, form, label, table, tbody, tfoot, thead, tr, th, td, article, aside, footer, header, nav, section {
margin:0;
padding:0;
border:0;
outline:0;
font-size:100%;
vertical-align:baseline;
background:transparent;
}
body {
-webkit-text-size-adjust:none;
font-family:sans-serif;
min-height:416px;
}
h1 {
font-size:33px;
margin:50px 0 15px;
text-align:center;
color:#212121;
}
h2 {
font-size:14px;
font-weight:bold;
color:#3c3c3c;
margin:20px 10px 10px;
}
small {
margin:0 10px 30px;
display:block;
font-size:12px;
}
a {
margin:0 0 0 10px;
font-size:12px;
color:#3c3c3c;
}
html, body {
background: #f3f3f3;
}
#console {
font-size: 12px;
font-family:"Inconsolata", "Monaco", "Consolas", "Andale Mono", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace;
color: #999;
line-height: 18px;
margin-top: 20px;
max-height: 150px;
overflow: auto;
}
#slider div b {
display:block;
font-weight:bold;
color:#14ADE5;
font-size:20px;
text-align:center;
margin:10px;
padding:100px 10px;
box-shadow: 0 1px #EBEBEB;
background: #fff;
border-radius: 3px;
border: 1px solid;
border-color: #E5E5E5 #D3D3D3 #B9C1C6;
}
.swipe {
overflow: hidden;
visibility: hidden;
position: relative;
}
.swipe-wrap {
overflow: hidden;
position: relative;
}
.swipe-wrap > div {
float:left;
width:100%;
position: relative;
}
.counter {
height: 55px;
ul {
text-align: center;
li {
// padding: 5px;
display: inline-block;
height: 10px;
width: 10px;
border: solid 2px #404041;
border-radius: 10px;
margin: 2.5px;
&.on {
background: #404041;
}
}
}
}
JAVASCRIPT
var elem = document.getElementById('slider');
var slider =
Swipe(document.getElementById('slider'), {
auto: 10000,
continuous: true,
callback: function(pos) {
var i = bullets.length;
while (i--) {
bullets[i].className = ' ';
}
bullets[pos].className = 'on';
}
});
var bullets = document.getElementById('position').getElementsByTagName('li');
$('li').on('click', function(event){
event.preventDefault();
var index = $("li").index(event.currentTarget);
slider.slide(index);
});
// with jQuery
// window.mySwipe = $('#mySwipe').Swipe().data('Swipe');

which files are necessary for bootstrap dropdown to work?

I am avoiding including the entire bootstrap source code in my app. All I need at this point is the bootstrap dropdown classes. I am trying to make a simple dropdown that looks like this...
<div class="dropdown">
<a data-toggle="dropdown" class="dropdown-toggle">display dropdown</a>
<ul
class="dropdown-menu dropdown-menu-right dropdown-menu-arrow dropdown-scrollable dropdown-content"
>
<li class="dropdown-item">Option 1</li>
<li class="dropdown-item">Option 2</li>
</ul>
</div>
I copied dropdown.scss from the tabler.io library.
This is all it contains...
.dropdown {
display: inline-block;
color: orange;
}
.dropdown-menu {
box-shadow: $dropdown-box-shadow;
min-width: 12rem;
}
.dropdown-item {
color: $text-muted-dark;
z-index: 1000;
}
.dropdown-menu-arrow {
&:before {
position: absolute;
top: -6px;
left: 12px;
display: inline-block;
border-right: 5px solid transparent;
border-bottom: 5px solid $border-color;
border-left: 5px solid transparent;
border-bottom-color: rgba(0, 0, 0, 0.2);
content: "";
}
&:after {
position: absolute;
top: -5px;
left: 12px;
display: inline-block;
border-right: 5px solid transparent;
border-bottom: 5px solid #fff;
border-left: 5px solid transparent;
content: "";
}
&.dropdown-menu-right {
&:before,
&:after {
left: auto;
right: 12px;
}
}
}
.dropdown-toggle {
user-select: none;
cursor: pointer;
&:after {
vertical-align: 0.155em;
}
&:empty:after {
margin-left: 0;
}
}
.dropdown-icon {
color: $text-muted;
margin-right: 0.5rem;
margin-left: -0.5rem;
width: 1em;
display: inline-block;
text-align: center;
vertical-align: -1px;
}
I know that my code is referencing this stylesheet because the font color is orange and the stylesheet's first rule includes color: orange.
I also know that my code detects the user clicking on the text because I tested by adding #click.prevent="doSomething()" where doSomething() simply console logged a message.The message did indeed print out in the Chrome dev tools console.
However, when I click on the words display dropdown, the dropdown menu does not open.
I understand there are javascript files I may need. Which files are those and how can I make sure my code uses this file? This is a Vuejs app using Nuxt.

Button doesn't call the function for the first click

I have a checkbox and a button that when it's checked, then, the button suppose to submit, but when it's unchecked, the button is disable.
However, my button keep disabling and I can't submit, the reason because I add: v-on:click.prevent="checkboxJsInput"
The reason I use "prevent" is because I have disable and enable button, and "prevent" is to disable the button from submit when it's disable.
If I don't use "prevent", the button would submit even it's disable and the strange thing it's enable for the first time, but after you "check" for the second and so on, it's working.
My question, how to make the button works?
so when the button is grey, it's supposed to disable, when it's blue, it's supposed to enable to submit.
Is there a way to do this in vue way rather than using native javascript in vue?
checkboxJsInput: function() {
const checkBox = document.querySelector(".guestlistCheckboxJs");
const buttonFormJs = document.querySelector(".buttonCheckboxJs");
if (checkBox != null) {
checkBox.addEventListener("click", function() {
if (this.checked) {
buttonFormJs.disabled = false;
buttonFormJs.classList.remove("disabled");
} else {
buttonFormJs.disabled = true;
buttonFormJs.classList.add("disabled");
}
});
}
}
}
.buttonCheckboxJs.disabled {
background-color: #d6d6d6;
}
.guestlist-form-wrapper-checkbox {
display: flex;
margin: 20px 0 30px 0;
}
.guestlist-text-checkbox {
color: #ffffff;
margin: 5px 10px;
}
.guestlist-input-checkbox[type="checkbox"] {
display: none;
width: 45px;
height: 45px;
background: #d6d6d6;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05),
inset 0 -15px 10px -12px rgba(0, 0, 0, 0.05);
}
.guestlist-span-checkbox {
visibility: visible;
content: "";
display: block;
width: 25px;
height: 25px;
font-size: 20px;
text-align: center;
color: #ffffff;
background-color: #4990e2;
display: flex;
justify-content: center;
align-items: center;
border-radius: 3px;
}
.guestlist-input-checkbox[type="checkbox"]:checked
+ label
.guestlist-span-checkbox::before {
content: "✔";
}
.guestlist-form-wrapper-textarea {
width: 100%;
margin-top: 25px;
}
.guestlist-textarea {
background-color: transparent;
text-indent: 5px;
height: 50px;
width: 100%;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
color: #ffffff;
}
.button-style {
background-color: #4990e2;
border: 0;
color: #ffffff;
font-weight: bold;
font-size: 18px;
padding: 15px 20px;
border-radius: 5px;
width: 600px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div class="guestlist-form-wrapper-checkbox">
<input
class="guestlist-input-checkbox guestlistCheckboxJs"
v-on:click="checkboxJsInput"
type="checkbox"
value="1"
name="OptIn"
id="OptIn"
>
<label class="guestlist-label-checkbox" for="OptIn">
<span class="guestlist-span-checkbox"></span>
</label>
</div>
<button
v-on:click.prevent="checkboxJsInput"
class="button-style buttonCheckboxJs disabled"
>Check Availability</button>
First of all I don't understand why you are using an event listner inside an event listner for that checkbox click.
Secondly, you can use v-model.
In your data, add property check like this:
data() {
return {
check: false
}
}
Then in your html use like this:
<div class="guestlist-form-wrapper-checkbox">
<input
class="guestlist-input-checkbox guestlistCheckboxJs"
v-model="check"
type="checkbox"
value="1"
name="OptIn"
id="OptIn"
>
<label class="guestlist-label-checkbox" for="OptIn">
<span class="guestlist-span-checkbox"></span>
</label>
</div>
<button
:disabled="check"
class="button-style buttonCheckboxJs disabled"
>Check Availability</button>
I just use a check property which is a boolean. Then you can run your function on click of that button.
Consider using v-model for this.
https://v2.vuejs.org/v2/guide/forms.html#v-model-with-Components
Your checkbox can have its v-model bound to a data property, and the disabled property on the button can be bound to the same. It'd look like this:
<template>
<input type="checkbox" v-model="enableSubmit" />
<button #click="mySubmitFunction()" :disabled="!enableSubmit">
</template>
<script>
export default {
data: function() {
return {
enableSubmit: false
}
},
methods: {
mySubmitFunction() {
//...
}
}
}
</script>

CSS TranslationX of container not working in Safari

Today I have noticed a weird behavior of Safari (9.0) when I applied a transition to an element that was translating on the X axis while the width was also increasing.
I have reproduced the behavior in this JsFiddle. Here is an embed code for those who like it better. In Firefox and Chrome it looks pretty smooth but not in Safari, does anyone have a solution or a best way to achieve the same effect?
var button = document.getElementsByTagName('button')[0],
container = document.getElementsByTagName('div')[0];
button.addEventListener('click', function() { container.classList.toggle('open'); });
.container {
width: 100%;
overflow: hidden;
}
ul {
display: block;
width: 100%;
padding: 0;
margin: 0;
transition: width 1s, transform 1s;
}
.open ul {
width: 200%;
transform: translateX(-50%);
}
li {
/* Just some style first */
font-family: sans-serif;
color: #fff;
text-align: center;
text-transform: uppercase;
background-color: red;
padding: 1em 0;
display: inline-block;
width: calc(50% - 4px);
}
li:first-child {
background-color: green;
}
<div class="container">
<ul>
<li>Test</li>
<li>Test</li>
</ul>
</div>
<button type="button">Toggle translation</button>
Re-posting as an answer.
Here is the jsFiddle result and snippet as below:
var button = document.getElementsByTagName('button')[0];
var container = document.getElementsByTagName('div')[0];
var timeline = new TimelineMax({ paused: true });
timeline.to('ul', 1, { width: '200%', xPercent: -50, ease: Power2.easeInOut });
button.addEventListener('click', function() {
timeline.progress() > 0 ? timeline.reverse() : timeline.play();
});
.container {
width: 100%;
overflow: hidden;
}
ul {
display: block;
width: 100%;
padding: 0;
margin: 0;
}
li {
font-family: sans-serif;
color: #fff;
text-align: center;
text-transform: uppercase;
background-color: red;
display: inline-block;
padding: 1em 0;
width: calc(50% - 4px);
}
li:first-child {
background-color: green;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/gsap/1.18.2/TweenMax.min.js"></script>
<div class="container">
<ul>
<li>Test</li>
<li>Test</li>
</ul>
</div>
<button type="button">Toggle translation</button>
Hope this is helpful.
P.S. I have been using GSAP for quite a while now and I don't remember getting stuck on any browser-specific issues unless a browser would do something differently. A little research into GSAP and it would tell you that browser compatibility is one of their main selling points.
By animating margin-left instead of translateX the result is acceptable in Safari:
var button = document.getElementsByTagName('button')[0],
container = document.getElementsByTagName('div')[0];
button.addEventListener('click', function() {
container.classList.toggle('open');
});
.container {
width: 100%;
overflow: hidden;
}
ul {
display: block;
width: 100%;
padding: 0;
margin: 0;
transition: width 1s, margin-left 1s;
}
.open ul {
width: 200%;
margin-left:-100%;
}
li {
font-family: sans-serif;
color: #fff;
text-align: center;
text-transform: uppercase;
background-color: red;
display: inline-block;
padding: 1em 0;
width: calc(50% - 4px);
}
li:first-child {
background-color: green;
}
<div class="container">
<ul>
<li>Test</li>
<li>Test</li>
</ul>
</div>
<button type="button">Toggle translation</button>
Using scaleX instead of animating width is smoother, but probably not what you want.
var button = document.getElementsByTagName('button')[0],
container = document.getElementsByTagName('div')[0];
button.addEventListener('click', function() {
container.classList.toggle('open');
});
.container {
width: 100%;
overflow: hidden;
}
ul {
display: block;
width: 100%;
padding: 0;
margin: 0;
transition: transform 1s;
}
.open ul {
transform: translateX(-50%) scaleX(2);
}
li {
font-family: sans-serif;
color: #fff;
text-align: center;
text-transform: uppercase;
background-color: red;
display: inline-block;
padding: 1em 0;
width: calc(50% - 4px);
}
li:first-child {
background-color: green;
}
<div class="container">
<ul>
<li>Test</li>
<li>Test</li>
</ul>
</div>
<button type="button">Toggle translation</button>
So, I will try to sum up the two best solutions here : one with CSS transform and the other with Javascript animation (GSAP).
CSS TRANSFORM
In terms of performance, it is recommended to only animate transforms (translate, scale, rotate) and opacity. If you are interested in more optimisation details you can have a look at this article by Anna Migas.
So, as #Meiko suggested, the best solution is to only animate scale and translate properties. Here is a code sample (and the JSFiddle)
var button = document.getElementsByTagName('button')[0],
container = document.getElementsByTagName('div')[0];
button.addEventListener('click', function() {
container.classList.toggle('open');
})
.container,
ul {
width: 100%;
}
ul {
overflow: hidden;
/* reset default browser styles */
padding: 0;
margin: 0;
}
li {
display: inline-block;
width: calc(50% - 2px);
position: relative;
transition: transform 1s;
/* Just some style */
font-family: sans-serif;
color: #fff;
text-align: center;
text-transform: uppercase;
padding: 1em 0;
}
li::before {
content: '';
position: absolute;
top: 0;
left: 0;
display: block;
width: 100%;
height: 100%;
background-color: red;
z-index: -1;
transition: transform 1s;
}
li:first-child::before {
background-color: green;
}
.open li:first-child {
transform: translateX(-100%);
}
.open li:nth-of-type(2) {
transform: translateX(-50%);
}
.open li:nth-of-type(2)::before {
transform: scaleX(2);
}
<div class="container">
<ul>
<li>Test</li>
<li>Test</li>
</ul>
</div>
<button type="button">Toggle translation</button>
PROS:
Only use a tiny bit of Javascript to toggle class,
The browser support is quite good (needs vendor-specific properties and some testing),
Really fast and light on GPU memory.
CONS:
Pretty limited in terms of usage (the actual width of the second cell stays the same),
Needs more lines of CSS.
JS ANIMATION (WITH GSAP)
This solution has been suggested by #Tahir Ahmed and use the GSAP library. As a side note, I really think that this is the best js library out there for this kind of animation. Here is a snippet of how it works (and the JSFiddle):
var button = document.getElementsByTagName('button')[0],
timeline = new TimelineMax({ paused: true });
timeline.to('ul', 1, { width: '200%', xPercent: -50 });
button.addEventListener('click', function() {
timeline.progress() > 0 ? timeline.reverse() : timeline.play();
})
.container {
width: 100%;
overflow: hidden;
}
ul {
width: 100%;
/* reset default browser styles */
padding: 0;
margin: 0;
}
li {
display: inline-block;
width: calc(50% - 2px);
background-color: red;
/* Just some style */
font-family: sans-serif;
color: #fff;
text-align: center;
text-transform: uppercase;
padding: 1em 0;
}
li:first-child {
background-color: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.18.2/TweenMax.min.js"></script>
<div class="container">
<ul>
<li>Test</li>
<li>Test</li>
</ul>
</div>
<button type="button">Toggle translation</button>
PROS:
Really flexible, sky is the limit!
You can animate properties such as display (you can't in CSS),
Compatible with every browser out there (down to IE6).
CONS:
Require a third party library (about 30kb),
Seems a bit harder for the GPU (although it needs more testing to be sure).
In the end it really depends on the animation you need but if it get's a little bit more complex than moving a container around then I will choose GSAP.