How to make a preloader for an image? - vue.js

I have a picture
<img class="block" :src="https://upload.wikimedia.org/wikipedia/commons/thumb/5/53/Google_%22G%22_Logo.svg/1024px-Google_%22G%22_Logo.svg.png" />
But how to show some other picture while the main one has not loaded?

<template> <div>
<img class="loading" v-if="pending" alt="image" /> <!-- This is a beautiful animation -->
<img src="your-image.png" v-if="pending" alt="image" /> <!-- If you want to put another optional picture you will do so -->
<img v-else :src="img" class="block" alt="image" /> </div>
</template>
<script>
export default {
data() {
return {
pending: false,
img: ''
};
},
created() {
this.pending = false
// your get request
fetch("https://example-api.com/image")
.then((data) => (this.img = data.img))
.finally(() => {
this.pending = false;
});
},
};
</script>
<style lang="scss">
.loading {
position: relative;
user-select: none;
cursor: wait;
transition: all 0.2s;
border-radius: 4px !important;
border: none;
background: transparent;
overflow: hidden;
color: transparent !important;
&:before {
content: "";
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
width: 100%;
height: 100%;
background-color: #e8ecf1 !important;
cursor: wait;
-webkit-mask-image: radial-gradient(white, black);
transition: all 0.2s;
}
&:after {
animation: loading 1.5s infinite;
content: "";
height: 100%;
left: 0;
position: absolute;
right: 0;
top: 0;
transform: translateX(-100%);
z-index: 1;
background: linear-gradient(
90deg,
rgba(255, 255, 255, 0),
rgba(255, 255, 255, 0.5),
rgba(255, 255, 255, 0)
);
}
svg {
opacity: 0;
}
}
</style>

Related

How to show vue component on only certain routes

I want to show my Navbar in every page except the login and register page of my Vue app. How can I do that?
App.vue code:
<template>
<div id="app">
<NavBar v-if="this.$route!= '/' || this.$route!='/register' " />
<main class="form-signin">
<router-view/>
</main>
</div>
</template>
<script>
import NavBar from './components/NavBar.vue'
export default {
components: {NavBar}
}
</script>
<style>
nav{
}
#app{
width: 100%;
height: 100%;
}
html,
body {
height: 100%;
min-height: 75rem;
}
body {
justify-content: center;
display: -ms-flexbox;
display: flex;
-ms-flex-align: center;
align-items: center;
padding-top: 40px;
padding-bottom: 40px;
background-color: #f5f5f5;
}
.form-signin {
width: 100%;
max-width: 330px;
padding: 15px;
margin: auto;
}
.form-signin .checkbox {
font-weight: 400;
}
.form-signin .form-control {
position: relative;
box-sizing: border-box;
height: auto;
padding: 10px;
font-size: 16px;
}
.form-signin .form-control:focus {
z-index: 2;
}
.form-signin input[type="email"] {
margin-bottom: -1px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
.form-signin input[type="password"] {
margin-bottom: 10px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
body,html{
padding: 0;
margin: 0;
min-height: 100vh;
width: 100%;
}
html{
background-color: white;
}
</style>
I tried using this.$route and v-if , but it wasn't working. The end result still looked like this:
Register page:
Login page:
I just need to exclude the navbar from both of these pages. How can I do that?

how to add a shape divider in vuetify.js

I am using vuetify and I'm trying to add a shape divider like what I've shown in the picture, but I'm unable to do it.
You need to do that with HTML and CSS.
There is some ways.
It would be done with a background image
Another way is a svg file and css
Do it with css transform
You can check this codepen
header {
position: relative;
height: 300px;
background-image: linear-gradient(#ff9d2f, #ff6126);
}
h1 {
margin: 0;
padding: 100px 0;
font: 44px "Arial";
text-align: center;
}
header h1 {
color: white;
}
.divider {
position: absolute;
bottom: 0;
left: 0;
right: 0;
width: 100%;
height: 100px;
/* drop the height to have a constant angle for all screen widths */
}
<header>
<h1>Header Content</h1>
<img src="https://assets.codepen.io/t-517/divider-triangle.png" class="divider" />
</header>
<section>
<h1>Section Content</h1>
</section>
to get Idea how to make it as 2nd way .
and this one codepen for make it with css transform(third way).
header {
position: relative;
height: 300px;
overflow: hidden;
}
.header__bg {
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 0;
width: 100%;
height: 100%;
background-image: linear-gradient(#ff9d2f, #ff6126);
transform: skewY(-6deg);
transform-origin: top left;
}
h1 {
margin: 0;
padding: 100px 0;
font: 44px "Arial";
text-align: center;
}
header h1 {
position: relative;
color: white;
}
<header>
<div class="header__bg"></div>
<h1>Header Content</h1>
</header>
<section>
<h1>Section Content</h1>
</section>

How to do dynamic pseudo class width calculation inside methods?

I want to build a stepper component (trying to avoid using packages as less as possible). I have created it using plain HTML template and CSS and I want to size the ::before element according to the x number.
Something like this width: ${100% / 4}. I tried this method using methods and got a rendering error. Is it possible to do this? Or there is another way to achieve this?
Please have a look at my script:
<template>
<div class="c-stepper">
<div class="step-item" :style="drawLinePrecision(4)" v-for="item in 4" :key="item">
<div class="step-marker">
<i class="icon icon-duotones-check"></i>
</div>
<div class="step-info">
<p class="step-title">Step 1</p>
</div>
</div>
</div>
</template>
<script>
export default {
methods: {
drawLinePrecision(length) {
return `.step-item:not(:last-child)::before {
content: "";
height: 3px;
width: ${(100% / length)};
position: absolute;
top: 25%;
background-color: $primary;
}`
}
}
}
</script>
<style lang="scss" scoped>
#import "../../sass/variables";
.c-stepper {
display: flex;
justify-content: space-around;
align-items: center;
margin: 5rem 0rem;
position: relative;
.step-item {
&:not(:last-child)::before {
content: "";
height: 3px;
width: calc(100% / 4);
position: absolute;
top: 25%;
background-color: $primary;
}
.step-marker {
position: relative;
width: 50px;
height: 50px;
background-color: white;
border: 3px solid $primary;
border-radius: 50%;
.icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
}
}
</style>
I just found a solution after some workaround. Instead of doing it like that, I had to make the width of the .step-item to be fixed by 100% except the last child and move the position: relative; from the parent down to the step-item itself.
Now, All I need is to set the padding from the parent component which display the c-stepper component.
Here is my script:
<template>
<div class="c-stepper">
<div class="step-item" v-for="item in 11" :key="item">
<div class="step-marker">
<i class="icon icon-duotones-check"></i>
</div>
<div class="step-info">
<p class="step-title">Step 1</p>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
#import "../../sass/variables";
.c-stepper {
display: flex;
justify-content: space-around;
align-items: center;
margin: 5rem 0rem;
.step-item {
margin: 0px auto;
position: relative; // step item must be relative
&:not(:last-child) {
width: 100%; // make it fixed
}
&:not(:last-child)::before {
content: "";
height: 3px;
width: 100%; // make it 100% width so that it won't let any gap to be rendered in the ui
position: absolute;
top: 25%;
background-color: $primary;
}
.step-marker {
position: relative;
width: 50px;
height: 50px;
background-color: white;
border: 3px solid $primary;
border-radius: 50%;
.icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
}
}
</style>

Overlay on hover in vue.js

Im trying to implement displaying text when hovering over an image in vue.js but I am a bit stuck. Im trying to implement this example on an array with multiple images:
https://www.w3schools.com/howto/tryit.asp?filename=tryhow_css_image_overlay_fade
I got a pretty big vue file but here is the essential:
template:
</template>
[...]
<div v-for="item in filteredPeople" v-bind:key="item.id" class="list-complete-item">
<router-link #mouseover.native="hovertext" :to="'/'+item.link">
<img class="list-complete-img" :src="'http://placehold.it/400x300?text='+item.id" alt>
</router-link>
</div>
[...]
</template>
script
<script>
export default {
data() {
return {
info: [
{
id: 1,
title: "Title one",
link: "project1",
hovertext: "project1 hover text lorem lorem lorem",
category_data: {
"1": "Category 1"
}
},
[...]
methods: {
hovertext() {
console.log("");
},
I had some idea to try to use a method to put the text in a div under the image but then I cannot get the text to get above the image on hover. And I cannot get the method right ... Not really sure this is a good way doing it,
I also found this codepen example:
https://codepen.io/oliviaisarobot/pen/xzPGvY
This is pretty close to what I want to do but I cannot get the text to display here either.
I am a bit lost. Any help how to do this in vue?
---------- UPDATE ----------
I want the image boxes to stretch so they always fits the window but I seems to get a gap between my flexbox rows which I cannot seem to get rid of. You can see the white space. I attach my stylesheet.
.list-complete {
display: flex;
height: auto;
flex-direction: row;
flex-flow: row wrap;
}
.list-complete-item {
flex: 0 1 50%;
display: inline-block;
}
.list-complete-item a {
display: inline-block;
position: relative;
width: 100%;
height: auto;
outline: 1px solid #fff;
}
.list-complete-img {
width: 100%;
}
.text {
color: rgb(186, 74, 74);
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
text-align: center;
}
.list-complete-item a:hover .overlay {
opacity: 1;
}
.overlay {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
overflow: hidden;
height: 60%;
width: 100%;
opacity: 0;
transition: 0.5s ease;
background-color: #008cba;
}
You don't need to use js(vue) events. Do it with plain css, like in example you linked to.
Go with this template:
<div v-for="item in filteredPeople" v-bind:key="item.id" class="list-complete-item">
<router-link :to="'/'+item.link">
<img class="list-complete-img" :src="'http://placehold.it/400x300?text='+item.id" alt>
<div class="overlay">
<div class="text">{{ item.hovertext }}</div>
</div>
</router-link>
</div>
And style it up:
.list-complete-item {
width: 400px;
height: 300px;
display: inline-block;
}
.list-complete-item a {
display: inline-block;
position: relative;
width: 400px;
height: 300px;
}
.list-complete-item a .image {
display: block;
width: 100%;
height: auto;
}
.list-complete-item a .overlay {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
height: 100%;
width: 100%;
opacity: 0;
transition: .5s ease;
background-color: #008CBA;
}
.list-complete-item a:hover .overlay {
opacity: 1;
}
.list-complete-item a .text {
color: white;
font-size: 20px;
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
text-align: center;
}
And the final result:

css animation rotate while moving

am trying to create an animation that
scale
rotate
translate
but i cant understand why the rotation doesnt play well with translateX, am trying to get the animation to play in a continuous motion along the z-axis not like a swirl, i've searched alot but i couldnt find what am after so any help is appreciated.
function reRun() {
document.querySelector('.item').classList.remove('spin-animation')
setTimeout(function() {
document.querySelector('.item').classList.add('spin-animation')
},300)
}
#keyframes spin {
0% {
transform: scale(1) rotateY(0deg) translate(0, 0);
}
25% {
transform: scale(2) rotateY(360deg) translate(-1rem, -1rem);
}
50% {
transform: scale(3) rotateY(0deg) translate(-1.5rem, -1.5rem);
}
75% {
transform: scale(4) rotateY(360deg) translate(-2rem, -2rem);
}
100% {
transform: scale(5) rotateY(0deg) translate(-2.5rem, -2.5rem);
}
}
.spin-animation {
animation: spin 2s linear forwards;
}
.item {
position: absolute;
bottom: 1rem;
right: 1rem;
font-size: 2rem;
color: red;
}
.axis {
position: absolute;
right: 0.8rem;
bottom: 1.2rem;
}
.y-axis {
width: 1px;
height: 300px;
background: black;
position: absolute;
right: 0;
bottom: 0;
}
.x-axis {
width: 300px;
height: 1px;
background: black;
}
.z-axis {
width: 1px;
height: 300px;
position: absolute;
right: 0;
bottom: 0;
background: black;
transform: skewX(45deg);
transform-origin: right bottom;
}
.z-axis span {
padding: 0.5rem;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
<button onclick="reRun()">ReRun the Example</button>
<div class="item spin-animation">
<i class="fa fa-question" aria-hidden="true"></i>
</div>
<div class="axis">
<div class="y-axis"><span>Y</span></div>
<div class="x-axis"><span>X</span></div>
<div class="z-axis"><span>Z</span></div>
</div>
In most cases the order matters look at this. First translate (it also changes the rotation center or axis) then scale then rotate.
function reRun() {
document.querySelector('.item').classList.remove('spin-animation')
setTimeout(function() {
document.querySelector('.item').classList.add('spin-animation')
},300)
}
#keyframes spin {
0% {
transform: translate(0, 0) scale(1) rotateY(0deg);
}
25% {
transform: translate(-1rem, -1rem) scale(2) rotateY(360deg);
}
50% {
transform: translate(-1.5rem, -1.5rem) scale(3) rotateY(0deg);
}
75% {
transform: translate(-2rem, -2rem) scale(4) rotateY(360deg);
}
100% {
transform: translate(-2.5rem, -2.5rem) scale(5) rotateY(0deg);
}
}
.spin-animation {
animation: spin 2s linear forwards;
}
.item {
position: absolute;
bottom: 1rem;
right: 1rem;
font-size: 2rem;
color: red;
}
.axis {
position: absolute;
right: 0.8rem;
bottom: 1.2rem;
}
.y-axis {
width: 1px;
height: 300px;
background: black;
position: absolute;
right: 0;
bottom: 0;
}
.x-axis {
width: 300px;
height: 1px;
background: black;
}
.z-axis {
width: 1px;
height: 300px;
position: absolute;
right: 0;
bottom: 0;
background: black;
transform: skewX(45deg);
transform-origin: right bottom;
}
.z-axis span {
padding: 0.5rem;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
<button onclick="reRun()">ReRun the Example</button>
<div class="item spin-animation">
<i class="fa fa-question" aria-hidden="true"></i>
</div>
<div class="axis">
<div class="y-axis"><span>Y</span></div>
<div class="x-axis"><span>X</span></div>
<div class="z-axis"><span>Z</span></div>
</div>