Implement carousel (#605)

Add type declaration
pull/608/head
Sashika Nawarathne 4 years ago committed by GitHub
parent 7c5fecbceb
commit c60f02d7c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,205 @@
// Copyright 2020 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file
import 'dart:html';
class Carousel {
final bool withArrowKeyControl;
final Element container = querySelector('.slider-container');
final List<Element> slides = querySelectorAll('.slider-single');
int currentSlideIndex, lastSlideIndex;
Element prevSlide, currentSlide, nextSlide;
Carousel.init({this.withArrowKeyControl = false}) {
lastSlideIndex = slides.length - 1;
currentSlideIndex = -1;
_hideSlides();
_initBullets();
_initArrows();
if (withArrowKeyControl) {
_initArrowKeyControl();
}
// Move to the first slide after init
// This is responsible for creating a smooth animation
Future.delayed(Duration(milliseconds: 500)).then((value) => _slideRight());
}
void _hideSlides() {
slides.forEach((s) {
s.classes.add('next-hidden');
});
}
void _initBullets() {
final bulletContainer = DivElement();
bulletContainer.classes.add('bullet-container');
for (var i = 0; i < slides.length; i++) {
final bullet = DivElement();
bullet.classes.add('bullet');
bullet.id = 'bullet-index-$i';
bullet.onClick.listen((e) => _goToIndexSlide(i));
bulletContainer.append(bullet);
}
container.append(bulletContainer);
}
void _initArrows() {
final prevArrow = AnchorElement();
final iPrev = DivElement();
iPrev.classes.addAll(['fa', 'fa-chevron-left', 'fa-lg']);
prevArrow.classes.add('slider-left');
prevArrow.append(iPrev);
prevArrow.onClick.listen((e) => _slideLeft());
final nextArrow = AnchorElement();
final iNext = DivElement();
iNext.classes.addAll(['fa', 'fa-chevron-right', 'fa-lg']);
nextArrow.classes.add('slider-right');
nextArrow.append(iNext);
nextArrow.onClick.listen((e) => _slideRight());
container.append(prevArrow);
container.append(nextArrow);
}
void _updateBullets() {
final bullets =
querySelector('.bullet-container').querySelectorAll('.bullet');
for (var i = 0; i < bullets.length; i++) {
bullets[i].classes.remove('active');
if (i == currentSlideIndex) {
bullets[i].classes.add('active');
}
}
_checkRepeat();
}
void _checkRepeat() {
var prevArrow = querySelector('.slider-left');
var nextArrow = querySelector('.slider-right');
if (currentSlideIndex == slides.length - 1) {
slides[0].classes.add('hidden');
slides[slides.length - 1].classes.remove('hidden');
prevArrow.classes.remove('hidden');
nextArrow.classes.add('hidden');
} else if (currentSlideIndex == 0) {
slides[slides.length - 1].classes.add('hidden');
slides[0].classes.remove('hidden');
prevArrow.classes.add('hidden');
nextArrow.classes.remove('hidden');
} else {
slides[slides.length - 1].classes.remove('hidden');
slides[0].classes.remove('hidden');
prevArrow.classes.remove('hidden');
nextArrow.classes.remove('hidden');
}
}
void _slideRight() {
if (currentSlideIndex < lastSlideIndex) {
currentSlideIndex++;
} else {
currentSlideIndex = 0;
}
if (currentSlideIndex > 0) {
prevSlide = slides[currentSlideIndex - 1];
} else {
prevSlide = slides[lastSlideIndex];
}
currentSlide = slides[currentSlideIndex];
if (currentSlideIndex < lastSlideIndex) {
nextSlide = slides[currentSlideIndex + 1];
} else {
nextSlide = slides[0];
}
slides.forEach((e) {
_removeSlideClasses([e]);
if (e.classes.contains('prev-hidden')) e.classes.add('next-hidden');
if (e.classes.contains('prev')) e.classes.add('prev-hidden');
});
_removeSlideClasses([prevSlide, currentSlide, nextSlide]);
prevSlide.classes.add('prev');
currentSlide.classes.add('active');
nextSlide.classes.add('next');
_updateBullets();
}
void _slideLeft() {
if (currentSlideIndex > 0) {
currentSlideIndex--;
} else {
currentSlideIndex = lastSlideIndex;
}
if (currentSlideIndex < lastSlideIndex) {
nextSlide = slides[currentSlideIndex + 1];
} else {
nextSlide = slides[0];
}
currentSlide = slides[currentSlideIndex];
if (currentSlideIndex > 0) {
prevSlide = slides[currentSlideIndex - 1];
} else {
prevSlide = slides[lastSlideIndex];
}
slides.forEach((e) {
_removeSlideClasses([e]);
if (e.classes.contains('next')) e.classes.add('next-hidden');
if (e.classes.contains('next-hidden')) e.classes.add('prev-hidden');
});
_removeSlideClasses([prevSlide, currentSlide, nextSlide]);
prevSlide.classes.add('prev');
currentSlide.classes.add('active');
nextSlide.classes.add('next');
_updateBullets();
}
void _goToIndexSlide(index) {
final sliding =
(currentSlideIndex < index) ? () => _slideRight() : () => _slideLeft();
while (currentSlideIndex != index) {
sliding();
}
}
void _removeSlideClasses(List<Element> slides) {
slides.forEach((s) {
s.classes
.removeAll(['prev-hidden', 'prev', 'active', 'next', 'next-hidden']);
});
}
void _initArrowKeyControl() {
Element.keyUpEvent.forTarget(document.body).listen((e) {
if (e.keyCode == KeyCode.LEFT && currentSlideIndex > 0) {
_slideLeft();
}
if (e.keyCode == KeyCode.RIGHT && currentSlideIndex < lastSlideIndex) {
_slideRight();
}
});
}
}

@ -51,6 +51,7 @@ String _descriptionHeader = '''
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<script src="packages/mdc_web/material-components-web.min.js"></script> <script src="packages/mdc_web/material-components-web.min.js"></script>
<script src="https://kit.fontawesome.com/16cc04762e.js"></script>
<script defer src="description.dart.js"></script> <script defer src="description.dart.js"></script>
</head> </head>
'''; ''';
@ -171,9 +172,11 @@ String _descriptionPage(Sample sample) => '''
</div> </div>
</div> </div>
</div> </div>
<div class="screenshots"> <div class="slider-container">
<div class="slider-content">
${util.indent(_descriptionScreenshots(sample), 4)} ${util.indent(_descriptionScreenshots(sample), 4)}
</div> </div>
</div>
<div class="description"> <div class="description">
${util.indent(_descriptionText(sample), 4)} ${util.indent(_descriptionText(sample), 4)}
</div> </div>
@ -218,7 +221,8 @@ String _tags(Sample sample) {
String _descriptionScreenshots(Sample sample) { String _descriptionScreenshots(Sample sample) {
var buf = StringBuffer(); var buf = StringBuffer();
for (var screenshot in sample.screenshots) { for (var screenshot in sample.screenshots) {
buf.write('<img src="${screenshot.url}" alt="${screenshot.alt}" />\n'); buf.write(
'''<div class="slider-single"><img class="slider-single-image" src="${screenshot.url}" alt="${screenshot.alt}" /></div>\n''');
} }
return buf.toString(); return buf.toString();
} }

@ -1,9 +1,15 @@
import 'dart:html'; import 'dart:html';
import 'package:mdc_web/mdc_web.dart'; import 'package:mdc_web/mdc_web.dart';
import 'package:samples_index/src/carousel.dart';
InputElement searchInput; InputElement searchInput;
void main() { void main() {
querySelectorAll('.mdc-card__primary-action').forEach((el) => MDCRipple(el)); querySelectorAll('.mdc-card__primary-action').forEach((el) => MDCRipple(el));
// Initialize carousel
// This carousel will use the div slider-container as the base
// withArrowKeyControl is used to listen for arrow key up events
Carousel.init(withArrowKeyControl: true);
} }

@ -41,6 +41,29 @@ $font: Roboto, sans-serif;
$title-font: Google Sans Display, Roboto, sans-serif; $title-font: Google Sans Display, Roboto, sans-serif;
$subtitle-font: Google Sans, Roboto, sans-serif; $subtitle-font: Google Sans, Roboto, sans-serif;
// Carousel Animation Configs
$time: 500ms;
$delay: $time/2;
$mode: cubic-bezier(0.17, 0.67, 0.55, 1.43);
@keyframes heartbeat {
0% {
transform: scale(0);
}
25% {
transform: scale(1.2);
}
50% {
transform: scale(1);
}
75% {
transform: scale(1.2);
}
100% {
transform: scale(1);
}
}
// CSS Reset // CSS Reset
html, body { html, body {
height: 100%; height: 100%;
@ -101,7 +124,8 @@ a {
// Custom Styles // Custom Styles
.content { .content {
min-height: 100%; min-height: 100%;
>.container {
> .container {
padding-bottom: $footer-height; padding-bottom: $footer-height;
} }
} }
@ -133,6 +157,7 @@ a {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
> * { > * {
margin: 0 8px 3px 8px; // adjusted vertically to align flutter logo text with "Samples" text margin: 0 8px 3px 8px; // adjusted vertically to align flutter logo text with "Samples" text
} }
@ -229,8 +254,8 @@ a {
img { img {
border-radius: 8px; border-radius: 8px;
margin: 0 8px 0 8px; margin: 0 8px 0 8px;
max-width:100%; max-width: 100%;
max-height:100%; max-height: 100%;
} }
} }
@ -354,6 +379,7 @@ a {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
h1 { h1 {
margin-right: 8px; margin-right: 8px;
} }
@ -361,6 +387,7 @@ a {
.tags-container { .tags-container {
max-width: 400px; max-width: 400px;
.tags-label { .tags-label {
color: $dark-text-color; color: $dark-text-color;
display: flex; display: flex;
@ -370,7 +397,8 @@ a {
text-transform: uppercase; text-transform: uppercase;
font-weight: bold; font-weight: bold;
margin-bottom: 6px; margin-bottom: 6px;
>span{
> span {
margin-left: 4px; margin-left: 4px;
} }
} }
@ -396,3 +424,143 @@ a {
} }
} }
} }
// Carousel Container
.slider-container {
position: relative;
margin: 0 auto;
width: 800px;
height: 600px;
max-width: 100%;
margin-top: -80px;
margin-bottom: -60px;
.bullet-container {
position: absolute;
bottom: 80px;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
.bullet {
margin-right: 8px;
&:last-child {
margin-right: 0px;
}
height: 8px;
width: 18px;
border-radius: 8px;
background-color: black;
opacity: 0.2;
cursor: pointer;
transition: 40ms ease;
&.active {
opacity: 1;
}
}
}
.slider-content {
position: relative;
left: 50%;
top: 50%;
width: 70%;
height: 60%;
transform: translate(-50%, -50%);
.slider-single {
position: absolute;
z-index: 0;
left: 0;
top: 0;
width: 100%;
height: 100%;
transition: z-index 0ms $delay;
.slider-single-image {
position: relative;
left: 0;
top: 0;
width: 100%;
height: 100%;
object-fit: contain;
transition: $time $mode;
transform: scale(0);
opacity: 0;
}
&.prev-hidden {
.slider-single-image {
transform: translateX(-50%) scale(0);
}
}
&.prev {
z-index: 1;
.slider-single-image {
opacity: 0.2;
transform: translateX(-25%) scale(0.8);
}
}
&.next {
z-index: 1;
.slider-single-image {
opacity: 0.2;
transform: translateX(25%) scale(0.8);
}
}
&.next-hidden {
.slider-single-image {
transform: translateX(50%) scale(0);
}
}
&.active {
z-index: 2;
.slider-single-image {
opacity: 1;
transform: translateX(0%) scale(1);
}
}
}
}
.slider-left {
position: absolute;
z-index: 3;
display: block;
right: 100%;
top: 50%;
color: black;
transform: translateY(-50%);
padding: 20px 20px;
margin-right: -2px;
cursor: pointer;
}
.slider-right {
position: absolute;
z-index: 3;
display: block;
left: 100%;
top: 50%;
color: black;
transform: translateY(-50%);
padding: 20px 20px;
margin-left: -2px;
cursor: pointer;
}
.hidden {
display: none !important;
}
}

Loading…
Cancel
Save