Bootstrap 4 Multi Carousel show 4 images instead of 3 - carousel

I can't figure out how to make it show 4 images instead of 3.
Based on this Code: Bootstrap 4 Multiple Item Carousel
Script JS
$('#recipeCarousel').carousel({
interval: 10000
})
$('.carousel .carousel-item').each(function(){
var next = $(this).next();
if (!next.length) {
next = $(this).siblings(':first');
}
next.children(':first-child').clone().appendTo($(this));
if (next.next().length>0) {
next.next().children(':first-child').clone().appendTo($(this));
}
else {
$(this).siblings(':first').children(':first-child').clone().appendTo($(this));
}
});
CSS
.carousel-inner .carousel-item-right.active,
.carousel-inner .carousel-item-next {
transform: translateX(25%);
}
.carousel-inner .carousel-item-left.active,
.carousel-inner .carousel-item-prev {
transform: translateX(-25%)
}
.carousel-inner .carousel-item-right,
.carousel-inner .carousel-item-left{
transform: translateX(0);
}
and I've changed to this code
<div class="carousel-item col-md-3">
instead of this original code
<div class="carousel-item col-md-4">

Since 4 cols, are 25% width (instead of 33%) change the CSS:
.carousel-inner .carousel-item-right.active,
.carousel-inner .carousel-item-next {
transform: translateX(25%);
}
.carousel-inner .carousel-item-left.active,
.carousel-inner .carousel-item-prev {
transform: translateX(-25%)
}
Also an additional item needs to be cloned into each slide. So the JS change is:
$('.carousel .carousel-item').each(function(){
var next = $(this).next();
if (!next.length) {
next = $(this).siblings(':first');
}
next.children(':first-child').clone().appendTo($(this));
for (var i=0;i<2;i++) {
next=next.next();
if (!next.length) {
next = $(this).siblings(':first');
}
next.children(':first-child').clone().appendTo($(this));
}
});
Bootstrap 4.0.0 Demo - 4 slides, advance 1
Note: From alpha 6 (when the original question was asked) to Bootstrap 4.0.0, the active carousel-item changed to flexbox. Therefore, the same must be done for the neighboring items in the multi-carousel.
.carousel-inner .carousel-item.active,
.carousel-inner .carousel-item-next,
.carousel-inner .carousel-item-prev {
display: flex;
}
Also see: Bootstrap 4.1.0 (advance all 4 at once)

Related

how to delay elements entering the DOM using TransitionGroup?

The problem I am running into is that all the elements are rendered first before the animation starts. The effect that I want to achieve is that the element is animated while entering the DOM and it happens one after another. What's wrong with my code?
<template>
<TransitionGroup appear name="stagger" tag="ul">
<li v-for="(num, index) in list" :style="{ '--order': index }" :key="num">
{{ num }}
</li>
</TransitionGroup>
</template>
<script setup>
const list = Array.from({ length: 5 }, (_, index) => index + 1);
</script>
<style scoped>
.stagger-enter-active {
animation-name: stagger;
animation-duration: 0.5s;
animation-delay: calc(var(--order) * 0.15s);
}
#keyframes stagger {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
After reading the official doc one more time, I realized that the same animation effect can be made with just transition and it works, which got me wondering what's the main difference between these two approaches?
.stagger-enter-from {
opacity: 0;
transform: translateX(-1em);
}
.stagger-enter-active {
transition: opacity 0.5s linear, transform 0.5s ease;
transition-delay: calc(var(--order) * 0.15s);
}

How can I animate the changed element with vuejs?

Using Vuejs the data of some elements on the page is changing.
However, this change is not understood by the user.
For example, I am making a counter by clicking a button. I am printing data as {{counter}} to span element.
But this change is not noticed by the user. How can I give it various animations?
I tried to combine a css that I found have an animation I wanted, but was unsuccessful.
The Vuejs documentation says you can do it with toggleCss, but that's not what I want.
var app=new Vue({
el: "#app",
data: {
myCount:0
}
,
methods: {
btnCount() {
this.myCount+=1
}
}
})
<div id='app'>
<button #click='btnCount'>
+++++
</button>
<span class='myAnimSpan'>{{myCount}}</span>
</div>
span:hover {
animation: shake 0.5s;
}
#keyframes shake {
0% { transform: translate(1px, 1px) rotate(0deg); }
10% { transform: translate(-1px, -2px) rotate(-1deg); }
20% { transform: translate(-3px, 0px) rotate(1deg); }
30% { transform: translate(3px, 2px) rotate(0deg); }
40% { transform: translate(1px, -1px) rotate(1deg); }
50% { transform: translate(-1px, 2px) rotate(-1deg); }
60% { transform: translate(-3px, 1px) rotate(0deg); }
70% { transform: translate(3px, 1px) rotate(-1deg); }
80% { transform: translate(-1px, -1px) rotate(1deg); }
90% { transform: translate(1px, 2px) rotate(0deg); }
100% { transform: translate(1px, -2px) rotate(-1deg); }
}
Jsfiddle : https://jsfiddle.net/au4x031g/
You can use Vue transitions (see https://v3.vuejs.org/guide/transitions-enterleave.html#transitioning-single-elements-components).
Using a <transition> tag with name, and the element with key (each change to that key will trigger a transition update)
<transition name="shaketext">
<span class='myAnimSpan' :key="myCount">{{myCount}}</span>
</transition>
The name property is used for css declarations for enter/leave events, for example:
.shaketext-enter-active {
animation: shake 0.9s;
}
.shaketext-leave-to, .shaketext-leave-active{
display: none;
}
Also, to make transforms work, the span element should be a block element (display:inline-block for example)
.myAnimSpan {
display: inline-block;
}
https://jsfiddle.net/udctfs49/1/

How to display a component when page is loading in nuxt

I am quite new to nuxt, and I need help here.
async asyncData({ params, route }) {
const { data } = await axios.get(
`${process.env.baseUrl}/homes/?search=${
params.search
}&home_status=${1}`
)
return {
homes: data.results,
}
}
I am trying to populate my component with data(using asyncData), but I want my skeleton loader to show if my page is loading. How do I do that in nuxt?
Here is the code for my skeleton loader;
<template>
<div class="placeholder-container">
<div class="placeholder wave">
<div class="square"></div>
<div class="line"></div>
<div class="line"></div>
<div class="line"></div>
</div>
</div>
</template>
<style scoped>
.placeholder-container {
width: 35rem;
margin: 15px auto 15px auto;
}
.placeholder {
padding: 10px;
width: 100%;
// border: 1px solid lightgrey;
display: flex;
flex-direction: column;
}
.placeholder div {
background: #e8e8e8;
}
.placeholder .square {
width: 100%;
height: 22rem;
border-radius: 1rem;
margin: 0 0 10px;
}
.placeholder .line {
height: 12px;
margin: 0 0 10px 0;
}
.placeholder .line:nth-child(2) {
width: 120px;
}
.placeholder .line:nth-child(3) {
width: 180px;
}
.placeholder .line:nth-child(4) {
width: 150px;
}
.placeholder.wave div {
animation: wave 1s infinite linear forwards;
-webkit-animation: wave 1s infinite linear forwards;
background: #f6f7f8;
background: linear-gradient(to right, #eeeeee 8%, #dddddd 18%, #eeeeee 33%);
background-size: 800px 104px;
}
#keyframes wave {
0% {
background-position: -468px 0;
}
100% {
background-position: 468px 0;
}
}
#-webkit-keyframes wave {
0% {
background-position: -468px 0;
}
100% {
background-position: 468px 0;
}
}
</style>
What I normally do without using nuxt, is to create a data variable(loading=true), and change it to false after I finish making the api call, but since asyncData runs in the server, how do I make that work? I will also appreciate it if there is a better way of doing something like this
Placeholder
To display a placeholder component on a particular page
during loading, switch from asyncData to the fetch hook, which exposes the $fetchState.pending flag that is set to true when complete:
<template>
<div>
<MyLoading v-if="$fetchState.pending" />
<MyContent v-else :posts="posts" />
</div>
</template>
<script>
export default {
data() {
return {
posts: []
}
},
async fetch() {
const { data } = await this.$axios.get(...)
this.posts = data
}
}
</script>
Customizing loading progress bar
Nuxt provides a default loading progress bar that appears at the top of the app while a page is loading. You could customize the progress bar's appearance:
// nuxt.config.js
export default {
loading: {
color: 'blue',
height: '5px'
}
}
Or you could specify your own custom loading component instead:
// nuxt.config.js
export default {
loading: '~/components/MyLoading.vue'
}
demo

Vuelidate - shaking the incorrect input field

Take a look at Basic form at Vuelidate documentation. If the rule is broken (text is too short), the label is red, then eror message appears and the input field shakes for a while. How is this done? I have copied the source code sample and the shaking effect is not there. I cannot even simulate it with an official fiddle: https://jsfiddle.net/so89zmpe/2/
<div class="form-group" :class="{ 'form-group--error': $v.name.$error }">
<label class="form__label">Name</label>
<input class="form__input" v-model.trim="$v.name.$model"/>
</div>
I cannot find anything relevant in Chrome developer
If you open DevTools > Animations tab, you can see that there's an animation name of shakeError applied on .form-group.form-group--error:
Here's the definition of shakeError:
#keyframes shakeError {
0% {
transform: translateX(0); }
15% {
transform: translateX(0.375rem); }
30% {
transform: translateX(-0.375rem); }
45% {
transform: translateX(0.375rem); }
60% {
transform: translateX(-0.375rem); }
75% {
transform: translateX(0.375rem); }
90% {
transform: translateX(-0.375rem); }
100% {
transform: translateX(0); } }
And then,
.form-group--alert,
.form-group--error {
animation-name: shakeError;
animation-fill-mode: forwards;
animation-duration: .6s;
animation-timing-function: ease-in-out; }
You can check the Sources tab of https://vuelidate.js.org/#sub-basic-form for a docs.scss file to dig in deeper.

Vue JS code is not updating inside a loop

I'm stuck with a problem for a couple of days now and I've no idea what might be the solution. I've tried a couple, nothing worked.
I'm trying to enlarge a span text to fit the parent container width.
I've tried this.$forceUpdate();, didn't work.
I've also tried to pause the loop, but I found out later that that's not really possible in JS.
<template>
<span
ref="textRowRef"
v-bind:style="{fontSize: fontSize + 'px'}" >
{{ textRow }}</span>
</template>
// this is the VueJS code
var textRow = this.$refs.textRowRef;
var parentRow = textRow.parentElement;
for(var i = 0; i < 10; i++) {
this.fontSize += 10;
console.log(`Text Row in loop: ${textRow.clientWidth}`);
textRow = this.$refs.textRowRef;
}
console.log(`Text Row: ${textRow.clientWidth}`);
console.log(`Parent Row: ${parentRow.clientWidth}`);
Results in the console:
10 Text Row in loop: 48
Text Row: 48
Parent Row: 378
Here's my attempt.
I set fontSize to 72px then gradually reduce it by 1px until the text fits within the box. For such a simple example the performance is fine but I suspect that in the context of a larger page reducing it by 1px each time might prove too slow.
Each time the fontSize changes it triggers a re-render. The updated hook then measures the text to decide whether it needs to be shrunk even further.
If the text changes it resets the size to 72px and starts the process over again. I've not made any attempt to track the size of the parent element but it would also need to perform a reset if the width of that element changed.
I've added whitespace: nowrap to the <span> to ensure that its width will overflow the parent element. Otherwise the text would just wrap when the width reaches the edge of the parent element.
const MAX_FONT_SIZE = 72
new Vue({
el: '#app',
data () {
return {
fontSize: MAX_FONT_SIZE,
textRow: 'Initial value for the text'
}
},
watch: {
textRow: 'resetSize'
},
mounted () {
this.refreshSize()
},
updated () {
this.refreshSize()
},
methods: {
async refreshSize () {
await this.$nextTick()
const textRow = this.$refs.textRowRef
const parentRow = textRow.parentElement
if (textRow.offsetWidth > parentRow.clientWidth) {
this.fontSize = Math.max(8, this.fontSize - 1)
}
},
resetSize () {
if (this.fontSize === MAX_FONT_SIZE) {
this.refreshSize()
} else {
this.fontSize = MAX_FONT_SIZE
}
}
}
})
#app {
background: #ccc;
border: 1px solid #000;
margin: 20px;
width: 300px;
}
.text-span {
white-space: nowrap;
}
<script src="https://unpkg.com/vue#2.6.10/dist/vue.js"></script>
<div id="app">
<span
ref="textRowRef"
class="text-span"
:style="{fontSize: fontSize + 'px'}"
>
{{ textRow }}
</span>
<br>
Edit: <input v-model="textRow">
</div>