Please refer to
README.md
on our repository for most up-to-date information.
HTML component for showing carousel just like a film strip. It is lightweight and focus on performance and accessibility.
Although this component is built on top of React, it can be used outside of a React project.
This project scaffolding is from react-component-template.
Try out our demo at https://spyip.github.io/react-film/.
You can use react-film
to retrofit existing DOM element outside of a React project.
Every children in the target element will become an individual carousel item.
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>React Film</title>
<script src="https://unpkg.com/react-film@latest/umd/react-film.production.min.js"></script>
</head>
<body>
<div id="film">
<img alt="Cat 01" src="image/01.jpg" />
<img alt="Cat 02" src="image/02.jpg" />
<img alt="Cat 03" src="image/03.jpg" />
</div>
<script>
window.ReactFilm.retrofit(
document.getElementById('film'),
{ height: 316 }
);
</script>
</body>
</html>
First, import react-film
on your JSX file, namely BasicFilm
.
import BasicFilm from 'react-film';
Then, instantiate a component using BasicFilm
.
<BasicFilm height={ 316 }>
<img alt="Cat 01" src="image/01.jpg" />
<img alt="Cat 02" src="image/02.jpg" />
<img alt="Cat 03" src="image/03.jpg" />
</BasicFilm>
Note: we need to specify
height
here because there are no CSS rule one can use to hide scroll bars in Firefox.
When we design react-film
, customization is our top priority. From shallow to deep, we support these customizations:
- Toggle features (auto-center/dots/flipper/scrollbar)
- Simple styling each feature (dot size, flipper width, etc)
- Replace or enhance themeing for each feature
- Add new controls, including headless component like auto-play
- Rebuild the whole component, without forking the code
You can control <BasicFilm>
using props listed below.
Name | Default | Description |
---|---|---|
autoCenter |
true |
true will enable auto-center after scroll stopped for a second, otherwise, false |
autoHide |
true |
true to auto hide controls after pointer leave or scroll stopped, otherwise, false (surfaced from style set) |
dir |
'ltr' |
'ltr' for left-to-right mode. 'rtl' for right-to-left mode. |
flipperBlurFocusOnClick |
false |
true will force the carousel flippers to blur on click, otherwise, false |
height |
Height of the carousel, in number (required for Firefox) |
|
leftFlipperAriaLabel |
'left' |
aria-label attribute for the left flipper |
leftFlipperText |
'<' |
Text for the left flipper |
rightFlipperAriaLabel |
'right' |
aria-label attribute for the right flipper |
rightFlipperText |
'>' |
Text for the right flipper |
showDots |
true |
true to show dots below the carousel, otherwise, false |
showFlipper |
true |
true to show flippers (side buttons), otherwise, false |
showScrollBar |
true |
true to show scroll bar, otherwise, false |
styleSet.carousel |
'css-*' |
Class name for carousel component |
styleSet.dotsBox |
'css-*' |
Class name for dots container |
styleSet.dotsItem |
'css-*' |
Class name for every dot |
styleSet.leftFlipper |
'css-*' |
Class name for left flipper |
styleSet.rightFlipper |
'css-*' |
Class name for right flipper |
styleSet.scrollBarBox |
'css-*' |
Class name for scroll bar container |
styleSet.scrollBarHandler |
'css-*' |
Class name for scroll bar handler |
stylesRoot |
undefined |
Set the container node for component styles to be placed into. When set to undefined , will use document.head |
To better assist designer to style the component, we introduce style set, a clean way to override or replace existing styles. This help designers to create their unique styles without the need to overcome the effect of existing styles.
To start with the basic style set, just copy the following code:
.my-scroll-bar-class { background: Red; }
import BasicFilm, { createBasicStyleSet } from 'react-film';
const originalStyleSet = createBasicStyleSet();
const myStyleSet = {
...originalStyleSet,
scrollBarHandler: originalStyleSet.scrollBarHandler + ' my-scroll-bar-class'
};
export default props =>
<BasicFilm styleSet={ myStyleSet }>
<img alt="Cat 01" src="image/01.jpg" />
<img alt="Cat 02" src="image/02.jpg" />
<img alt="Cat 03" src="image/03.jpg" />
</BasicFilm>
If you are familiar with glamor
, you can also use it to style.
import { css } from 'glamor';
import BasicFilm, { createBasicStyleSet } from 'react-film';
const originalStyleSet = createBasicStyleSet();
const myStyleSet = {
...originalStyleSet,
scrollBarHandler: css(originalStyleSet.scrollBarHandler, { backgroundColor: 'Red' })
};
export default props =>
<BasicFilm styleSet={ myStyleSet }>
<img alt="Cat 01" src="image/01.jpg" />
<img alt="Cat 02" src="image/02.jpg" />
<img alt="Cat 03" src="image/03.jpg" />
</BasicFilm>
Sometimes, just increasing some paddings are more than enough for your styling need. When calling createBasicStyleSet(options)
, you can specify the following options:
Name | Default | Description |
---|---|---|
autoHide |
true |
Auto-hide controls |
cursor |
pointer |
Cursor style on dots and flippers |
dotBoxSize |
20 |
Hit box size of every dot |
dotSize |
6 |
Visible dot size |
flipperBoxWidth |
60 |
Hit box size of flippers |
flipperSize |
40 |
Visible flipper size (circle) |
scrollBarHeight |
8 |
Scroll bar handler height |
scrollBarMargin |
4 |
Margin around scroll bar |
Sometimes, CSS themeing is not enough for deep-customization. You may need to rebuild part of the carousel to achieve your customization goals.
Instead of forking our repository and building your carousel, you can rebuild react-film
using composer/context pattern.
You can start from copying the following code, an absolute minimal without any non-essential styles. Our <BasicFilm>
starts from here too.
import React from 'react';
import { AutoCenter, Composer, Dots, FilmStrip, Flipper, ScrollBar } from 'react-film';
export default ({ children }) =>
<Composer numItems={ React.Children.count(children) }>
<div>
<FilmStrip>
{ children }
</FilmStrip>
<ScrollBar />
<Flipper mode="left"><</Flipper>
<Flipper mode="right">></Flipper>
</div>
<Dots>
{ () => '.' }
</Dots>
<AutoCenter />
</Composer>
You can also copy
<BasicFilm>
as your base template. There are no logic in it, so you can easily rebase latest changes from us without breaking your code.
You can copy Flipper.js
to start creating your own control, or from the simpler version below:
import React from 'react';
import { Context } from 'react-film';
export default ({ mode }) =>
<Context.Consumer>
{ context =>
<button onClick={ mode === 'left' ? context.scrollOneLeft : context.scrollOneRight }>
{ mode === 'left' ? '<' : '>' }
</button>
}
</Context.Consumer>
To reduce an extra render callback, we now require deep-customization users to pass in numItems
prop to <Composer>
.
Maybe you want to create a new flipper to control the carousel, the context object provides an API for interfacing with the carousel.
Name | Type | Description |
---|---|---|
index |
number |
Index of the current item |
indexFraction |
number |
Index of the current item, in fraction (1.5 means 50% between 2nd and 3rd item) |
itemContainerRef |
React refs | Ref of the immediate parent element containing all scrollable items |
numItems |
number |
Number of items in the carousel |
scrollableRef |
React refs | Ref of the scrollable element, use for artificial scrolling and tracking scroll position |
scrollBarPercentage |
string |
Percentage of the scroll bar position |
scrollBarWidth |
string |
Width (in percentage) of the scroll bar, respective to its total content |
scrolling |
boolean |
true if the user is scrolling (debounced 500ms after last onScroll event), otherwise, false |
scrollOneLeft |
() => {} |
Helper function to scroll one item to the left |
scrollOneRight |
() => {} |
Helper function to scroll one item to the right |
scrollTo |
(({ index: number, indexFraction: number }) => {}) => {} |
Scroll to a specified index, see sample below |
If the context object lack of features you want to use, just tell us your idea.
scrollTo
is the core of the carousel. You call it to jump to specific items in the carousel. For example, to scroll one item to the left:
context.scrollTo(({ indexFraction }) => {
// indexFraction = 0 means it is on the first item
// indexFraction = 0.5 means it is 50% between the first item and the second item
return Math.ceil(indexFraction) - 1;
});
- Native horizontal scrolling
- Virtual scroll bar
- Show when hover
- Support touch scrolling
- Bring your own flipper
- Show only when overflow
- Show when hover
- Bring your own scrollbar
- Show only when overflow
- Show when hover
- Bring your own dots
- Show only when overflow
- Variable container width
- Users can resize the container width any time they want
- Variable item width:
- Users can resize item width any time they want
- Item may resize itself from time to time (consider when the image is not loaded)
- Minimal styling as possible, let user customize it
- Support keyboard left/right arrow (supported natively)
- Using without React
While you can dream about everything, engineering a piece of software that works great requires some opinions and it may not please everyone.
- Non-native scrolling
- Physics model are different across browsers
- Too many inputs need to be handled, difficult to build a good model
- Mouse with physical wheel (some browsers support SHIFT + wheel to scroll horizontally)
- Mouse with capacitive touch wheel, or trackpoint devices
- Precision Touchpad
- Touchpad without precision scrolling
- Pen input
- Xbox controller
- If items are focusable, it is difficult to scroll it into view because it is difficult to detect focus
- Infinite or "wrap-around" scrolling
- Does not play nice with browser native scrolling
Like us? Star us.
Want to make it better? File us an issue.
Don't like something you see? Submit a pull request.