Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -346,9 +346,33 @@ The slide tag represents each slide in the presentation. Giving a slide tag an `
|maxHeight| PropTypes.number | Used to set max dimensions of the Slide.
|maxWidth| PropTypes.number | Used to set max dimensions of the Slide.
|notes| PropTypes.string| Text which will appear in the presenter mode. Can be HTML.
|transition|PropTypes.array|Accepts `slide`, `zoom`, `fade` or `spin`, and can be combined. Sets the slide transition. **Note: If you use the 'scale' transition, fitted text won't work in Safari.**|
|transition|PropTypes.array|Accepts `slide`, `zoom`, `fade`, `spin`, or a [function](#transition-function), and can be combined. Sets the slide transition. This will affect both enter and exit transitions. **Note: If you use the 'scale' transition, fitted text won't work in Safari.**|
|transitionIn|PropTypes.array|Specifies the slide transition when the slide comes into view. Accepts the same values as transition.|
|transitionOut|PropTypes.array|Specifies the slide transition when the slide exits. Accepts the same values as transition.|
|transitionDuration| PropTypes.number| Accepts integer value in milliseconds for slide transition duration.

<a name="transition-function"></a>
##### Transition Function
Spectacle now supports defining custom transitions. The function prototype is `(transitioning: boolean, forward: boolean) => Object`. The `transitioning` param is true when the slide enters and exits. The `forward` param is `true` when the slide is entering, `false` when the slide is exiting. The function returns a style object. You can mix string-based transitions and functions. Styles provided when `transitioning` is `false` will appear during the lifecyle of the slide. An example is shown below:

```jsx
<Slide
transition={[
'fade',
(transitioning, forward) => {
const angle = forward ? -180 : 180;
return {
transform: `
translate3d(0%, ${transitioning ? 100 : 0}%, 0)
rotate(${transitioning ? angle : 0}deg)
`,
backgroundColor: transitioning ? '#26afff' : '#000'
};
}
]}
>
```

<a name="notes"></a>
#### Notes

Expand Down
26 changes: 24 additions & 2 deletions example/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,35 @@ export default class Presentation extends React.Component {
</Link>
<Text textSize="1.5em" margin="20px 0px 0px" bold>Hit Your Right Arrow To Begin!</Text>
</Slide>
<Slide id="wait-what" transition={['slide']} bgColor="black" notes="You can even put notes on your slide. How awesome is that?">
<Slide
id="wait-what"
transition={[
'fade',
(transitioning, forward) => {
const angle = forward ? -180 : 180;
return {
transform: `
translate3d(0%, ${transitioning ? 100 : 0}%, 0)
rotate(${transitioning ? angle : 0}deg)
`,
backgroundColor: transitioning ? '#26afff' : '#000'
};
}
]}
bgColor="black"
notes="You can even put notes on your slide. How awesome is that?"
>
<Image src={images.kat.replace('/', '')} margin="0px auto 40px" />
<Heading size={2} caps fit textColor="primary" textFont="primary">
Wait what?
</Heading>
</Slide>
<Slide transition={['zoom', 'fade']} bgColor="primary" notes="<ul><li>talk about that</li><li>and that</li></ul>">
<Slide
transitionIn={['zoom', 'fade']}
transitionOut={['slide', 'fade']}
bgColor="primary"
notes="<ul><li>talk about that</li><li>and that</li></ul>"
>
<CodePane
lang="jsx"
source={require('raw-loader!../assets/deck.example')}
Expand Down
33 changes: 30 additions & 3 deletions src/components/slide.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
/* eslint-disable no-invalid-this */
/* eslint-disable no-invalid-this, max-statements */
import React from 'react';
import PropTypes from 'prop-types';
import isUndefined from 'lodash/isUndefined';
import isFunction from 'lodash/isFunction';
import { getStyles } from '../utils/base';
import { addFragment } from '../actions';
import stepCounter from '../utils/step-counter';
Expand Down Expand Up @@ -118,8 +119,22 @@ class Slide extends React.PureComponent {
return this.state.reverse ? slideIndex > routeSlideIndex : slideIndex > lastSlideIndex;
}

getTransitionKeys = () => {
const {
props: { transition = [], transitionIn = [], transitionOut = [] },
state: { reverse }
} = this;
if (reverse && transitionOut.length > 0) {
return transitionOut;
} else if (transitionIn.length > 0) {
return transitionIn;
}
return transition;
}

getTransitionStyles = () => {
const { props: { transition = [] }, state: { transitioning, z } } = this;
const { transitioning, z } = this.state;
const transition = this.getTransitionKeys();
let styles = { zIndex: z };
let transformValue = '';

Expand All @@ -143,7 +158,17 @@ class Slide extends React.PureComponent {
transformValue += ` rotateY(${transitioning ? angle : 0}deg)`;
}

return { ...styles, transform: transformValue };
const functionStyles = transition.reduce((memo, current) => {
if (isFunction(current)) {
return {
...memo,
...(current(transitioning, this.transitionDirection()))
};
}
return memo;
}, {});

return { ...styles, transform: transformValue, ...functionStyles };
}

getRouteSlideIndex = () => {
Expand Down Expand Up @@ -235,6 +260,8 @@ Slide.propTypes = {
style: PropTypes.object,
transition: PropTypes.array,
transitionDuration: PropTypes.number,
transitionIn: PropTypes.array,
transitionOut: PropTypes.array,
viewerScaleMode: PropTypes.bool,
};

Expand Down
26 changes: 22 additions & 4 deletions src/components/slide.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,17 @@ const _mockContext = function() {
};

describe('<Slide />', () => {
test('should render correctly without transitions.', () => {
beforeEach(() => {
window.watchMedia = jest.fn();
window.matchMedia = jest.fn().mockReturnValue({ matches: [] });
});

afterEach(() => {
window.watchMedia = null;
window.matchMedia = null;
});

test('should render correctly without transitions.', () => {
const wrapper = mount(
<Slide>
<div>Slide Content</div>
Expand All @@ -34,9 +42,6 @@ describe('<Slide />', () => {
});

test('should render correctly with transitions.', () => {
window.watchMedia = jest.fn();
window.matchMedia = jest.fn().mockReturnValue({ matches: [] });

const wrapper = mount(
<Slide transition={['slide', 'spin']}>
<div>Slide Content</div>
Expand All @@ -45,4 +50,17 @@ describe('<Slide />', () => {
);
expect(wrapper).toMatchSnapshot();
});

test('should return the correct transition keys', () => {
const wrapper = mount(
<Slide transitionIn={['slide']} transitionOut={['fade']}>
<div>Slide Content</div>
</Slide>,
{ context: _mockContext() }
);

expect(wrapper.instance().getTransitionKeys()).toEqual(['slide']);
wrapper.setState({ reverse: true });
expect(wrapper.instance().getTransitionKeys()).toEqual(['fade']);
});
});