mirror of
https://github.com/flutter/samples.git
synced 2025-11-09 06:18:49 +00:00
committed by
GitHub
parent
7c5fecbceb
commit
c60f02d7c3
205
web/samples_index/lib/src/carousel.dart
Normal file
205
web/samples_index/lib/src/carousel.dart
Normal file
@@ -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,6 +124,7 @@ 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
|
||||||
}
|
}
|
||||||
@@ -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,6 +397,7 @@ 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user