Angular闪卡滑动切换问题:实现双向滑入滑出效果
实现Angular闪卡双向滑入滑出切换效果
你的核心问题在于只用单个卡片容器,无法同时展示当前卡片滑出和新卡片从另一侧滑入的双向动画。下面是具体的修改方案,通过双卡片容器叠加的方式实现流畅的双向切换效果:
1. HTML结构修改
添加两个卡片容器,分别对应当前显示的卡片和即将进入的卡片,通过Angular条件渲染控制它们的显示与动画状态:
<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-viewport"> <!-- 当前显示的卡片 --> <div class="flashcard-wrapper" ng-class="{ 'slide-out-right': flashCtrl.slideOutRight, 'slide-out-left': flashCtrl.slideOutLeft }" ng-if="!flashCtrl.isNewCardActive"> <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> <!-- 即将进入的新卡片 --> <div class="flashcard-wrapper new-card" ng-class="{ 'slide-in-left': flashCtrl.slideInLeft, 'slide-in-right': flashCtrl.slideInRight }" ng-if="flashCtrl.isNewCardActive"> <div class="flashcard" ng-click="flashCtrl.flipCard()"> <div class="front"> <p>{{ flashCtrl.flashcards[flashCtrl.nextCardIndex].frontContent }}</p> </div> <div class="back"> <p>{{ flashCtrl.flashcards[flashCtrl.nextCardIndex].backContent }}</p> </div> </div> </div> </div> <button class="navigation-btn next" ng-disabled="flashCtrl.currentCardIndex === flashCtrl.flashcards.length - 1" ng-click="flashCtrl.nextCard()">Next</button> </div>
2. CSS样式调整
修改容器布局实现卡片重叠,新增双向滑入滑出的动画类:
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-viewport { width: 50%; height: 60vh; margin: auto; position: relative; overflow: hidden; } .flashcard-wrapper { width: 100%; height: 100%; position: absolute; top: 0; left: 0; transition: transform 0.6s ease; } /* 新卡片初始位置在视口右侧外 */ .new-card { transform: translateX(150%); } .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-out-right { transform: translateX(150%); } .slide-out-left { transform: translateX(-150%); } /* 新卡片滑入动画 */ .slide-in-left { transform: translateX(0); } .slide-in-right { transform: translateX(0); }
3. 控制器逻辑更新
新增状态变量控制新卡片的显示和动画时序,确保双向动画同步执行:
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.nextCardIndex = 0; // 存储即将显示的卡片索引 this.animating = false; this.isNewCardActive = false; // 控制新卡片是否显示 this.slideOutRight = false; this.slideOutLeft = false; this.slideInLeft = false; this.slideInRight = false; this.flipCard = function() { if (!this.animating) { const activeIndex = this.isNewCardActive ? this.nextCardIndex : this.currentCardIndex; this.flashcards[activeIndex].isFlipped = !this.flashcards[activeIndex].isFlipped; } }; this.nextCard = function() { if (this.currentCardIndex < this.flashcards.length - 1 && !this.animating) { this.animating = true; this.nextCardIndex = this.currentCardIndex + 1; this.isNewCardActive = true; // 触发当前卡片右滑出、新卡片左滑入的动画 this.slideOutRight = true; this.slideInLeft = true; $timeout(() => { // 动画结束后更新状态 this.slideOutRight = false; this.slideInLeft = false; this.currentCardIndex = this.nextCardIndex; this.isNewCardActive = false; // 切换后默认显示正面 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.nextCardIndex = this.currentCardIndex - 1; this.isNewCardActive = true; // 触发当前卡片左滑出、新卡片右滑入的动画 this.slideOutLeft = true; this.slideInRight = true; $timeout(() => { // 动画结束后更新状态 this.slideOutLeft = false; this.slideInRight = false; this.currentCardIndex = this.nextCardIndex; this.isNewCardActive = false; // 切换后默认显示正面 this.flashcards[this.currentCardIndex].isFlipped = false; $timeout(() => this.animating = false, 100); }, 600); } }; });
关键改进点
- 双卡片叠加布局,实现当前卡片滑出与新卡片滑入的同步动画
- 拆分独立的动画状态变量,精准控制滑出/滑入方向
- 严格的动画时序控制,避免内容切换与动画不同步导致的视觉错乱
- 保留原卡片翻转功能,同时在切换时自动重置为正面显示
内容的提问来源于stack exchange,提问作者Jacob
相关产品推荐
相关产品推荐

