I want to create a custom loading spinner for my ionic 4 app with a GIF or SVG animation. There is no "content" property to fill with html, so how do I replace the bubbles SVG in this case with a custom SVG or GIF?
async presentLoading() {
const loading = await this.loadingController.create({
spinner: 'bubbles',
duration: 2000
});
return await loading.present();
}
As ionic V4 official documentation, its not possible. But you can use a trick via CSS.
Use <ion-img> tag inside message key of allert intead of <img/> tag
const loading = await this.loadingController.create({
message: '<ion-img src="/assets/svg/shopping.svg" alt="loading..."></ion-img>',
cssClass: 'scale-down-center',
translucent: true,
showBackdrop: false,
spinner: null,
duration: 2000
});
Create custom keyframes, you can also use this one to generate your own animation.
// CUSTOM ANIMATION KEYFRAMS********************
#-webkit-keyframes scale-down-center {
0% {
-webkit-transform: scale(1);
transform: scale(1);
}
100% {
-webkit-transform: scale(0.5);
transform: scale(0.5);
}
}
#keyframes scale-down-center {
0% {
-webkit-transform: scale(1);
transform: scale(1);
}
100% {
-webkit-transform: scale(0.5);
transform: scale(0.5);
}
}
// CUSTOM ANIMATION KEYFRAMS********************
Create custom class
.scale-down-center {
-webkit-animation: scale-down-center 0.6s cubic-bezier(0.175, 0.885, 0.320, 1.275) infinite alternate both;
animation: scale-down-center 0.6s cubic-bezier(0.175, 0.885, 0.320, 1.275) infinite alternate both;
}
EResman,
I answered your question on this link
I'm Using IONIC 4
this.myLoading = await this.loadingCtrl.create({
spinner: null, -> here you can add others spinners ou set null
remove this attribute -> message: '<ion-img src="assets/gif/loading.gif"></ion-img>',
cssClass: 'custom-loading'
});
await this.myLoading.present();
at theme/variables.scss
ion-loading.custom-loading {
.loading-wrapper {
background: #ffffff url("assets/gif/loading.gif") no-repeat center;
}
}
If you want change dimensions you can change this properties.
background-size: 100px 100px; /* to change dimension of background */
padding-top: 36px; /* padding top of white square */
padding-bottom: 36px; /* padding bottom of white square */
border-radius: 0.8rem; /* border-radius white square */
I hope that help you.
I hope this Help.
async showLoader() {
if (!this.loader) {
this.loader = await this.loadingController.create({
spinner: null,
message: '' ,
cssClass: 'custom-class custom-loading',
});
}
await this.loader.present();
}
You can use this CSS class to add your Gif as a Background.you can also use ion-loading to style the loading dialogue.
You can use this...
async presentLoading() {
const loading = await this.loading.create({
spinner: null,
message: '<img src="assets/icon/loader.gif">',
duration: 5000,
});
loading.present();
}
Related
The slider I am building have the active slider bigger than the others. I managed to make it work without the animation with flkty.reposition(). However, I am trying now to add the animation where the next slide grows in and the active decrease out. For The animation I am using GSAP.
The issue I am facing is to overwrite the left property with gsap so that it continuous animate. As of now, the left property (controlled by Flickity) does not take into account the final size (controlled by GSAP) of the selected slide.
https://codepen.io/stefanomonteiro/pen/VwzwjLw?editors=0010
As the left property of each slide is controlled by Flickity, we could use margin-left with a minus value as an alternative property to pull the selected slide to the left. I know margin is not a good property to animate but it works in this case without digging too deep into the Flickity core.
Here is the GSAP code:
gsap.to(slides, {
duration: 1,
width: "220px",
height: "336px"
});
gsap.to(selectedSlide, {
duration: 1,
marginLeft: "-248px", // the empty space calculated by newWidth - oldWidth
width: "468px",
height: "630px",
onComplete: () => {
// once all animations have been settled, we reset the margin
gsap.set(selectedSlide, { marginLeft: "" });
// and tell Flickity to update
flkty.resize();
flkty.reposition();
}
});
And the snippets
const animate = () => {
const flkty = Flickity.data(".carousel");
const selectedSlide = flkty.selectedElement;
const slides = flkty.getCellElements();
// remove the selected slides
slides.splice(flkty.selectedIndex, 1);
gsap.to(slides, {
duration: 1,
width: "220px",
height: "336px"
});
gsap.to(selectedSlide, {
duration: 1,
marginLeft: "-248px", // the empty space calculated by newWidth - oldWidth
width: "468px",
height: "630px",
onComplete: () => {
// once all animations have been settled, we reset the margin
gsap.set(selectedSlide, {
marginLeft: ""
});
// and tell Flickity to update
flkty.resize();
flkty.reposition();
}
});
};
new Flickity(".carousel", {
cellAlign: "right",
wrapAround: true,
percentPosition: false,
on: {
ready: () => animate()
}
});
const nextButton = document.querySelector(".flickity-button.next");
nextButton.addEventListener("click", () => animate());
/* external css: flickity.css */
* {
box-sizing: border-box;
}
body {
font-family: sans-serif;
}
.carousel {
background: #EEE;
}
.carousel-cell {
width: 220px;
height: 336px;
margin-right: 20px;
background: #8C8;
border-radius: 5px;
counter-increment: carousel-cell;
}
.carousel-cell.is-selected {
width: 468px;
height: 630px;
z-index: 1;
}
/* cell number */
.carousel-cell:before {
display: block;
text-align: center;
content: counter(carousel-cell);
line-height: 200px;
font-size: 80px;
color: white;
}
<link href="https://npmcdn.com/flickity#2/dist/flickity.css" rel="stylesheet" />
<script src="https://unpkg.co/gsap#3/dist/gsap.min.js"></script>
<script src="https://npmcdn.com/flickity#2/dist/flickity.pkgd.js"></script>
<h1>Flickity - wrapAround</h1>
<!-- Flickity HTML init -->
<div class="carousel">
<div class="carousel-cell"></div>
<div class="carousel-cell"></div>
<div class="carousel-cell"></div>
<div class="carousel-cell"></div>
<div class="carousel-cell"></div>
</div>
And the Codepen
You can also notice that we have to wait until the animation is finished until we perform the next click, otherwise, it would mess up the whole process. This is predictable. Hence, I personally will try not to manipulate this Flickity slider for this kind of animation. Just want to give you a solution, anyway.
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
The transition element of vue only works with display:none but not visibility:hidden, is there any way to make it work with visibility? I want to get the clientWidth of the element before it shows up, with display:none I can't get that value.
By the way I'm using vue3.
Here is the reproduction demo:
https://codesandbox.io/s/competent-hermann-b1s5q
I'm going to assume, for the sake of argument, that you genuinely do need to use visibility for hiding and that other potential solutions (such as opacity) won't work in your real use case, possibly because they don't prevent user interactions with the element.
However, the assertion in the question is slightly misleading. It isn't really a difference between display and visibility. The real difference here is that the display case is using v-show, which includes special handling for transitions.
The current source code for v-show can be seen here:
https://github.com/vuejs/vue-next/blob/d7beea015bdb208d89a2352a5d43cc1913f87337/packages/runtime-dom/src/directives/vShow.ts
A similar approach can be used to construct a directive that uses visibility. Below is an example. It is based on the code for v-show but I've cut it back to just the code required for this particular use case:
const visible = {
updated(el, { value, oldValue }, { transition }) {
if (!value === !oldValue) {
return
}
if (value) {
transition.beforeEnter(el)
el.style.visibility = ''
transition.enter(el)
} else {
transition.leave(el, () => {
el.style.visibility = 'hidden'
})
}
}
}
Vue.createApp({
data() {
return {
show: true
};
},
methods: {
toggle() {
this.show = !this.show;
}
},
directives: {
visible
}
}).mount('#app')
#app {
text-align: center;
}
.tooltip-enter-active {
transition: transform 0.4s ease-out, opacity 0.3s ease-out;
}
.tooltip-leave-active {
transition: transform 0.35s ease-in, opacity 0.28s ease-out;
}
.tooltip-enter-from {
transition: none;
}
.tooltip-enter-from,
.tooltip-leave-to {
transform: translateY(-30px) scale(0.96);
opacity: 0;
}
<script src="https://unpkg.com/vue#3.0.2/dist/vue.global.prod.js"></script>
<div id="app">
<transition name="tooltip">
<div v-visible="show">
Using visibility
</div>
</transition>
<button #click="toggle">toggle message</button>
</div>
I did also have to make a small CSS change to give the enter transition a kick:
.tooltip-enter-from {
transition: none;
}
You'd probably be better off without <transition> in this case:
const app = Vue.createApp({
data() {
return {
show: true,
};
},
methods: {
toggle() {
const tooltip = this.$refs.tooltip;
this.show = !this.show;
tooltip.classList.toggle("tooltip-show");
},
},
mounted() {
console.log('Tooltip-width: ', this.$refs.tooltip.clientWidth);
},
});
app.mount('#app')
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
.tooltip {
opacity: 0;
transform: translateY(-30px) scale(0.96);
transition: transform 0.35s, opacity 0.25s;
}
.tooltip-show {
opacity: 1;
transform: translateY(0) scale(1);
}
<script src="https://unpkg.com/vue#3.0.2/dist/vue.global.js"></script>
<div id="app">
<div class="tooltip" ref="tooltip">This will work!</div>
<button #click="toggle">toggle tooltip</button>
</div>
Target
On click "Open menu" button:
Dim overlay appearing with fade-in animation
Once dim overlay animation done, from the top, dim overlay is appearing with the sliding animation from the top to bottom:
Solution attempt and problem
<template lang="pug">
transition(name="fade")
.DrawerMenu-DimUnderlay(v-if="displayFlag")
.DrawerMenu-Body Drawer menu
</template>
Before slide down the .DrawerMenu-Body, .DrawerMenu-DimUnderlay must be mounted and rendered.
I don't know how to implement it.
🌎 Fiddle
You can achieve that by using CSS Animations and Vue Transition.
First, separate your overlay and content into different transitions:
<template lang="pug">
div
transition(name="overlay")
.DrawerMenu-Overlay(v-if="displayFlag")
transition(name="content")
.DrawerMenu-Body(v-if="displayFlag") Drawer menu
</template>
Then define your animations:
.DrawerMenu {
&-Overlay {
...
display: none;
}
...
}
.overlay-enter-active {
display: block;
animation: fade-in-and-slide-down 2s;
}
.content-enter-active {
animation: wait-and-fade-in 3s;
}
.content-leave-active {
animation: fade-out 1s;
}
#keyframes fade-in-and-slide-down {
0% {
opacity: 0;
}
50% {
opacity: 1;
transform: translateY(0);
}
100% {
transform: translateY(100%);
}
}
#keyframes wait-and-fade-in {
0% {
opacity: 0;
}
66% {
opacity: 0;
}
100% {
opacity: 1;
}
}
#keyframes fade-out {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
Example in CodeSandbox.
Another solution is using JavaScript animation library (such as animejs) combine with Vue Transition Hooks. I would prefer this solution for a complex animation.
<template lang="pug">
transition(#enter='enter' #leave='leave')
div(v-if='displayFlag')
.DrawerMenu-Overlay(ref='overlay')
.DrawerMenu-Body(ref='content' #click="displayFlag = false") Drawer menu
</template>
import anime from "animejs";
...
methods: {
enter(el, done) {
anime
.timeline({
easing: "linear",
duration: 1000,
complete: done
})
.add({
targets: this.$refs.overlay,
opacity: [0, 1]
})
.add({
targets: this.$refs.overlay,
translateY: "100%"
})
.add({
targets: this.$refs.content,
opacity: [0, 1]
});
},
leave(el, done) {
anime({
targets: el,
duration: 2000,
opacity: 0,
complete: done
});
},
...
}
...
You can also use without transition component but you have to handle v-if variable by yourself.
Example in CodeSandbox.
Not sure if there are 2 questions here, but for your last question, I would say that is because that ref component does not have a property display.
It does however, have a function display()
Therefore, change your button click to this:
<button #click="$refs.drawerMenu.display()">Open menu</button>
I am trying to create a menu which is hidden but appears, fixed to the top, once the user begins scrolling down the page. So far I have managed to create a menu which sticks to the top upon scrolling but am stuck on how to hide this menu initially.
This is the code I am using so far:
(I am using wordpress-headway)
JQuery:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript">
jQuery(document).ready(function($) {
//STICKY NAV
var isMobile = {
Android: function() {
return navigator.userAgent.match(/Android/i) ? true : false;
},
BlackBerry: function() {
return navigator.userAgent.match(/BlackBerry/i) ? true : false;
},
iOS: function() {
return navigator.userAgent.match(/iPhone|iPad|iPod/i) ? true : false;
},
Windows: function() {
return navigator.userAgent.match(/IEMobile/i) ? true : false;
},
any: function() {
return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Windows());
}
};
//Calculate the height of <header>
//Use outerHeight() instead of height() if have padding
var aboveHeight = $('.top-row').outerHeight();
//when scroll
$(window).scroll(function(){
//if scrolled down more than the header’s height but this isn't mobile
if ($(window).scrollTop() > aboveHeight && !isMobile.any()){
// if yes, add “fixed” class to the <nav>
// add padding top to the #content
// (value is same as the height of the nav)
$('.block-type-navigation').addClass('fixed').css('top','0').next()
.css('padding-top','42px');
} else {
// when scroll up or less than aboveHeight,
// remove the “fixed” class, and the padding-top
$('.block-type-navigation').removeClass('fixed').next()
.css('padding-top','0');
}
});
});
</script>
CSS:
.fixed {
position:fixed !important;
left: 0;
text-align: center;
}
.fixed .block-content {
display: inline-block;
text-align: left;
width: 940px; /* This should be the width of your grid!!! */
float:none;
}
.fixed {
position:fixed !important;
left: 0;
text-align: center;
display: block !important;
}
It's driving me crazy so I'd appreciate ANY help!
Thank you!
If you don't want the nav to show unless the user has scrolled passed a certain point then couldn't it always be fixed just off the top of the screen:
.menu {
position:fixed;
top:-42px;
}
then shown or hidden by toggling a class
.menu.is-visible {
top:0;
}
using a scroll listener.
$win = $(window);
$win.on('scroll', function() {
$(".menu").toggleClass('is-visible', $win.scrollTop() > 42);
});
You could even add some CSS animation to the top property
.menu {
-webkit-transition: top 0.2s ease-in-out;
}
to get a cool transition.
Note: All this code is typed out right in here and not tested.
Note: You should definitely put a throttle on the scroll handler too.