├── .gitignore ├── example ├── responsive │ ├── images │ │ ├── img_1.jpg │ │ ├── img_2.jpg │ │ ├── img_3.jpg │ │ └── README.md │ ├── example.dart │ ├── example.css │ └── index.html ├── simple │ ├── example.dart │ ├── example.css │ └── index.html ├── fullscreen │ ├── example.dart │ ├── example.css │ └── index.html ├── html-content │ ├── example.dart │ ├── example.css │ └── index.html ├── index.html └── methods-events │ ├── example.dart │ ├── example.css │ └── index.html ├── CHANGELOG.md ├── pubspec.yaml ├── tool └── drone_io.sh ├── LICENSE ├── README.md └── lib └── swiper.dart /.gitignore: -------------------------------------------------------------------------------- 1 | packages/ 2 | build/ 3 | pubspec.lock -------------------------------------------------------------------------------- /example/responsive/images/img_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcojakob/dart-swiper/HEAD/example/responsive/images/img_1.jpg -------------------------------------------------------------------------------- /example/responsive/images/img_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcojakob/dart-swiper/HEAD/example/responsive/images/img_2.jpg -------------------------------------------------------------------------------- /example/responsive/images/img_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcojakob/dart-swiper/HEAD/example/responsive/images/img_3.jpg -------------------------------------------------------------------------------- /example/simple/example.dart: -------------------------------------------------------------------------------- 1 | import 'dart:html'; 2 | import 'package:swiper/swiper.dart'; 3 | 4 | main() { 5 | // Initialize the Swiper. 6 | Swiper swiper = new Swiper(querySelector('.swiper')); 7 | } 8 | -------------------------------------------------------------------------------- /example/fullscreen/example.dart: -------------------------------------------------------------------------------- 1 | import 'dart:html'; 2 | import 'package:swiper/swiper.dart'; 3 | 4 | main() { 5 | // Initialize the Swiper. 6 | Swiper swiper = new Swiper(querySelector('.swiper')); 7 | } 8 | -------------------------------------------------------------------------------- /example/html-content/example.dart: -------------------------------------------------------------------------------- 1 | import 'dart:html'; 2 | import 'package:swiper/swiper.dart'; 3 | 4 | main() { 5 | // Initialize the Swiper. 6 | Swiper swiper = new Swiper(querySelector('.swiper')); 7 | } -------------------------------------------------------------------------------- /example/responsive/example.dart: -------------------------------------------------------------------------------- 1 | import 'dart:html'; 2 | import 'package:swiper/swiper.dart'; 3 | 4 | main() { 5 | // Initialize the Swiper. 6 | Swiper swiper = new Swiper(querySelector('.swiper'), autoHeightRatio: 0.666); 7 | } -------------------------------------------------------------------------------- /example/responsive/images/README.md: -------------------------------------------------------------------------------- 1 | # Image Sources 2 | 3 | The images are coypright (c) by Bart Hiddink. They are released 4 | under [CC BY 2.0](http://creativecommons.org/licenses/by/2.0/). The 5 | images can be found in this 6 | [Flickr stream](http://www.flickr.com/photos/zoutedrop/4023338872/in/photostream/). -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Version 0.0.3 (2015-03-09) 4 | 5 | * Update `dnd` dependency to v0.2.1. 6 | * Simplify deployment script. 7 | * Update readme with badges. 8 | 9 | 10 | ## Version 0.0.2 (2014-07-22) 11 | 12 | * Use `dnd` library for drag detection. 13 | 14 | 15 | ## Version 0.0.1 (2014-02-10) 16 | 17 | * Initial Version. -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: swiper 2 | version: 0.0.3 3 | author: Marco Jakob 4 | description: A touch and mouse slider for swiping through images and html. 5 | homepage: https://github.com/marcojakob/dart-swiper 6 | documentation: http://www.dartdocs.org/documentation/swiper/latest/ 7 | dependencies: 8 | browser: '>=0.10.0+2 <0.11.0' 9 | dnd: '>=0.2.1 <0.3.0' 10 | logging: '>=0.9.1 <0.10.0' 11 | dev_dependencies: 12 | bootstrap_for_pub: 3.1.0+2 13 | -------------------------------------------------------------------------------- /tool/drone_io.sh: -------------------------------------------------------------------------------- 1 | # ----------------------------------- 2 | # Script that builds Dart app and pushes it to gh-pages. 3 | # 4 | # Set following variables: 5 | # ----------------------------------- 6 | build_folder='example' 7 | github_repo='git@github.com:marcojakob/dart-swiper.git' 8 | 9 | # ----------------------------------- 10 | # Build. 11 | # ----------------------------------- 12 | pub install 13 | pub build ${build_folder} 14 | 15 | 16 | # ----------------------------------- 17 | # Configure git in build subfolder 18 | # ----------------------------------- 19 | cd build/${build_folder} 20 | git init 21 | git add . 22 | 23 | 24 | # ----------------------------------- 25 | # Deploy to github pages. 26 | # ----------------------------------- 27 | git commit -m 'deploy commit from drone' 28 | git push -f ${github_repo} master:gh-pages -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Dart Swiper Examples 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |

Dart Swiper Examples

17 |

18 | Here are some examples of how to use the Dart Swiper: 19 |

20 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /example/responsive/example.css: -------------------------------------------------------------------------------- 1 | .swiper { 2 | overflow: hidden; 3 | position: relative; 4 | visibility: hidden; /* Hide until layout is ready. */ 5 | border: 2px solid; 6 | width: 50%; 7 | float: left; 8 | margin-right: 10px; 9 | max-width: 500px; 10 | cursor: url(../packages/dnd/cursor/openhand.cur), move; /* Cursor for IE. */ 11 | cursor: url(../packages/dnd/cursor/openhand.cur) 7 5, move; /* Cursor for FF and Chrome (setting midpoint). */ 12 | } 13 | 14 | .page-container { 15 | position: relative; 16 | height: 100%; 17 | } 18 | 19 | .page { 20 | position: absolute; 21 | width: 100%; 22 | text-align: center; 23 | } 24 | 25 | 26 | /* Show other curser during drag. */ 27 | .swiper-dragging, .swiper-drag-occurring { 28 | cursor: url(../packages/dnd/cursor/closedhand.cur), move; /* Cursor for IE. */ 29 | cursor: url(../packages/dnd/cursor/closedhand.cur) 7 5, move; /* Cursor for FF and Chrome (setting midpoint). */ 30 | } 31 | 32 | 33 | /* Page Styling. */ 34 | .page img { 35 | max-width: 100%; 36 | height: auto; 37 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Marco Jakob (majakob@gmx.ch) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /example/methods-events/example.dart: -------------------------------------------------------------------------------- 1 | import 'dart:html'; 2 | import 'package:swiper/swiper.dart'; 3 | 4 | main() { 5 | // Initialize the Swiper. 6 | Swiper swiper = new Swiper(querySelector('.swiper'), speed: 600); 7 | 8 | // Set up method calls for button clicks. 9 | ButtonElement prevButton = querySelector('#previous-button') 10 | ..onClick.listen((_) => swiper.prev()); 11 | ButtonElement nextButton = querySelector('#next-button') 12 | ..onClick.listen((_) => swiper.next()); 13 | 14 | InputElement moveInput = querySelector('#move-input'); 15 | ButtonElement moveButton = querySelector('#move-button')..onClick.listen((_) { 16 | int index = int.parse(moveInput.value, onError: (txt) => 0); 17 | moveInput.value = index.toString(); 18 | swiper.moveToIndex(index); 19 | }); 20 | 21 | // Write events to TextArea. 22 | TextAreaElement events = querySelector('#events'); 23 | swiper.onPageChange.listen((index) { 24 | append(events, 'PageChange Event: index=${index}\n'); 25 | }); 26 | swiper.onPageTransitionEnd.listen((index) { 27 | append(events, 'TransitionEnd Event: index=${index}\n'); 28 | }); 29 | } 30 | 31 | void append(TextAreaElement textArea, String text) { 32 | textArea.appendText(text); 33 | textArea.scrollTop = textArea.scrollHeight; 34 | } -------------------------------------------------------------------------------- /example/fullscreen/example.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; 3 | } 4 | 5 | .swiper { 6 | overflow: hidden; 7 | position: relative; 8 | height: 100%; /* Declare the height of the swiper. */ 9 | visibility: hidden; /* Hide until layout is ready. */ 10 | width: 100%; 11 | cursor: url(../packages/dnd/cursor/openhand.cur), move; /* Cursor for IE. */ 12 | cursor: url(../packages/dnd/cursor/openhand.cur) 7 5, move; /* Cursor for FF and Chrome (setting midpoint). */ 13 | } 14 | 15 | .page-container { 16 | position: relative; 17 | height: 100%; 18 | } 19 | 20 | .page { 21 | position: absolute; 22 | width: 100%; 23 | height: 100%; 24 | } 25 | 26 | .swiper-dragging, .swiper-drag-occurring { 27 | cursor: url(../packages/dnd/cursor/closedhand.cur), move; /* Cursor for IE. */ 28 | cursor: url(../packages/dnd/cursor/closedhand.cur) 7 5, move; /* Cursor for FF and Chrome (setting midpoint). */ 29 | } 30 | 31 | /* Page Styling. */ 32 | .page { 33 | color: white; 34 | } 35 | 36 | .page a, .page h1 small { 37 | color: #CCC; 38 | } 39 | 40 | .page1 { 41 | background-color: #00A0B0; 42 | } 43 | 44 | .page2 { 45 | background-color: #6A4A3C; 46 | } 47 | 48 | .page3 { 49 | background-color: #CC333F; 50 | } 51 | 52 | .page4 { 53 | background-color: #EB6841; 54 | } 55 | 56 | .page5 { 57 | background-color: #EDC951; 58 | } 59 | 60 | .big { 61 | margin-top: 100px; 62 | font-size: 60px; 63 | text-align: center; 64 | } -------------------------------------------------------------------------------- /example/simple/example.css: -------------------------------------------------------------------------------- 1 | .swiper { 2 | overflow: hidden; 3 | position: relative; 4 | height: 333px; /* Declare the height of the swiper. */ 5 | visibility: hidden; /* Hide until layout is ready. */ 6 | width: 500px; 7 | border: 2px solid; 8 | cursor: url(../packages/dnd/cursor/openhand.cur), move; /* Cursor for IE. */ 9 | cursor: url(../packages/dnd/cursor/openhand.cur) 7 5, move; /* Cursor for FF and Chrome (setting midpoint). */ 10 | } 11 | 12 | .page-container { 13 | position: relative; 14 | height: 100%; 15 | } 16 | 17 | .page { 18 | position: absolute; 19 | width: 100%; 20 | height: 100%; 21 | } 22 | 23 | 24 | /* Show other curser during drag. */ 25 | .swiper-dragging, .swiper-drag-occurring { 26 | cursor: url(../packages/dnd/cursor/closedhand.cur), move; /* Cursor for IE. */ 27 | cursor: url(../packages/dnd/cursor/closedhand.cur) 7 5, move; /* Cursor for FF and Chrome (setting midpoint). */ 28 | } 29 | 30 | 31 | /* Page Styling. */ 32 | .page { 33 | color: white; 34 | font-size: 100px; 35 | text-align: center; 36 | display: table; 37 | } 38 | 39 | .page p { 40 | display: table-cell; 41 | vertical-align: middle; 42 | } 43 | 44 | .page1 { 45 | background-color: #00A0B0; 46 | } 47 | 48 | .page2 { 49 | background-color: #6A4A3C; 50 | } 51 | 52 | .page3 { 53 | background-color: #CC333F; 54 | } 55 | 56 | .page4 { 57 | background-color: #EB6841; 58 | } 59 | 60 | .page5 { 61 | background-color: #EDC951; 62 | } -------------------------------------------------------------------------------- /example/html-content/example.css: -------------------------------------------------------------------------------- 1 | .swiper { 2 | overflow: hidden; 3 | position: relative; 4 | height: 400px; /* Declare the height of the swiper. */ 5 | visibility: hidden; /* Hide until layout is ready. */ 6 | border: 2px solid; 7 | cursor: url(../packages/dnd/cursor/openhand.cur), move; /* Cursor for IE. */ 8 | cursor: url(../packages/dnd/cursor/openhand.cur) 7 5, move; /* Cursor for FF and Chrome (setting midpoint). */ 9 | } 10 | 11 | .page-container { 12 | position: relative; 13 | height: 100%; 14 | } 15 | 16 | .page { 17 | position: absolute; 18 | width: 100%; 19 | height: 100%; 20 | } 21 | 22 | 23 | /* Show other curser during drag. */ 24 | .swiper-dragging, .swiper-drag-occurring { 25 | cursor: url(../packages/dnd/cursor/closedhand.cur), move; /* Cursor for IE. */ 26 | cursor: url(../packages/dnd/cursor/closedhand.cur) 7 5, move; /* Cursor for FF and Chrome (setting midpoint). */ 27 | } 28 | 29 | 30 | /* Page Styling. */ 31 | .page { 32 | color: white; 33 | padding: 0 15px; 34 | } 35 | 36 | .page1 { 37 | background-color: #00A0B0; 38 | font-size: 50px; 39 | text-align: center; 40 | } 41 | 42 | .page2 { 43 | background-color: #6A4A3C; 44 | } 45 | 46 | .page3 { 47 | background-color: #CC333F; 48 | } 49 | 50 | .page3 > .row > div { 51 | background-color: #EB6841; 52 | border: 1px solid #00A0B0; 53 | margin-bottom: 10px; 54 | padding: 5px; 55 | } 56 | 57 | .page4 { 58 | background-color: #EB6841; 59 | } 60 | 61 | .page5 { 62 | background-color: #EDC951; 63 | font-size: 20px; 64 | } -------------------------------------------------------------------------------- /example/methods-events/example.css: -------------------------------------------------------------------------------- 1 | .swiper { 2 | overflow: hidden; 3 | position: relative; 4 | height: 200px; /* Declare the height of the swiper. */ 5 | visibility: hidden; /* Hide until layout is ready. */ 6 | border: 2px solid; 7 | cursor: url(../packages/dnd/cursor/openhand.cur), move; /* Cursor for IE. */ 8 | cursor: url(../packages/dnd/cursor/openhand.cur) 7 5, move; /* Cursor for FF and Chrome (setting midpoint). */ 9 | } 10 | 11 | .page-container { 12 | position: relative; 13 | height: 100%; 14 | } 15 | 16 | .page { 17 | position: absolute; 18 | width: 100%; 19 | height: 100%; 20 | } 21 | 22 | 23 | /* Show other curser during drag. */ 24 | .swiper-dragging, .swiper-drag-occurring { 25 | cursor: url(../packages/dnd/cursor/closedhand.cur), move; /* Cursor for IE. */ 26 | cursor: url(../packages/dnd/cursor/closedhand.cur) 7 5, move; /* Cursor for FF and Chrome (setting midpoint). */ 27 | } 28 | 29 | 30 | /* Page Styling. */ 31 | .page { 32 | color: white; 33 | font-size: 100px; 34 | text-align: center; 35 | display: table; 36 | } 37 | 38 | .page p { 39 | display: table-cell; 40 | vertical-align: middle; 41 | } 42 | 43 | .page1 { 44 | background-color: #00A0B0; 45 | } 46 | 47 | .page2 { 48 | background-color: #6A4A3C; 49 | } 50 | 51 | .page3 { 52 | background-color: #CC333F; 53 | } 54 | 55 | .page4 { 56 | background-color: #EB6841; 57 | } 58 | 59 | .page5 { 60 | background-color: #EDC951; 61 | } 62 | 63 | 64 | .form-control { 65 | display: inline-block; 66 | width: auto; 67 | } 68 | -------------------------------------------------------------------------------- /example/fullscreen/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Dart Swiper: Fullscreen Example 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |
21 |
22 |
23 | 26 | 27 |

28 | This example shows how to do fullscreen swiping. 29 |

30 |

31 | 32 | 33 | Example Source on GitHub 34 |

35 | 36 |

Swipe →

37 |
38 |
39 |
40 |

1

41 |
42 |
43 |

2

44 |
45 |
46 |

3

47 |
48 |
49 |

4

50 |
51 |
52 |
53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /example/simple/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Dart Swiper: Simple Example 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 22 | 23 |

24 | This example shows basic use of the Dart Swiper. 25 |

26 |

27 | 28 | 29 | Example Source on GitHub 30 |

31 | 32 |

Demo

33 | 34 | 35 |
36 |
37 |
38 |

0

39 |
40 |
41 |

1

42 |
43 |
44 |

2

45 |
46 |
47 |

3

48 |
49 |
50 |

4

51 |
52 |
53 |
54 | 55 | 56 |

Description

57 | 84 |
85 | 86 | 87 | -------------------------------------------------------------------------------- /example/methods-events/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Dart Swiper: Method and Events Example 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 22 |

23 | This example shows how to use the Swiper's methods and events. 24 |

25 |

26 | 27 | 28 | Example Source on GitHub 29 |

30 | 31 | 32 |

Demo

33 | 34 | 35 |
36 |
37 |
38 |

0

39 |
40 |
41 |

1

42 |
43 |
44 |

2

45 |
46 |
47 |

3

48 |
49 |
50 |

4

51 |
52 |
53 |
54 | 55 | 56 | 57 |

Methods

58 |

59 | 60 | 61 |

62 |
63 | 64 | 65 |
66 | 67 |

Events

68 | 69 |

70 | Note: There are more events than those demonstrated here, like 71 | onDragStart, onDrag, onDragEnd. 72 |

73 | 74 | 75 |

Description

76 | 77 | 110 | 111 |
112 | 113 | 114 | -------------------------------------------------------------------------------- /example/responsive/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Dart Swiper: Responsive Images Example 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 21 |

22 | This example shows how to use Swiper with responsive images. Whenever the 23 | browser is resized, the Swiper is automatically adjusted. 24 |

25 |

26 | 27 | 28 | Example Source on GitHub 29 |

30 | 31 | 32 |

Demo

33 | 34 | 35 |
36 |
37 |
38 | img 39 |
40 |
41 | img 42 |
43 |
44 | img 45 |
46 |
47 | img 48 |
49 |
50 | img 51 |
52 |
53 | img 54 |
55 |
56 | img 57 |
58 |
59 | img 60 |
61 |
62 | img 63 |
64 |
65 | img 66 |
67 |
68 | img 69 |
70 |
71 |
72 | 73 | 74 |

75 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. 76 |

77 | 78 | 79 |

Description

80 | 81 | 108 |
109 | 110 | 111 | -------------------------------------------------------------------------------- /example/html-content/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Dart Swiper: HTML Content Example 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 22 |

23 | This example shows how to use the Swiper with any HTML content. 24 |

25 |

26 | 27 | 28 | Example Source on GitHub 29 |

30 | 31 | 32 |

Demo

33 | 34 | 35 |
36 |
37 |
38 |

Swipe to see some HTML content →

39 |
40 |
41 |

Look, an HTML Form

42 |
43 |
44 | 45 | 46 |
47 |
48 | 49 | 50 |
51 |
52 | 53 | 54 |

Example block-level help text here.

55 |
56 |
57 | 60 |
61 | 62 |
63 |
64 |
65 |

Bootstrap Grid

66 |
67 |
.col-sm-6
68 |
.col-sm-6
69 |
70 |
71 |
.col-sm-8
72 |
.col-sm-4
73 |
74 |
75 |
.col-sm-4
76 |
.col-sm-4
77 |
.col-sm-4
78 |
79 |
80 |
81 |

A Table

82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 |
#First NameLast NameUsername
1MickeyMousemick
2BieneMajamaja
3MarcoJakobmmm
112 |
113 |
114 |

Feedback?

115 |

If you have problems with the Swiper please go to the 116 | Swiper 117 | GitHub Repository and report an 118 | Issue. 119 |

120 |

Hope you have fun swiping.

121 |

Marco

122 |
123 |
124 |
125 | 126 | 127 | 128 |

Description

129 | 130 | 161 | 162 |
163 | 164 | 165 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Swiper 2 | 3 | A touch (and mouse) slider for swiping through images and HTML. 4 | 5 | [![Star this Repo](https://img.shields.io/github/stars/marcojakob/dart-swiper.svg?style=flat-square)](https://github.com/marcojakob/dart-swiper) 6 | [![Pub Package](https://img.shields.io/pub/v/swiper.svg?style=flat-square)](https://pub.dartlang.org/packages/swiper) 7 | [![Build Status](https://drone.io/github.com/marcojakob/dart-swiper/status.png)](https://drone.io/github.com/marcojakob/dart-swiper/latest) 8 | 9 | [GitHub](https://github.com/marcojakob/dart-swiper) | 10 | [Pub](https://pub.dartlang.org/packages/swiper) | 11 | [Demos and Examples](http://marcojakob.github.io/dart-swiper/) 12 | 13 | 14 | ## Browser Support 15 | 16 | Swiper supports all [browsers supported by Dart 1.6 and later](https://www.dartlang.org/support/faq.html#browsers-and-compiling-to-javascript): 17 | 18 | * Internet Explorer, versions 10, and 11. 19 | * Firefox, latest version. 20 | * Chrome, latest version. 21 | * Safari for desktop, version 6. 22 | * Safari for mobile, version 6. 23 | 24 | 25 | ## Features 26 | 27 | * **Touch and Mouse.** Slide either on a touch screen or with the mouse. 28 | * **Smooth Transitions.** Swiper uses [Hardware-accelerated CSS3 transitions](http://blog.teamtreehouse.com/increase-your-sites-performance-with-hardware-accelerated-css) 29 | for smooth animations. 30 | * **Auto Resizing.** When the browser window is resized or a mobile device is 31 | rotated, the swiper and all its pages are resized automatically. 32 | * **Scroll Prevention.** Swiper detects if the user tries to slide or tries to 33 | scroll vertically. 34 | * **Images or HTML.** Swiper supports any HTML content for swipe pages. 35 | 36 | 37 | ## Demos 38 | 39 | * [Simple Example](http://marcojakob.github.io/dart-swiper/simple/) 40 | * [Responsive Images Example](http://marcojakob.github.io/dart-swiper/responsive/) 41 | * [Method and Events Example](http://marcojakob.github.io/dart-swiper/methods-events/) 42 | * [HTML Content Example](http://marcojakob.github.io/dart-swiper/html-content/) 43 | * [Fullscreen Example](http://marcojakob.github.io/dart-swiper/fullscreen/) 44 | 45 | *All Demos can also be found in the `example` folder.* 46 | 47 | 48 | ## Usage 49 | 50 | ### 1. HTML 51 | 52 | Swiper needs a simple HTML structure. Here is an example: 53 | 54 | ```HTML 55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | ``` 63 | 64 | * The `swiper` is the main container. This will become the viewport. 65 | * The `page-container` is the container that wraps all pages. 66 | * The inner `div`s are the slide pages and can contain any HTML content. 67 | 68 | 69 | ### 2. Initialize the Swiper 70 | 71 | In the Dart code you initialize the Swiper with a single line. The main 72 | container needs to be passed to the `Swiper` constructor. 73 | 74 | ```Dart 75 | Swiper swiper = new Swiper(querySelector('.swiper')); 76 | ``` 77 | 78 | 79 | ### 3. CSS 80 | 81 | A few styles are needed: 82 | 83 | ```CSS 84 | .swiper { 85 | overflow: hidden; 86 | position: relative; 87 | height: 333px; /* Declare the height of the swiper. */ 88 | visibility: hidden; /* Hide until layout is ready. */ 89 | } 90 | 91 | .page-container { 92 | position: relative; 93 | height: 100%; 94 | } 95 | 96 | .page-container > div { 97 | position: absolute; 98 | width: 100%; 99 | } 100 | ``` 101 | 102 | 103 | ## Constructor Options 104 | 105 | * `startIndex`: the index position the Swiper should start at - *default: 0* 106 | * `speed`: the speed of prev and next transitions in milliseconds - 107 | *default: 300* 108 | * `autoWidth`: defines if the Swiper should automatically adjust the width when 109 | the browser is resized. You might want to disable this if you want 110 | to manually do some width and height calculations. If disabled, make sure 111 | you call the `resize` method after the size of the Swiper changed - *default: true* 112 | * `autoHeightRatio` defines if and how the Swiper should calculate the 113 | height. If defined, the height is calculated from the Swiper's width with 114 | `autoHeightRatio` and automatically applied when the browser is resized. 115 | This is useful, e.g. for responsive images - *default: null* 116 | * `distanceThreshold`: If swipe distance is more than the `distanceTreshold` 117 | (in px), a swipe is detected (regardless of swipe duration) - *default: 20* 118 | * `durationThreshold`: If swipe duration is less than the `durationTreshold` 119 | (in ms), a swipe is detected (regardless of swipe distance) - *default: 250* 120 | * `handle`: If handle query String is specified, it restricts the dragging from 121 | starting unless it occurs on the specified element(s). Only elements that 122 | descend from the `swiperElement` are permitted - 123 | *default: null* 124 | * `cancel`: If cancel query String is specified, drag starting is prevented on 125 | specified elements - 126 | *default: input, textarea, button, select, option* 127 | * `draggingClass`: Is the CSS class set to the `swiperElement` while a user is 128 | dragging. If set to `null`, no such CSS class is added - 129 | *default: swiper-dragging* 130 | * `draggingClassBody`: Is the CSS class set to the html `body` tag while a user is 131 | dragging. If set to `null`, no such CSS class is added - 132 | *default: swiper-drag-occurring* 133 | 134 | 135 | #### Example 136 | 137 | ```Dart 138 | Swiper swiper = new Swiper(querySelector('.swiper'), 139 | startIndex: 2, 140 | speed: 600, 141 | autoWidth: true, 142 | autoHeightRatio: 0.66); 143 | ``` 144 | 145 | 146 | ## Methods 147 | 148 | * `currentIndex`: The current page index. 149 | * `currentPage`: The HTML element of the current page. 150 | * `moveToIndex(int index, {int speed, bool noEvents: false}`: Moves to the page 151 | at the specified *index*. Optionally, the *speed* of the transition can be 152 | specified. If no events (pageChange and pageTransitionEnd) should be fired, 153 | the *noEvents* flag can be set to true. 154 | * `next({int speed})`: Moves to the next page. 155 | * `prev({int speed})`: Moves to the previous page. 156 | * `hasNext()`: Returns true if there is a next page. 157 | * `hasPrev()`: Returns true if there is a previous page. 158 | * `resize()`: Updates the cached page width and the container sizes. The resize 159 | method is automatically called when the browser is resized. But if the Swiper 160 | is resized other than through browser resizing, the *resize* method must be 161 | called manually. 162 | * `destroy()`: Unistalls all listeners. This will return the swiper element 163 | back to its pre-init state. 164 | 165 | 166 | #### Example 167 | 168 | ```Dart 169 | // Set up method calls for button clicks. 170 | ButtonElement prevButton = querySelector('#previous-button') 171 | ..onClick.listen((_) => swiper.prev()); 172 | ButtonElement nextButton = querySelector('#next-button') 173 | ..onClick.listen((_) => swiper.next()); 174 | ``` 175 | 176 | ## Events 177 | 178 | * `onPageChange`: Fired when the current page changed. The data of the event is 179 | the page index. 180 | * `onPageTransitionEnd`: Fired when the transition ends after a page change. 181 | Note: If the user swipes again before the previous transition ended, this 182 | event is only fired once, at the end of all page transitions. The data of the 183 | event is the page index. 184 | * `onDragStart`: Fired when the user starts dragging. 185 | * `onDrag`: Fired periodically throughout the drag operation. 186 | * `onDragEnd`: Fired when the user ends the dragging. 187 | 188 | 189 | #### Example 190 | 191 | ```Dart 192 | swiper.onPageChange.listen((index) { 193 | print('PageChange Event: index=${index}'); 194 | }); 195 | ``` 196 | 197 | ## Attribution 198 | 199 | Swiper is heavily inspiried by [flipsnap.js](https://github.com/pxgrid/js-flipsnap/), 200 | [swipe.js](https://github.com/bradbirdsall/Swipe), and 201 | [SwipeView](https://github.com/cubiq/SwipeView). 202 | 203 | Many thanks to the authors for those great JavaScript projects! 204 | 205 | 206 | ## License 207 | The MIT License (MIT) -------------------------------------------------------------------------------- /lib/swiper.dart: -------------------------------------------------------------------------------- 1 | library swiper; 2 | 3 | import 'dart:html'; 4 | import 'dart:async'; 5 | import 'package:logging/logging.dart'; 6 | import 'package:dnd/dnd.dart'; 7 | 8 | final _log = new Logger('swiper'); 9 | 10 | /** 11 | * A touch and mouse slider for swiping through images and html. 12 | */ 13 | class Swiper { 14 | 15 | // ------------------- 16 | // Events 17 | // ------------------- 18 | StreamController _onPageChange; 19 | StreamController _onPageTransitionEnd; 20 | 21 | /** 22 | * Fired when the current page changed. 23 | * 24 | * The data is the page index. To get the page, use [currentPage]. 25 | */ 26 | Stream get onPageChange { 27 | if (_onPageChange == null) { 28 | _onPageChange = new StreamController.broadcast( 29 | onCancel: () => _onPageChange = null); 30 | } 31 | return _onPageChange.stream; 32 | } 33 | 34 | /** 35 | * Fired when the transition ends after a page change. If the user swipes 36 | * again before the previous transition ended, this event is only fired once, 37 | * at the end of all page transitions. 38 | * 39 | * The data is the page index. To get the page, use [currentPage]. 40 | */ 41 | Stream get onPageTransitionEnd { 42 | if (_onPageTransitionEnd == null) { 43 | _onPageTransitionEnd = new StreamController.broadcast( 44 | onCancel: () => _onPageTransitionEnd = null); 45 | } 46 | return _onPageTransitionEnd.stream; 47 | } 48 | 49 | /** 50 | * Fired when the user starts dragging. 51 | * 52 | * Note: The [onDragStart] is fired not on touchStart or mouseDown but as 53 | * soon as there is a drag movement. When a drag is started an [onDrag] event 54 | * will also be fired. 55 | */ 56 | Stream get onDragStart => _draggable.onDragStart; 57 | 58 | /** 59 | * Fired periodically throughout the drag operation. 60 | */ 61 | Stream get onDrag => _draggable.onDrag; 62 | 63 | /** 64 | * Fired when the user ends the dragging. 65 | * 66 | * Is also fired when the user clicks the 'esc'-key or the window loses focus. 67 | */ 68 | Stream get onDragEnd => _draggable.onDragEnd; 69 | 70 | 71 | // ------------------- 72 | // Private Properties 73 | // ------------------- 74 | /// The main swiper element. 75 | final Element _swiperElement; 76 | 77 | /// The container for all swipe pages (child of [_swiperElement]). 78 | Element _containerElement; 79 | 80 | /// Speed of prev and next transitions in milliseconds. 81 | final int _speed; 82 | 83 | /// If true the 84 | final bool _autoWidth; 85 | 86 | /// The aspect ratio of the Swiper if it should be automatically applied. 87 | final double _autoHeightRatio; 88 | 89 | /// If swipe distance is more than [_distanceThreshold] (in px), a swipe is 90 | /// detected (regardless of swipe duration). 91 | int _distanceThreshold; 92 | 93 | /// If swipe duration is less than [_durationThreshold] (in ms), a swipe is 94 | /// detected (regardless of swipe distance). 95 | int _durationThreshold; 96 | 97 | /// The [Draggable] used to track drags on the [_containerElement]. 98 | Draggable _draggable; 99 | 100 | /// Stops the time between a drag start and a drag end to determine if the 101 | /// duration was below the [_durationThreshold]. 102 | Stopwatch _dragStopwatch; 103 | 104 | /// Width of one slide. 105 | int _pageWidth; 106 | 107 | /// The current page index. 108 | int _currentIndex; 109 | 110 | /// If true, the next transitionEnd event will be fired, if false, the 111 | /// transitionEnd events will be ignored. Without this flag the transitionEnd 112 | /// would also fire when there was an animation with no index change. 113 | bool _fireNextPageTransitionEnd = false; 114 | 115 | /// Tracks listener subscriptions. 116 | List _subs = []; 117 | 118 | /** 119 | * Creates a new [Swiper]. 120 | * 121 | * The [swiperElement] must contain a child element that is the **container** 122 | * of all swipe pages. 123 | * 124 | * ## Options 125 | * 126 | * [startIndex] is the index position the Swiper should start at. 127 | * 128 | * [speed] is the speed of prev and next transitions in milliseconds. 129 | * 130 | * [autoWidth] defines if the Swiper should automatically adjust the width 131 | * when the browser is resized. You might want to disable this if you want 132 | * to manually do some width and height calculations. If disabled make sure 133 | * you call [resize] after the size of the Swiper changed. 134 | * 135 | * [autoHeightRatio] defines if and how the Swiper should calculate the 136 | * height. If defined, the height is calculated from the Swiper's width with 137 | * [autoHeightRatio] and automatically applied when the browser is resized. 138 | * This is useful, e.g. for responsive images. 139 | * 140 | * If a [handle] query String is specified, it restricts the dragging from 141 | * starting unless it occurs on the specified element(s). Only elements that 142 | * descend from [swiperElement] are permitted. 143 | * 144 | * If [cancel] query String is specified, drag starting is prevented on 145 | * specified elements. 146 | * 147 | * If swipe distance is more than [distanceThreshold] (in px), a swipe is 148 | * detected (regardless of swipe duration). 149 | * 150 | * If swipe duration is less than [durationThreshold] (in ms), a swipe is 151 | * detected (regardless of swipe distance). 152 | * 153 | * The [draggingClass] is the css class set to the [swiperElement] 154 | * while a user is dragging. If set to null, no such css class is added. 155 | * 156 | * The [draggingClassBody] is the css class set to the html body tag 157 | * while a user is dragging. If set to null, no such css class is added. 158 | */ 159 | Swiper(Element swiperElement, 160 | { int startIndex: 0, 161 | int speed: 300, 162 | bool autoWidth: true, 163 | double autoHeightRatio: null, 164 | int distanceThreshold: 20, 165 | int durationThreshold: 250, 166 | String handle: null, 167 | String cancel: 'input, textarea, button, select, option', 168 | String draggingClass: 'swiper-dragging', 169 | String draggingClassBody: 'swiper-drag-occurring'}) 170 | 171 | : this._swiperElement = swiperElement, 172 | this._speed = speed, 173 | this._autoWidth = autoWidth, 174 | this._autoHeightRatio = autoHeightRatio, 175 | this._distanceThreshold = distanceThreshold, 176 | this._durationThreshold = durationThreshold { 177 | 178 | _log.fine('Initializing Swiper'); 179 | 180 | // Get the page-container. 181 | _containerElement = _swiperElement.children[0]; 182 | 183 | // Validate and set the start index. 184 | _currentIndex = _getNextValidIndex(startIndex); 185 | 186 | // Horizontally stack pages inside the container. 187 | _initPages(_currentIndex); 188 | 189 | // Resize for the first time. 190 | resize(); 191 | 192 | // We're ready, set to visible. 193 | _swiperElement.style.visibility = 'visible'; 194 | 195 | // Set up the Draggable on the swiper. 196 | _draggable = new Draggable(_swiperElement, 197 | handle: handle, 198 | cancel: cancel, 199 | draggingClass: draggingClass, 200 | draggingClassBody: draggingClassBody, 201 | horizontalOnly: true); // Swiping is only done horizontally. 202 | 203 | // Listen for drag events. 204 | _subs.add(_draggable.onDragStart.listen(_handleDragStart)); 205 | _subs.add(_draggable.onDrag.listen(_handleDrag)); 206 | _subs.add(_draggable.onDragEnd.listen(_handleDragEnd)); 207 | 208 | // Install transitionEnd listener. 209 | _subs.add(_containerElement.onTransitionEnd.listen((_) { 210 | // Remove css animation transition style. 211 | _removeCssTransition(); 212 | 213 | if (_fireNextPageTransitionEnd) { 214 | _firePageTransitionEndEvent(); 215 | } 216 | })); 217 | 218 | if (_autoWidth) { 219 | // Install browser resize listener. This is done asynchronously after the 220 | // visibility has been applied, because setting visibility would somethimes 221 | // trigger a resize event. 222 | new Future(() { 223 | _subs.add(window.onResize.listen((_) => resize())); 224 | }); 225 | } 226 | } 227 | 228 | /** 229 | * Initializes the pages and shows the page at [startIndex]. 230 | */ 231 | void _initPages(int startIndex) { 232 | // Stack the pages horizontally with the `left` css attribute of 100%, 200%, 233 | // 300%, etc. 234 | for (int i = 0; i < _containerElement.children.length; i++) { 235 | _containerElement.children[i].style.left = '${i}00%'; 236 | } 237 | 238 | // Move to the start index. 239 | _translatePercentX(-startIndex * 100); 240 | } 241 | 242 | /** 243 | * Fires an [onPageTransitionEnd] event. 244 | */ 245 | void _firePageTransitionEndEvent() { 246 | // Fire the page transition end event. 247 | _log.fine('Transition end event: index=$_currentIndex'); 248 | if (_onPageTransitionEnd != null) { 249 | _onPageTransitionEnd.add(_currentIndex); 250 | } 251 | _fireNextPageTransitionEnd = false; 252 | } 253 | 254 | /** 255 | * The current page index. 256 | */ 257 | int get currentIndex => _currentIndex; 258 | 259 | /** 260 | * The current page. 261 | */ 262 | Element get currentPage => _containerElement.children[currentIndex]; 263 | 264 | /** 265 | * The main swiper element. 266 | */ 267 | Element get swiperElement => _swiperElement; 268 | 269 | /** 270 | * The container for all swipe pages (child of [swiperElement]). 271 | */ 272 | Element get containerElement => _containerElement; 273 | 274 | /** 275 | * Moves to the page at [index]. 276 | * 277 | * The [speed] is the duration of the transition in milliseconds. 278 | * If no [speed] is provided, the speed attribute of this [Swiper] is used. 279 | * 280 | * If [noEvents] is set to true, no [onPageChange] and [onPageTransitionEnd] 281 | * events are fired. 282 | */ 283 | void moveToIndex(int index, {int speed, bool noEvents: false}) { 284 | // Validate index. 285 | index = _getNextValidIndex(index); 286 | 287 | // Use default speed if none was provided. 288 | if (speed == null) { 289 | speed = _speed; 290 | } 291 | 292 | _log.fine('Moving to index: index=$index, speed=$speed'); 293 | 294 | // Do the move with or without animation. 295 | if (speed > 0) { 296 | _moveToIndexAnim(index, speed); 297 | } else { 298 | _moveToIndexNoAnim(index); 299 | } 300 | 301 | // Fire events if there was a move to a new index. 302 | if (index != _currentIndex && !noEvents) { 303 | // Fire page change event. 304 | _log.fine('Page change event: index=$index'); 305 | if (_onPageChange != null) { 306 | _onPageChange.add(index); 307 | } 308 | 309 | // Ensure that the page transition end event is fired. 310 | if (speed > 0) { 311 | // At the end of the animation there will be a transitionEnd event. 312 | // Set the flag that the next transitionEnd event triggers a new 313 | // pageTransitionEnd event. 314 | _fireNextPageTransitionEnd = true; 315 | 316 | } else { 317 | // As there was no animation, no transitionEndEvent will be fired by the 318 | // browser. So we need to manually call the fire method here. 319 | _firePageTransitionEndEvent(); 320 | } 321 | } 322 | 323 | // Set the new current index. 324 | _currentIndex = index; 325 | } 326 | 327 | /** 328 | * Moves to [index] with an animation. 329 | * 330 | * [speed] defines the duration of the animation. [speed] must be > 0. 331 | */ 332 | void _moveToIndexAnim(int index, int speed) { 333 | _addCssTransition(speed); 334 | _translatePercentX(index * -100); 335 | } 336 | 337 | /** 338 | * Moves to [index] with no animation. 339 | */ 340 | void _moveToIndexNoAnim(int index) { 341 | _translatePercentX(index * -100); 342 | } 343 | 344 | /** 345 | * Moves the current page to the specified [offset]. 346 | */ 347 | void _moveToOffset(int offset) { 348 | _translatePixelX(_currentIndex * -_pageWidth + offset); 349 | } 350 | 351 | /** 352 | * Moves to the next page. 353 | */ 354 | void next({int speed}) { 355 | if (hasNext()) { 356 | moveToIndex(currentIndex + 1, speed: speed); 357 | } 358 | } 359 | 360 | /** 361 | * Moves to the previous page. 362 | */ 363 | void prev({int speed}) { 364 | if (hasPrev()) { 365 | moveToIndex(currentIndex - 1, speed: speed); 366 | } 367 | } 368 | 369 | /** 370 | * Returns true if there is a next page. 371 | */ 372 | bool hasNext() { 373 | return currentIndex < _containerElement.children.length - 1; 374 | } 375 | 376 | /** 377 | * Returns true if there is a previous page. 378 | */ 379 | bool hasPrev() { 380 | return currentIndex > 0; 381 | } 382 | 383 | /** 384 | * Updates the cached page width and the container sizes. 385 | * 386 | * The [resize] method is automatically called when the browser is resized. 387 | * But if the [Swiper] is resized other than trough browser resizing, [resize] 388 | * must be called manually. 389 | */ 390 | void resize() { 391 | // Get the swiper's contentWidth. The contentWidth is a ROUNDEN pixel value. 392 | _pageWidth = _swiperElement.contentEdge.width.round(); 393 | 394 | // We set the ROUNDED width to the container element. This is important, 395 | // otherwise a floating point value might get passed down to the 396 | // pages because they are 100%-width. A floating point width of the pages 397 | // would cause rounding errors. 398 | _containerElement.style.width = '${_pageWidth}px'; 399 | 400 | if (_autoHeightRatio != null) { 401 | // Calculate the height with the provided aspect ratio. 402 | _containerElement.style.height = '${(_pageWidth * _autoHeightRatio).floor()}px'; 403 | } 404 | } 405 | 406 | /** 407 | * Unistalls all listeners. This will return the swiper element back to its 408 | * pre-init state. 409 | */ 410 | void destroy() { 411 | _subs.forEach((sub) => sub.cancel()); 412 | _subs.clear(); 413 | _draggable.destroy(); 414 | _draggable = null; 415 | } 416 | 417 | /** 418 | * Handles drag start. 419 | */ 420 | void _handleDragStart(DraggableEvent dragEvent) { 421 | // Start the stopwatch. 422 | _dragStopwatch = new Stopwatch()..start(); 423 | 424 | // Ensure there is no transition animation style during the drag operation. 425 | _removeCssTransition(); 426 | } 427 | 428 | /** 429 | * Handles drag. 430 | */ 431 | void _handleDrag(DraggableEvent dragEvent) { 432 | int deltaX = _calcDragDelta(dragEvent.startPosition.x, dragEvent.position.x); 433 | 434 | _log.finest('Drag: deltaX=$deltaX'); 435 | 436 | // Increases resistance if necessary. 437 | deltaX = _addResistance(deltaX); 438 | 439 | // Translate to new x-position. 440 | _moveToOffset(deltaX); 441 | } 442 | 443 | /** 444 | * Handles drag end. 445 | */ 446 | void _handleDragEnd(DraggableEvent dragEvent) { 447 | // Stop the stopwatch. 448 | _dragStopwatch.stop(); 449 | 450 | int index = _currentIndex; 451 | int dragDelta = _calcDragDelta(dragEvent.startPosition.x, dragEvent.position.x); 452 | 453 | // Determine if the drag leads to a new index. If the event was cancelled 454 | // we go back to last index. Otherwise, we check if thresholds for new 455 | // index were met. 456 | if (!dragEvent.cancelled) { 457 | _log.finest('DragEnd: dragDelta=$dragDelta'); 458 | 459 | // Determine if we are past the thresholds. 460 | if (dragDelta.abs() > _distanceThreshold || 461 | _dragStopwatch.elapsedMilliseconds < _durationThreshold) { 462 | 463 | // Direction of swipe. Dragging left means revealing page on the right! 464 | bool dragLeft = dragDelta < 0; 465 | 466 | if (dragLeft && hasNext()) { 467 | index++; 468 | } else if (!dragLeft && hasPrev()){ 469 | index--; 470 | } 471 | } 472 | } 473 | 474 | // Adjust the speed to the distance left for the move animation. 475 | int animDistance; 476 | if (index != _currentIndex) { 477 | // New index: Calc distance left. 478 | animDistance = (_pageWidth - dragDelta.abs()).abs(); 479 | } else { 480 | // Back to old index: Just reverse drag distance. 481 | animDistance = dragDelta.abs(); 482 | } 483 | int adjustedSpeed = _adjustSpeed(_speed, animDistance); 484 | 485 | // Move to index (might be the same as the current index). 486 | moveToIndex(index, speed: adjustedSpeed); 487 | } 488 | 489 | /** 490 | * If [index] is out of bounds, the nearest valid index is returned. 491 | * If [index] is already valid, it is just returned again. 492 | */ 493 | int _getNextValidIndex(int index) { 494 | // Ensure left bound. 495 | if (index < 0) { 496 | return 0; 497 | } 498 | 499 | // Ensure is in right bound. 500 | if (index > _containerElement.children.length - 1) { 501 | return _containerElement.children.length - 1; 502 | } 503 | 504 | // Was already valid, just return index. 505 | return index; 506 | } 507 | 508 | /** 509 | * Calculates the delta movement between [startX] and [endX] coordinates. 510 | * 511 | * * Result > 0 means dragging right, possible revealing a slide on the left. 512 | * * Result < 0 means dragging left, possible revealing a slide on the right. 513 | */ 514 | int _calcDragDelta(int startX, int endX) { 515 | return endX - startX; 516 | } 517 | 518 | /** 519 | * Adds move resistance if first page and sliding left or last page and 520 | * sliding right. 521 | */ 522 | int _addResistance(int offset) { 523 | bool firstPage = !hasPrev(); 524 | bool lastPage = !hasNext(); 525 | 526 | if ( (firstPage && offset > 0) || (lastPage && offset < 0) ) { 527 | // Add resistance. 528 | return offset ~/ (offset.abs() / _pageWidth + 1); 529 | } else { 530 | // No resistance. 531 | return offset; 532 | } 533 | } 534 | 535 | /** 536 | * Helper method to adjusts the [speed] proportionally to the [distance]. 537 | * The [speed] corresponds to the distance of one page ([_swiperWidth]). 538 | */ 539 | int _adjustSpeed(int speed, num distance) { 540 | if (distance > _pageWidth) { 541 | return speed; 542 | } 543 | 544 | return (speed / _pageWidth * distance).round(); 545 | } 546 | 547 | /** 548 | * Sets the transform translate property to the [xPercent] value. 549 | */ 550 | void _translatePercentX(int xPercent) { 551 | _containerElement.style.transform = 'translate3d(${xPercent}%, 0, 0)'; 552 | } 553 | 554 | /** 555 | * Sets the transform translate property to the [xPixel] value. 556 | */ 557 | void _translatePixelX(int xPixel) { 558 | // Unsing `translate3d` to activate GPU hardware-acceleration (a bit of a hack). 559 | _containerElement.style.transform = 'translate3d(${xPixel}px, 0, 0)'; 560 | } 561 | 562 | /** 563 | * Adds the css transition style for the [_containerElement]. The transition is 564 | * for an ease-out animation with duration of [speed]. 565 | */ 566 | void _addCssTransition(int speed) { 567 | _containerElement.style 568 | ..transitionProperty = 'transform' 569 | ..transitionDuration = '${speed}ms' 570 | ..transitionTimingFunction = 'ease-out'; 571 | } 572 | 573 | /** 574 | * Removes the css transition style from [_containerElement]. 575 | */ 576 | void _removeCssTransition() { 577 | _containerElement.style.transition = null; 578 | } 579 | } 580 | 581 | 582 | 583 | 584 | --------------------------------------------------------------------------------