When I click the button, the state showMore will be toggled.
If I click it at the first time, it rotates as expected.
But when I click again the button, the animation doesn't works.
App.vue
<template>
<div class="item">
<button class="learn-more-btn" #click="toggleShow">
<span>Rotate</span>
<img
src="./assets/logo.png"
alt="arrow"
:class="arrowLeft"
width="50"
height="50"
/>
</button>
</div>
</template>
<script>
export default {
components: {},
data() {
return {
showMore: false,
};
},
methods: {
toggleShow() {
this.showMore = !this.showMore;
},
},
computed: {
arrowLeft() {
let arrow = "turn-arrow";
if (this.showMore === true) {
return arrow;
}
return true;
},
},
};
</script>
<style scoped lang="scss">
.item {
display: flex;
border: 1px solid grey;
width: 150px;
.learn-more-btn {
display: flex;
justify-content: center;
align-items: center;
background-color: transparent;
border: none;
&:hover {
cursor: pointer;
}
img {
display: inline-block;
}
.turn-arrow {
-moz-transform: rotate(180deg);
-ms-transform: rotate(180deg);
-webkit-transform: rotate(180deg);
transform: rotate(180deg);
transition: all 3000ms;
}
span {
padding: 0 0.5rem;
}
}
}
</style>
Codesandbox:
https://codesandbox.io/s/headless-fire-ng001?file=/src/App.vue
I just refactored your code a little bit and simplified things.
Instead of using transitions I replaced them with animations and toggled between .rotate-down and .rotate-up classes which use this animations.
Check it out: https://codesandbox.io/s/wonderful-wescoff-icnnm?file=/src/App.vue
Hope it's helpful :)!
Related
I'm adding a keydown eventlistener that'll simply make my box div show. However, when I press a key, nothing happens and instead the console tells me, "Uncaught TypeError: box is null".
No idea what the problem is, please help
Here's my App.js:
import "./styles.css"
const box = document.querySelector(".box");
const notification = document.querySelector(".notif");
window.onload = document.addEventListener("keydown", e => {
box.classList.add("active");
notification.classList.add("inactive");
})
function App() {
return (
<>
<div className='notif'>Press Any Key</div>
<div className='box'>
<h2>20</h2>
<h1>Shift</h1>
</div>
</>
);
}
export default App;
and the css:
#import url('https://fonts.googleapis.com/css2?family=Poppins&display=swap');
:root {
--teal: #00ccff;
--pink: #d400d4;
}
*, *::before, *::after {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Poppins', sans-serif;
}
body {
display: flex;
min-height: 100vh;
align-items: center;
justify-content: center;
background: #0e1538;
}
.box {
position: relative;
width: 300px;
height: 300px;
display: flex;
justify-content: center;
align-items: center;
background: rgba(0,0,0,.5);
overflow: hidden;
border-radius: 20px;
flex-direction: column;
display: none;
}
.box.active {
display: flex;
}
.box::before {
content: '';
position: absolute;
width: 150px;
height: 140%;
background: linear-gradient(var(--teal), var(--pink));
animation: animate 4s linear infinite;
}
#keyframes animate {
0%{
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.box::after {
content: '';
position: absolute;
background: #0e1538;
inset: 4px;
border-radius: 16px;
}
.box h2 {
position: relative;
color: white;
font-size: 5em;
z-index: 10;
}
.box h1 {
position: relative;
color: white;
z-index: 10;
background: rgba(0,0,0,.5);
width: 97.5%;
text-align: center;
letter-spacing: 5px;
}
.notif {
font-size: 3em;
background: var(--teal);
padding: 0 20px;
border-radius: 20px;
}
.notif.inactive {
display: none;
}
You should be maintaining/updating state with the new class information when you press a key. Add the listener within the useEffect (along with a cleanup function). When a key is pressed the listener calls a function that updates the state (in this case switching the active/inactive classes). The component is re-rendered, and the elements' class names are changed using a little helper function.
const { useEffect, useState } = React;
function Example() {
// Initialise state
const [ active, setActive ] = useState({
box: false,
notification: true
});
// A function to update the state
function toggleActive() {
setActive(prev => ({
box: !prev.box,
notification: !prev.notification
}));
}
// An effect that adds a listener, w/ a cleanup function
useEffect(() => {
window.addEventListener('keydown', toggleActive);
return () => {
window.removeEventListener('keydown', toggleActive);
}
}, []);
// Returns the active status of the element from state
function isActive(type) {
return active[type] ? 'active' : 'inactive';
}
return (
<div>
<div className={isActive('notification')}>
Press Any Key
</div>
<div className={isActive('box')}>
<h2>20</h2>
<h1>Shift</h1>
</div>
</div>
);
}
ReactDOM.render(
<Example />,
document.getElementById('react')
);
.active { color: red; }
.inactive { color: black; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
import "./styles.css"
import {useEffect} from 'react'
function App() {
useEffect(()=>{
const box = document.querySelector(".box");
const notification = document.querySelector(".notif");
document.addEventListener("keydown", e => {
box.classList.add("active");
notification.classList.add("inactive");
})
},[])
return (
<>
<div className='notif'>Press Any Key</div>
<div className='box'>
<h2>20</h2>
<h1>Shift</h1>
</div>
</>
);
}
export default App;
I have three div boxes in a row, and on scroll, they should show slowly from the side, but when I scroll up slowly and a little after they show up, the left and right boxes for some reason stay on the page.
My function showBox3 writes in console "false" and classList where is "show" class, but this class shouldn't be there.
Actually, my code works when I scroll normally but when I stop scrolling slightly above the limit, the left and right boxes stay on page.
And one more problem is when "topBox" is a little below the limit, and I scroll up just a little more, but still below the limit, boxes quickly remove from the page and show up again.
const box21 = document.querySelector(".box21");
const box22 = document.querySelector(".box22");
const box23 = document.querySelector(".box23");
var trigger = window.innerHeight * 0.8;
window.addEventListener("scroll", showBox2);
function showBox2() {
var check;
const topBox = box22.getBoundingClientRect().top;
if (trigger > topBox) {
box22.classList.add("show");
check = true;
} else {
box22.classList.remove("show");
check = false;
}
showBox1(check);
showBox3(check);
}
function showBox1(ch) {
if (ch == true) {
setTimeout(() => {
box21.classList.add("show");
}, 400);
} else {
box21.classList.remove("show");
}
}
function showBox3(ch) {
if (ch == true) {
setTimeout(function () {
box23.classList.add("show");
}, 700);
} else {
box23.classList.remove("show");
console.log(box23.classList);
console.log(ch);
}
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.box1 {
height: 110vh;
background-color: aqua;
display: flex;
align-items: center;
justify-content: center;
}
/* I have a bug in box 2 */
.box2 {
height: 60vh;
width: 100%;
background-color: #ddd;
display: flex;
align-items: center;
justify-content: space-around;
}
.box2>div {
height: 70%;
width: 27%;
background-color: black;
}
.box21 {
transform: translateX(-140%) rotate(-45deg);
transition: transform 1.4s ease;
}
.box21.show {
transform: translateX(0%) rotate(0deg);
}
.box22 {
transform: translateX(340%) rotate(+45deg);
transition: transform 0.8s ease;
}
.box22.show {
transform: translateX(0%) rotate(0deg);
}
.box23 {
transform: translateX(340%) rotate(-45deg);
transition: transform 1.8s ease;
}
.box23.show {
transform: translateX(0%) rotate(0deg);
}
/* this part below is not important */
.box3 {
height: 60vh;
width: 100%;
background-color: cornflowerblue;
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="css.css" />
</head>
<body>
<div class="box1">
<h1>Scroll down</h1>
</div>
<div class="box2">
<div class="box21"></div>
<div class="box22"></div>
<div class="box23"></div>
</div>
<div class="box3"></div>
</body>
<script src="js.js"></script>
</html>
I have a navbar with a hamburger button and i want to change the style (the bar color and the hamburger button style) on tap. I mention that i've tried using &:active, &.activeBar on the scss file.
For some reason the background color doesn t change and the hamburger button doesn't turn into an X
This is my SCSS file:
.topbar {
width: 100%;
background-color: $secondaryColor;
color: $mainColor;
height: 70px;
position: fixed;
top: 0;
z-index: 3;
transition: all 1s ease;
overflow: hidden;
.wrapper {
padding: 10px 30px;
display: flex;
align-items: center;
justify-content: space-between;
}
.left {
display: flex;
align-items: center;
margin: 10;
.itemContainer {
display: flex;
align-items: center;
.icon {
font-size: 36px;
margin-right: 5px;
&:hover {
color: blue;
}
}
span {
font-size: 15px;
font-weight: 700;
}
}
.logo {
font-size: 40px;
font-weight: 700;
text-decoration: none;
color: inherit;
margin-right: 40px;
}
}
.right {
.hamburger {
width: 32px;
height: 25px;
display: flex;
flex-direction: column;
justify-content: space-between;
span {
width: 100%;
height: 3px;
background-color: $mainColor;
transform-origin: left;
transition: all 0.5s ease;
}
}
}
&.activeBar {
background-color: $mainColor;
color: $secondaryColor;
.hamburger span {
&:first-child {
background-color: $secondaryColor;
transform: rotate(45deg);
}
&:nth-child(2) {
opacity: 0;
}
&:last-child {
background-color: $secondaryColor;
transform: rotate(-45deg);
}
}
}
}
And this is where i try to change the classname in my js file:
import React from 'react';
import styles from './topbar.module.scss';
import { Facebook } from '#material-ui/icons';
const Topbar = ({ open, toggle }) => {
function menuToggled() {
toggle(!open);
console.log('menuToggled', open);
}
return (
<div className={`${styles.topbar} ${open ? 'active' : ''}`}>
<div className={styles.wrapper}>
<div className={styles.left}>
<a href='#slider' className={styles.logo}>
Muntenia cu gust
</a>
<div className={styles.itemContainer}>
<Facebook
className={styles.icon}
onClick={() => window.open('https://stackoverflow.com/')}
/>
</div>
</div>
<div className={styles.right}>
<div className={styles.hamburger} onClick={menuToggled}>
<span className={styles.line1}></span>
<span className={styles.line2}></span>
<span className={styles.line3}></span>
</div>
</div>
</div>
</div>
);
};
export default Topbar;
In your code, you're adding active as a class in the "global" scope. By default with css-modules, all classes are output in "local" scope. So your .topbar class, after compilation, would look something like: .Topbar_topbar__abc12 ("abc12" would be a hash).
So, the .active class is also being compiled to local scope, looking something like: .Topbar_active_def34
In your markup, you're toggling the .active class as a string rather than as styles.active. So, on output, you're expecting your "active" class to be .Topbar_topbar__abc12.active when in actuality, your stylesheet is declaring .Topbar_topbar_abc12.Topbar_active_def32 as the "active" class.
By declaring .active as being in the global scope, the output would then be as expected. Try adding the :global flag to your stylesheet:
...
&:global(.active) {
background-color: $mainColor;
color: $secondaryColor;
.hamburger span {
&:first-child {
background-color: $secondaryColor;
transform: rotate(45deg);
}
&:nth-child(2) {
opacity: 0;
}
&:last-child {
background-color: $secondaryColor;
transform: rotate(-45deg);
}
}
}
...
use classnames package (https://www.npmjs.com/package/classnames) to combine classes that are on the same element, the rest is handled the same as in vanilla html/css/js
In your code you use active it also should come from styles and be combined using:
classnames('styles.ElementClassName', active && styles.active)
Adding the active selector on the regular .hamburger class should trigger on touch. Also the classname in you scss is activeBar but in your react app is only active.
&.clicked {
background-color: $mainColor;
color: $secondaryColor;
.hamburger span {
&:first-child {
background-color: $secondaryColor;
transform: rotate(45deg);
}
&:nth-child(2) {
opacity: 0;
}
&:last-child {
background-color: $secondaryColor;
transform: rotate(-45deg);
}
}
}
If you want to change the topBar also then you'll need to change your TopBar component a bit
import React, { useState } from 'react';
import styles from './topbar.module.scss';
import { Facebook } from '#material-ui/icons';
const Topbar = ({ open, toggle }) => {
const [isClicked, setIsClicked] = useState(false);
function menuToggled() {
toggle(!open);
console.log('menuToggled', open);
}
return (
<div className={`${styles.topbar} ${open ? 'active' : ''} ${ isClicked && ' clicked'}`}>
<div className={styles.wrapper}>
<div className={styles.left}>
<a href='#slider' className={styles.logo}>
Muntenia cu gust
</a>
<div className={styles.itemContainer}>
<Facebook
className={styles.icon}
onClick={() => window.open('https://stackoverflow.com/')}
/>
</div>
</div>
<div className={styles.right}>
<div className={styles.hamburger} onClick={menuToggled} onTouchStart={(e) => setIsClicked(true)}>
<span className={styles.line1}></span>
<span className={styles.line2}></span>
<span className={styles.line3}></span>
</div>
</div>
</div>
</div>
);
};
I have a simple CSS3 Flipbox which I'm trying to re-use multiple times. The best would be if each element has a unique ID, and each of them would be separately triggered on click.
At the moment all the elements are being triggered on click.
What would be the best approach without just doubling the code?
Here demo of the code:
https://codepen.io/baidoc/pen/LYeqwxe
let cardTransitionTime = 500;
let $card = $('.card');
let switching = false;
$('.card').click(flipCard);
function flipCard() {
if (switching) {
return false;
}
switching = true;
$card.toggleClass('is-switched');
window.setTimeout(function () {
$card.children().children().toggleClass('is-active');
switching = false;
}, cardTransitionTime / 2);
}
I havent thought your transitionTime example through and left it out in a more simplified example, but for the other part you can simply use the event argument which is handed over to your called function per default:
$('.card').click(flipCard);
function flipCard(ev) {
ev.currentTarget.classList.toggle('is-switched');
}
Here I created an event listener on a parent element to the cards. No matter how many cards there are this event will be fired.
When you click either the header or one of the parent divs the nearest <div class="card"> will be found. Now the class name can be toggled for that element.
// event listener for any of the cards
document.getElementById('cardwrapper').addEventListener('click', e => {
// what card was clicked
let card = e.target.closest('.card');
// toogle class name 'is-switched'
card.classList.toggle('is-switched');
});
$card-transition-time: 0.5s;
.card {
perspective: 600px;
position: relative;
cursor: pointer;
}
.card.is-switched .card__wrapper {
animation: rotate .5s linear both;
}
.card__wrapper {
transform-style: preserve-3d;
animation: rotate-inverse .5s linear both;
}
.card__side {
backface-visibility: hidden;
position: absolute;
top: 0;
left: 0;
width: 100%;
}
.card__side.is-active {
position: static;
}
.card__side--back {
transform: rotateY(180deg);
}
#keyframes rotate {
0% {
transform: rotateY(0);
}
70% {
transform: rotateY(200deg);
}
100% {
transform: rotateY(180deg);
}
}
#keyframes rotate-inverse {
0% {
transform: rotateY(180deg);
}
70% {
transform: rotateY(-20deg);
}
100% {
transform: rotateY(0);
}
}
// ignore
* {
box-sizing: border-box;
}
body {
background-color: #fff;
text-align: center;
padding: 1.5rem;
}
h1,
h2 {
margin: 0;
}
.card {
margin: 2rem auto;
max-width: 350px;
}
.card__side {
padding: 1em;
border-radius: 5px;
color: white;
background-color: #ff4136;
}
.card__side--back {
background-color: #0074d9;
}
<div id="cardwrapper">
<div class="card" id="item1">
<div class="card__wrapper">
<div class="card__side is-active">
<h1>Card 1</h1>
</div>
<div class="card__side card__side--back">
<h2>Card 1</h2>
</div>
</div>
</div>
<div class="card" id="item2">
<div class="card__wrapper">
<div class="card__side is-active">
<h1>Card 2</h1>
</div>
<div class="card__side card__side--back">
<h2>Card 2</h2>
</div>
</div>
</div>
</div>
I am working on my personal website currently. For the frontend I am using Vue.js with vue-router. Until today the transition worked fine, you can see here: https://imanuel.ulbricht.codes/ (you need to scroll down or use the arrow key down). But after some changes that I cannot really find it stopped working.
This is how it "works" now: https://imanuel-not-working.ulbricht.codes/. I really have no idea what I changed, that might affect this change in behavior, maybe someone has an idea.
Side note: The published code has sourcemaps enabled so you should see the complete Vue.js source code.
Another point, the code should animate the page transition, that doesn't happen currently. After 2h searching I couldn't find the root of the problem, if anyone could give me a hint what code might be the cause I will add it to the question.
This is the code, that might cause the issue:
App.vue
<template>
<div class="ulbricht-app" id="app" #wheel="navigateWheel" #keyup.down="navigateNext" #keyup.up="navigatePrevious">
<ribbon v-if="!$route.meta.isHelloWorld"/>
<transition>
<div class="ulbricht-slice__top" :class="{ 'ulbricht-slice__hello-world': $route.meta.isHelloWorld }"></div>
</transition>
<NavIndicator v-if="!$route.meta.isHelloWorld"/>
<transition name="ulbricht-router__fade" mode="out-in">
<router-view/>
</transition>
<transition>
<div class="ulbricht-slice__bottom" v-if="!$route.meta.isHelloWorld"></div>
</transition>
</div>
</template>
<script>
export default {
components: {
NavIndicator,
Ribbon
},
name: 'App',
mounted() {
window.addEventListener('keyup', (event) => {
if (event.key === 'ArrowUp') {
this.navigatePrevious();
} else if (event.key === 'ArrowDown') {
this.navigateNext();
}
});
},
methods: {
navigateWheel($event) {
if ($event.deltaY > 0) {
this.navigateNext();
} else {
this.navigatePrevious();
}
},
navigateNext() {
if (this.$route.meta.next) {
this.$router.push(this.$route.meta.next);
}
},
navigatePrevious() {
if (this.$route.meta.previous) {
this.$router.push(this.$route.meta.previous);
}
}
}
};
</script>
<style lang="scss">
.ulbricht-app {
.ulbricht-slice__top {
background: var(--primary);
position: absolute;
width: 200%;
z-index: 0;
transition: transform 0.4s, height 0.4s;
height: 250px;
transform: skewY(-3deg);
left: 0;
top: -210px;
&.ulbricht-slice__hello-world {
transition: transform 0.4s, height 0.2s;
top: -25px;
height: 120%;
transform: skewY(15deg);
}
}
.ulbricht-router__fade-enter-active {
span,
p,
h1,
img {
transition: opacity 0.3s;
opacity: 1;
}
}
.ulbricht-router__fade-leave-active {
span,
p,
h1,
img {
transition: opacity 0.3s;
opacity: 0;
}
}
}
</style>
And one of the components, they basically all look the same just the content varies.
<template>
<div class="ulbricht-app ulbricht-app__hello">
<img class="ulbricht-hello__background" src="../assets/background.png" alt="">
<div class="ulbricht-hello__content">
<h1 class="ulbricht-hello__header">
Hello World, I am Imanuel
</h1>
<p class="ulbricht-hello__text">
What I do is dead simple, I write software, design websites and landscapes,<br/>
let me show you what I am capable of
</p>
<router-link class="ulbricht-chevron" :to="$route.meta.next">
<span class="ulbricht-chevron__button"></span>
</router-link>
</div>
</div>
</template>
<script>
export default {
name: 'HelloWorld'
};
</script>
And the NavIndicator component, I think after adding this component it stopped working, but I am not sure.
<template>
<div class="ulbricht-sidenav">
<router-link :to="$route.meta.previous || ''" class="ulbricht-sidenav__chevron is--up"
:class="{'is--disabled': !$route.meta.previous}"></router-link>
<router-link :to="nav" class="ulbricht-sidenav__dot" :class="{'is--active': $route.name === nav.name}"
:title="nav.meta.title" v-for="nav in navs"></router-link>
<router-link :to="$route.meta.next || ''" class="ulbricht-sidenav__chevron is--down"
:class="{'is--disabled': !$route.meta.next}"></router-link>
</div>
</template>
<script>
import Routes from "../router/Routes";
export default {
name: "NavIndicator",
computed: {
navs() {
return Routes;
}
}
}
</script>
<style lang="scss">
.ulbricht-sidenav {
position: fixed;
right: 1em;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
z-index: 9999;
}
.ulbricht-sidenav__chevron {
&::before {
border-style: solid;
border-width: 0.25em 0.25em 0 0;
content: '';
display: inline-block;
height: 0.45em;
left: 0.15em;
position: relative;
vertical-align: top;
width: 0.45em;
border-color: var(--primary);
margin-right: 0.3em;
}
&.is--disabled {
&::before {
border-color: var(--primary-grey);
}
}
&.is--up {
&::before {
transform: rotate(-45deg);
top: 0.5em;
}
}
&.is--down {
&::before {
top: 0;
transform: rotate(135deg);
}
}
}
.ulbricht-sidenav__dot {
border-radius: 50%;
width: 1em;
height: 1em;
border: 0.2em solid var(--primary-opaque-0\.5);
margin-top: 0.25em;
margin-bottom: 0.25em;
transition: border 0.3s, background 0.3s;
&.is--active {
background: var(--primary);
}
&:hover {
border: 0.2em solid var(--primary-opaque-0\.7);
background: var(--primary);
}
}
</style>
Removing this component, however, didn't have an effect.
As addition, here is the Github Link, so you can investigate all of the source code if I might have forgotten something, that might be relevant.
https://github.com/DerKnerd/imanuel.ulbricht.codes/tree/98272361549617191bb6d6f5d88ad88c94cbdcfe
There is a <!-- --> comment between
<div class="ulbricht-slice__top ulbricht-slice__hello-world"></div>
and
<div class="ulbricht-app ulbricht-app__hello">
on the copy of your site that is not working. Something is being rendered/removed there.
It may be causing a CSS selector to be applied incorrectly.