Issue
Background Info
I am making a flashcard web app with Angular and I am having some difficulties getting the flashcards to slide in and out of view. The app allows users to add flashcards via input's that are then saved as cookies. The user can then navigate through flashcards using the next/previous buttons and remove flashcards using the Remove Current Card button. When the user clicks on the currently displayed flashcard, it flips it over and displays the content on the back. When the user navigates between flashcards, regardless of whether they last left the card on the front or back side, they always appear on the front side of the card.
Issue
As of right now, there is only one physical html element that is considered the flashcard. Upon clicking next/previous, this flashcard then slides out of view, is replaced with the new cards content, and slides back into view from the same side it exited. Instead, when the user presses the next/previous button, I want the card to slide out of view on the appropriate side, and the "new card" to slide in from the opposite side.
I've tried a couple different approaches, including having an additional card on either side of the actual card, and various css transitions and animations but have been unable to achieve this smoothly.
Code
For the purposes of this snippet, I've removed the user's functionality to dynamically add and remove flashcards.
var app = angular.module('flashcardApp', []);
app.controller('FlashcardController', function($scope, $timeout) {
this.flashcards = [{
frontContent: 'Front of Card 1',
backContent: 'Back of Card 1',
isFlipped: false
},
{
frontContent: 'Front of Card 2',
backContent: 'Back of Card 2',
isFlipped: false
},
{
frontContent: 'Front of Card 3',
backContent: 'Back of Card 3',
isFlipped: false
},
{
frontContent: 'Front of Card 4',
backContent: 'Back of Card 4',
isFlipped: false
}
];
this.currentCardIndex = 0;
this.animating = false;
this.flipCard = function() {
if (!this.animating) {
this.flashcards[this.currentCardIndex].isFlipped = !this.flashcards[this.currentCardIndex].isFlipped;
}
};
this.nextCard = function() {
if (this.currentCardIndex < this.flashcards.length - 1 && !this.animating) {
this.animating = true;
this.slideRight = true;
$timeout(() => {
this.slideRight = false;
this.currentCardIndex++;
this.flashcards[this.currentCardIndex].isFlipped = false;
$timeout(() => this.animating = false, 100);
}, 600);
}
};
this.previousCard = function() {
if (this.currentCardIndex > 0 && !this.animating) {
this.animating = true;
this.slideLeft = true;
$timeout(() => {
this.slideLeft = false;
this.currentCardIndex--;
this.flashcards[this.currentCardIndex].isFlipped = false;
$timeout(() => this.animating = false, 100);
}, 600);
}
};
});
body,
html {
height: 100%;
margin: 0;
font-family: 'Arial', sans-serif;
background-color: #f7f7f7;
color: #444;
}
.flashcard-container {
perspective: 1000px;
width: 100%;
height: 100%;
display: flex;
align-items: center;
position: relative;
}
.flashcard-wrapper {
width: 50%;
height: 60vh;
margin: auto;
position: relative;
z-index: 1;
transition: transform 0.6s ease;
}
.flashcard {
width: 100%;
height: 100%;
border-radius: 15px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
background-color: #ffffff;
border: 1px solid #e0e0e0;
position: relative;
transform-style: preserve-3d;
cursor: pointer;
user-select: none;
}
.flashcard,
.front,
.back {
transition: transform 0.6s ease;
}
.flashcard.flipped {
transform: rotateY(180deg);
}
.front,
.back {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
border-radius: 15px;
display: flex;
justify-content: center;
align-items: center;
font-size: 24px;
padding: 20px;
box-sizing: border-box;
}
.back {
transform: rotateY(180deg);
background-color: #eeeeee;
}
.navigation-btn {
padding: 10px 20px;
margin: 20px;
border: none;
border-radius: 5px;
background-color: #2c3e50;
color: white;
font-size: 16px;
cursor: pointer;
transition: background-color 0.3s ease;
position: absolute;
z-index: 2;
}
.navigation-btn.previous {
left: 10px;
top: 50%;
transform: translateY(-50%);
}
.navigation-btn.next {
right: 10px;
top: 50%;
transform: translateY(-50%);
}
.slide-left {
transform: translateX(-150%);
}
.slide-right {
transform: translateX(150%);
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script>
<div class="flashcard-container" ng-app="flashcardApp" ng-controller="FlashcardController as flashCtrl">
<button class="navigation-btn previous" ng-disabled="flashCtrl.currentCardIndex === 0" ng-click="flashCtrl.previousCard()">Previous</button>
<div class="flashcard-wrapper" ng-class="{ 'slide-left': flashCtrl.slideLeft, 'slide-right': flashCtrl.slideRight }">
<div class="flashcard" ng-class="{ flipped: flashCtrl.flashcards[flashCtrl.currentCardIndex].isFlipped }" ng-click="flashCtrl.flipCard()">
<div class="front" ng-show="!flashCtrl.flashcards[flashCtrl.currentCardIndex].isFlipped">
<p>{{ flashCtrl.flashcards[flashCtrl.currentCardIndex].frontContent }}</p>
</div>
<div class="back" ng-show="flashCtrl.flashcards[flashCtrl.currentCardIndex].isFlipped">
<p>{{ flashCtrl.flashcards[flashCtrl.currentCardIndex].backContent }}</p>
</div>
</div>
</div>
<button class="navigation-btn next" ng-disabled="flashCtrl.currentCardIndex === flashCtrl.flashcards.length - 1" ng-click="flashCtrl.nextCard()">Next</button>
</div>
As you can see in the snippet above, the flashcards are exiting and re-entering from the same side, whereas I want them to exit and enter from opposite sides in a similar fashion to an image carousel. Any advice is greatly appreciated!
Solution
I have figured out a solution that seems to be working... I am now sliding the card in the desired direction, removing the transition effect (transform 0.6s ease
) and then repositioning the card to the opposite side of the screen and then calling the opposite sliding effect. I have noticed once in a while though it will glitch or behave slightly differently, so if anyone has an alternative solution I would love to hear it!
var app = angular.module('flashcardApp', []);
app.controller('FlashcardController', function($scope, $timeout) {
this.flashcards = [{
frontContent: 'Front of Card 1',
backContent: 'Back of Card 1',
isFlipped: false
},
{
frontContent: 'Front of Card 2',
backContent: 'Back of Card 2',
isFlipped: false
},
{
frontContent: 'Front of Card 3',
backContent: 'Back of Card 3',
isFlipped: false
},
{
frontContent: 'Front of Card 4',
backContent: 'Back of Card 4',
isFlipped: false
}
];
this.currentCardIndex = 0;
this.animating = false;
this.flipCard = function() {
if (!this.animating) {
this.flashcards[this.currentCardIndex].isFlipped = !this.flashcards[this.currentCardIndex].isFlipped;
}
};
this.nextCard = function() {
if (this.currentCardIndex < this.flashcards.length - 1 && !this.animating) {
this.animating = true;
var wrapper = angular.element(document.querySelector('.flashcard-wrapper'));
this.slideLeft = true;
$timeout(() => {
this.currentCardIndex++;
this.flashcards[this.currentCardIndex].isFlipped = false;
this.slideLeft = false;
this.hideCard = true;
wrapper.css('transition', 'none');
$timeout(() => {
this.hideCard = false;
this.slideRight = true;
$timeout(() => {
wrapper.css('transition', 'transform 0.6s ease');
$timeout(() => {
this.slideRight = false;
this.animating = false;
}, 600);
}, 20);
}, 20);
}, 600);
}
};
this.previousCard = function() {
if (this.currentCardIndex > 0 && !this.animating) {
this.animating = true;
var wrapper = angular.element(document.querySelector('.flashcard-wrapper'));
this.slideRight = true;
$timeout(() => {
this.currentCardIndex--;
this.flashcards[this.currentCardIndex].isFlipped = false;
this.slideRight = false;
this.hideCard = true;
wrapper.css('transition', 'none');
$timeout(() => {
this.hideCard = false;
this.slideLeft = true;
$timeout(() => {
wrapper.css('transition', 'transform 0.6s ease');
$timeout(() => {
this.slideLeft = false;
this.animating = false;
}, 600);
}, 20);
}, 20);
}, 600);
}
};
});
body,
html {
height: 100%;
margin: 0;
font-family: 'Arial', sans-serif;
background-color: #f7f7f7;
color: #444;
}
.flashcard-container {
perspective: 1000px;
width: 100%;
height: 100%;
display: flex;
align-items: center;
position: relative;
}
.flashcard-wrapper {
width: 50%;
height: 60vh;
margin: auto;
position: relative;
z-index: 1;
transition: transform 0.6s ease;
}
.flashcard {
width: 100%;
height: 100%;
border-radius: 15px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
background-color: #ffffff;
border: 1px solid #e0e0e0;
position: relative;
transform-style: preserve-3d;
cursor: pointer;
user-select: none;
}
.flashcard,
.front,
.back {
transition: transform 0.6s ease;
}
.flashcard.flipped {
transform: rotateY(180deg);
}
.front,
.back {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
border-radius: 15px;
display: flex;
justify-content: center;
align-items: center;
font-size: 24px;
padding: 20px;
box-sizing: border-box;
}
.back {
transform: rotateY(180deg);
background-color: #eeeeee;
}
.navigation-btn {
padding: 10px 20px;
margin: 20px;
border: none;
border-radius: 5px;
background-color: #2c3e50;
color: white;
font-size: 16px;
cursor: pointer;
transition: background-color 0.3s ease;
position: absolute;
z-index: 2;
}
.navigation-btn.previous {
left: 10px;
top: 50%;
transform: translateY(-50%);
}
.navigation-btn.next {
right: 10px;
top: 50%;
transform: translateY(-50%);
}
.slide-left {
transform: translateX(-150%);
}
.slide-right {
transform: translateX(150%);
}
.hidden-card {
position: absolute;
left: -100%;
z-index: -1;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script>
<div class="flashcard-container" ng-app="flashcardApp" ng-controller="FlashcardController as flashCtrl">
<button class="navigation-btn previous" ng-disabled="flashCtrl.currentCardIndex === 0" ng-click="flashCtrl.previousCard()">Previous</button>
<div class="flashcard-wrapper" ng-class="{ 'hidden-card': flashCtrl.hideCard, 'slide-left': flashCtrl.slideLeft, 'slide-right': flashCtrl.slideRight }">
<div class="flashcard" ng-class="{ flipped: flashCtrl.flashcards[flashCtrl.currentCardIndex].isFlipped }" ng-click="flashCtrl.flipCard()">
<div class="front" ng-show="!flashCtrl.flashcards[flashCtrl.currentCardIndex].isFlipped">
<p>{{ flashCtrl.flashcards[flashCtrl.currentCardIndex].frontContent }}</p>
</div>
<div class="back" ng-show="flashCtrl.flashcards[flashCtrl.currentCardIndex].isFlipped">
<p>{{ flashCtrl.flashcards[flashCtrl.currentCardIndex].backContent }}</p>
</div>
</div>
</div>
<button class="navigation-btn next" ng-disabled="flashCtrl.currentCardIndex === flashCtrl.flashcards.length - 1" ng-click="flashCtrl.nextCard()">Next</button>
</div>
Answered By - Jacob
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.