Skip to content

Commit

Permalink
✨ Add parameter to configure amp-story-player animation (#34204)
Browse files Browse the repository at this point in the history
* added animation options

* Removed animation from behaviordef

* Added newline on player

* Added documentation on options

* Use next to toggle animation

* Use transitionend

* Using classes for CSS

* Reverted requestAnimationFrame promise

* Added tests

* Apply suggestions from code review

Co-authored-by: Enrique Marroquin <5449100+Enriqe@users.noreply.github.com>

* Use true instead of property

Co-authored-by: Enrique Marroquin <5449100+Enriqe@users.noreply.github.com>

Co-authored-by: Enrique Marroquin <5449100+Enriqe@users.noreply.github.com>
  • Loading branch information
mszylkowski and Enriqe authored May 11, 2021
1 parent c85ae91 commit 4d86879
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 29 deletions.
6 changes: 5 additions & 1 deletion css/amp-story-player-iframe.css
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,15 @@
transform-style: preserve-3d;
}

.i-amphtml-story-player-loaded iframe {
.i-amphtml-story-player-loaded .story-player-iframe {
opacity: 1;
transition: transform 200ms cubic-bezier(0.4, 0, 0.2, 1);
}

.i-amphtml-story-player-no-navigation-transition .story-player-iframe {
transition-duration: 0.01s; /* Set to low value so transitionend is emitted */
}

.i-amphtml-story-player-main-container iframe:nth-of-type(1),
.i-amphtml-story-player-main-container .story-player-iframe[i-amphtml-iframe-position="0"] {
transform: translate3d(0, 0, 1px);
Expand Down
94 changes: 75 additions & 19 deletions examples/amp-story/player.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,31 +32,87 @@
line-height: 1.25;
text-shadow: 3px 3px #000;
}
.actions .circle {
width: 65px;
height: 65px;
background-size: cover;
overflow: hidden;
border-radius: 50%;
display: inline-block;
margin: 10px;
box-shadow:
0 0 0 3px white,
0 0 0 7px hsl(20, 79%, 60%);
color: white;
text-align: center;
padding-top: 20px;
box-sizing: border-box;
text-shadow: 0px 2px 4px #000000;
cursor: pointer;
}

.actions .circle.active {
box-shadow:
0 0 0 3px white,
0 0 0 7px hsl(257, 79%, 60%);
}
</style>
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
<script>
window.onload = () => {
const player = document.querySelector('amp-story-player');
document.querySelector('.actions').addEventListener('click', (e) => {
if (e.target.hasAttribute('data-story')) {
player.show(e.target.getAttribute('data-story'), null, {animate: false});
}
})
player.addEventListener('navigation', (e) => {
document.querySelectorAll('.actions .circle').forEach((circle, index) => {
circle.classList.toggle('active', index === e.detail.index)
})
});
};
</script>
</head>
<body>
<h1>This is a NON-AMP page that embeds a story below:</h1>
<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras viverra neque ex, sit amet varius sem maximus sed. Suspendisse potenti. Donec erat purus, sagittis sit amet tincidunt ut, maximus sit amet massa. Maecenas venenatis fringilla dui vitae vestibulum. Fusce imperdiet euismod lobortis. Nullam sagittis nunc at tristique mattis. In sodales consectetur mollis. Maecenas sollicitudin, ex vel tempor rutrum, turpis eros interdum enim, sit amet volutpat tortor dolor at ipsum. Nam posuere velit vel urna vulputate interdum. Aenean eu vulputate lorem. Praesent nec nunc sodales, egestas orci sed, hendrerit mauris. Ut blandit turpis non erat sagittis, quis fermentum odio feugiat.</div>
<amp-story-player style="width: 360px; height: 600px;">
<a href="https://www-washingtonpost-com.cdn.ampproject.org/v/s/www.washingtonpost.com/graphics/2019/lifestyle/travel/amp-stories/a-locals-guide-to-what-to-eat-and-do-in-new-york-city/"
class="story">
<img data-amp-story-player-poster-img src="https://www-washingtonpost-com.cdn.ampproject.org/i/s/www.washingtonpost.com/graphics/2019/lifestyle/travel/amp-stories/a-locals-guide-to-what-to-eat-and-do-in-new-york-city/img/promo3x4.jpg" width="360" height="600" loading="lazy">
<span class="title">A local’s guide to what to eat and do in New York City</span>
</a>
<a href="https://www-washingtonpost-com.cdn.ampproject.org/v/s/www.washingtonpost.com/graphics/2019/lifestyle/travel/amp-stories/a-locals-guide-to-what-to-eat-and-do-in-miami/">
<img data-amp-story-player-poster-img src="https://www-washingtonpost-com.cdn.ampproject.org/i/s/www.washingtonpost.com/graphics/2019/lifestyle/travel/amp-stories/a-locals-guide-to-what-to-eat-and-do-in-miami/img/promo3x4.jpg" width="360" height="600" loading="lazy">
<span class="title">A local’s guide to what to eat and do in Miami</span>
</a>
<a href="https://www-washingtonpost-com.cdn.ampproject.org/v/s/www.washingtonpost.com/graphics/2019/lifestyle/travel/amp-stories/a-locals-guide-to-what-to-eat-and-do-in-mexico-city/">
<img data-amp-story-player-poster-img src="https://www-washingtonpost-com.cdn.ampproject.org/i/s/www.washingtonpost.com/graphics/2019/lifestyle/travel/amp-stories/a-locals-guide-to-what-to-eat-and-do-in-mexico-city/img/promo3x4.jpg" width="360" height="600" loading="lazy">
<span class="title">A local’s guide to what to eat and do in Mexico City</span>
</a>
<a href="https://www-washingtonpost-com.cdn.ampproject.org/v/s/www.washingtonpost.com/graphics/2019/lifestyle/travel/amp-stories/a-locals-guide-to-what-to-eat-and-do-in-rome/">
<img data-amp-story-player-poster-img src="https://www-washingtonpost-com.cdn.ampproject.org/i/s/www.washingtonpost.com/graphics/2019/lifestyle/travel/amp-stories/a-locals-guide-to-what-to-eat-and-do-in-rome/img/promo3x4.jpg" width="360" height="600" loading="lazy">
<span class="title">A local’s guide to what to eat and do in Rome</span>
</a>
</amp-story-player>
<div>
<amp-story-player style="width: 360px; height: 600px;">
<a href="https://www-washingtonpost-com.cdn.ampproject.org/v/s/www.washingtonpost.com/graphics/2019/lifestyle/travel/amp-stories/a-locals-guide-to-what-to-eat-and-do-in-new-york-city/"
class="story">
<img data-amp-story-player-poster-img src="https://www-washingtonpost-com.cdn.ampproject.org/i/s/www.washingtonpost.com/graphics/2019/lifestyle/travel/amp-stories/a-locals-guide-to-what-to-eat-and-do-in-new-york-city/img/promo3x4.jpg" width="360" height="600" loading="lazy">
<span class="title">A local’s guide to what to eat and do in New York City</span>
</a>
<a href="https://www-washingtonpost-com.cdn.ampproject.org/v/s/www.washingtonpost.com/graphics/2019/lifestyle/travel/amp-stories/a-locals-guide-to-what-to-eat-and-do-in-miami/">
<img data-amp-story-player-poster-img src="https://www-washingtonpost-com.cdn.ampproject.org/i/s/www.washingtonpost.com/graphics/2019/lifestyle/travel/amp-stories/a-locals-guide-to-what-to-eat-and-do-in-miami/img/promo3x4.jpg" width="360" height="600" loading="lazy">
<span class="title">A local’s guide to what to eat and do in Miami</span>
</a>
<a href="https://www-washingtonpost-com.cdn.ampproject.org/v/s/www.washingtonpost.com/graphics/2019/lifestyle/travel/amp-stories/a-locals-guide-to-what-to-eat-and-do-in-mexico-city/">
<img data-amp-story-player-poster-img src="https://www-washingtonpost-com.cdn.ampproject.org/i/s/www.washingtonpost.com/graphics/2019/lifestyle/travel/amp-stories/a-locals-guide-to-what-to-eat-and-do-in-mexico-city/img/promo3x4.jpg" width="360" height="600" loading="lazy">
<span class="title">A local’s guide to what to eat and do in Mexico City</span>
</a>
<a href="https://www-washingtonpost-com.cdn.ampproject.org/v/s/www.washingtonpost.com/graphics/2019/lifestyle/travel/amp-stories/a-locals-guide-to-what-to-eat-and-do-in-rome/">
<img data-amp-story-player-poster-img src="https://www-washingtonpost-com.cdn.ampproject.org/i/s/www.washingtonpost.com/graphics/2019/lifestyle/travel/amp-stories/a-locals-guide-to-what-to-eat-and-do-in-rome/img/promo3x4.jpg" width="360" height="600" loading="lazy">
<span class="title">A local’s guide to what to eat and do in Rome</span>
</a>
</amp-story-player>

<div class="actions">
<div class="circle active"
data-story="https://www-washingtonpost-com.cdn.ampproject.org/v/s/www.washingtonpost.com/graphics/2019/lifestyle/travel/amp-stories/a-locals-guide-to-what-to-eat-and-do-in-new-york-city/"
style="background-image: url('https://www-washingtonpost-com.cdn.ampproject.org/i/s/www.washingtonpost.com/graphics/2019/lifestyle/travel/amp-stories/a-locals-guide-to-what-to-eat-and-do-in-new-york-city/img/42a59562-834d-11e9-b585-e36b16a531aa.jpg')">New York</div>
<div class="circle"
data-story="https://www-washingtonpost-com.cdn.ampproject.org/v/s/www.washingtonpost.com/graphics/2019/lifestyle/travel/amp-stories/a-locals-guide-to-what-to-eat-and-do-in-miami/"
style="background-image: url('https://www-washingtonpost-com.cdn.ampproject.org/i/s/www.washingtonpost.com/graphics/2019/lifestyle/travel/amp-stories/a-locals-guide-to-what-to-eat-and-do-in-miami/img/9f1d2656-6b7a-11e9-bbe7-1c798fb80536.jpg')">Miami</div>
<div class="circle"
data-story="https://www-washingtonpost-com.cdn.ampproject.org/v/s/www.washingtonpost.com/graphics/2019/lifestyle/travel/amp-stories/a-locals-guide-to-what-to-eat-and-do-in-mexico-city/"
style="background-image: url('https://www-washingtonpost-com.cdn.ampproject.org/i/s/www.washingtonpost.com/graphics/2019/lifestyle/travel/amp-stories/a-locals-guide-to-what-to-eat-and-do-in-mexico-city/img/2e0f5178-746c-11e9-9331-30bc5836f48e.jpg')">Mexico City</div>
<div class="circle"
data-story="https://www-washingtonpost-com.cdn.ampproject.org/v/s/www.washingtonpost.com/graphics/2019/lifestyle/travel/amp-stories/a-locals-guide-to-what-to-eat-and-do-in-rome/"
style="background-image: url('https://www-washingtonpost-com.cdn.ampproject.org/i/s/www.washingtonpost.com/graphics/2019/lifestyle/travel/amp-stories/a-locals-guide-to-what-to-eat-and-do-in-rome/img/f5903e2e-86fa-11e9-9d73-e2ba6bbf1b9b.jpg')">Rome</div>
</div>
</div>
<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras viverra neque ex, sit amet varius sem maximus sed. Suspendisse potenti. Donec erat purus, sagittis sit amet tincidunt ut, maximus sit amet massa. Maecenas venenatis fringilla dui vitae vestibulum. Fusce imperdiet euismod lobortis. Nullam sagittis nunc at tristique mattis. In sodales consectetur mollis. Maecenas sollicitudin, ex vel tempor rutrum, turpis eros interdum enim, sit amet volutpat tortor dolor at ipsum. Nam posuere velit vel urna vulputate interdum. Aenean eu vulputate lorem. Praesent nec nunc sodales, egestas orci sed, hendrerit mauris. Ut blandit turpis non erat sagittis, quis fermentum odio feugiat.</div>
</body>
</html>
4 changes: 4 additions & 0 deletions spec/amp-story-player.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,27 +182,31 @@ playerEl.load();

- number: the story in the player to which you want to move, relative to the current story.
- number (optional): the page of the story to which you want to move, relative to the current page.
- {animate: boolean} (optional): options for the navigation (animate: whether to animate the story transition).

If the player is currently on the third story out of five stories:

- `player.go(1)` will go forward one story to the fourth story
- `player.go(-1)` will go backward one story to the second story
- `player.go(-1, 1)` will go backward one story and navigate one page backwards
- `player.go(0, 5)` will stay in the current story and navigate 5 pages forward
- `player.go(1, 0, {animate: false})` will go to the next page without the swipe animation

#### show

**Parameters**

- string or null: the URL of the story to show.
- string (optional): the ID attribute of the page element.
- {animate: boolean} (optional): options for the navigation (animate: whether to animate the story transition).

Will change the current story being displayed by the player.

```javascript
player.show('cool-story.html'); // Will display cool-story.html
player.show('cool-story.html', 'page-4'); // Will display cool-story.html and switch to page-4
player.show(null, 'page-4'); // Stay on current story and switch to page-4
player.show('cool-story.html', null, {animate: false}); // Will display cool-story.html without the swipe animation
```

#### rewind
Expand Down
42 changes: 33 additions & 9 deletions src/amp-story-player/amp-story-player-impl.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {
serializeQueryString,
} from '../url';
import {applySandbox} from '../3p-frame';
import {createCustomEvent} from '../event-helper';
import {createCustomEvent, listenOnce} from '../event-helper';
import {dict} from '../core/types/object';
import {isJsonScriptTag, tryFocus} from '../dom';
// Source for this constant is css/amp-story-player-iframe.css
Expand Down Expand Up @@ -115,6 +115,10 @@ const STORY_MESSAGE_STATE_TYPE = {
/** @const {string} */
export const AMP_STORY_PLAYER_EVENT = 'AMP_STORY_PLAYER_EVENT';

/** @const {string} */
const CLASS_NO_NAVIGATION_TRANSITION =
'i-amphtml-story-player-no-navigation-transition';

/** @typedef {{ state:string, value:(boolean|string) }} */
let DocumentStateTypeDef;

Expand All @@ -135,17 +139,27 @@ let StoryDef;

/**
* @typedef {{
* on: string,
* action: string,
* endpoint: string,
* on: ?string,
* action: ?string,
* endpoint: ?string,
* pageScroll: ?boolean,
* autoplay: ?boolean,
* }}
*/
let BehaviorDef;

/**
* @typedef {{
* controls: (!Array<!ViewerControlDef>),
* behavior: !BehaviorDef,
* attribution: ?string,
* }}
*/
let DisplayDef;

/**
* @typedef {{
* controls: ?Array<!ViewerControlDef>,
* behavior: ?BehaviorDef,
* display: ?DisplayDef,
* }}
*/
let ConfigDef;
Expand Down Expand Up @@ -756,16 +770,24 @@ export class AmpStoryPlayer {
* Shows the story provided by the URL in the player and go to the page if provided.
* @param {?string} storyUrl
* @param {string=} pageId
* @param {{animate: boolean?}} options
* @return {!Promise}
*/
show(storyUrl, pageId = null) {
show(storyUrl, pageId = null, options = {}) {
const story = this.getStoryFromUrl_(storyUrl);

let renderPromise = Promise.resolve();
if (story.idx !== this.currentIdx_) {
this.currentIdx_ = story.idx;

renderPromise = this.render_();

if (options.animate === false) {
this.rootEl_.classList.toggle(CLASS_NO_NAVIGATION_TRANSITION, true);
listenOnce(story.iframe, 'transitionend', () => {
this.rootEl_.classList.remove(CLASS_NO_NAVIGATION_TRANSITION);
});
}
this.onNavigation_();
}

Expand Down Expand Up @@ -945,8 +967,9 @@ export class AmpStoryPlayer {
* Navigates stories given a number.
* @param {number} storyDelta
* @param {number=} pageDelta
* @param {{animate: boolean?}} options
*/
go(storyDelta, pageDelta = 0) {
go(storyDelta, pageDelta = 0, options = {}) {
if (storyDelta === 0 && pageDelta === 0) {
return;
}
Expand All @@ -969,7 +992,7 @@ export class AmpStoryPlayer {

let showPromise = Promise.resolve();
if (this.currentIdx_ !== newStory.idx) {
showPromise = this.show(newStory.href);
showPromise = this.show(newStory.href, /* pageId */ null, options);
}

showPromise.then(() => {
Expand All @@ -980,6 +1003,7 @@ export class AmpStoryPlayer {
/**
* Updates story position.
* @param {!StoryDef} story
* @return {!Promise}
* @private
*/
updatePosition_(story) {
Expand Down
67 changes: 67 additions & 0 deletions test/unit/test-amp-story-player.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {AmpStoryComponentManager} from '../../src/amp-story-player/amp-story-com
import {AmpStoryPlayer} from '../../src/amp-story-player/amp-story-player-impl';
import {Messaging} from '@ampproject/viewer-messaging';
import {PageScroller} from '../../src/amp-story-player/page-scroller';
import {expect} from 'chai';

describes.realWin('AmpStoryPlayer', {amp: false}, (env) => {
let win;
Expand Down Expand Up @@ -1440,5 +1441,71 @@ describes.realWin('AmpStoryPlayer', {amp: false}, (env) => {
},
});
});

it('supress navigation animation if called go with options.animate = false', async () => {
const playerEl = win.document.createElement('amp-story-player');
attachPlayerWithStories(playerEl, 2);
playerEl.appendChild(buildCircularWrappingConfig());

const player = new AmpStoryPlayer(win, playerEl);

await player.load();
await nextTick();

player.go(1, 0, {animate: false});

expect(
player
.getElement()
.querySelector('.i-amphtml-story-player-main-container')
.classList.contains('i-amphtml-story-player-no-navigation-transition')
).to.be.true;
});

it('not supress navigation animation if called go with options.animate = true', async () => {
const playerEl = win.document.createElement('amp-story-player');
attachPlayerWithStories(playerEl, 2);
playerEl.appendChild(buildCircularWrappingConfig());

const player = new AmpStoryPlayer(win, playerEl);

await player.load();
await nextTick();

player.go(1, 0, {animate: true});

expect(
player
.getElement()
.querySelector('.i-amphtml-story-player-main-container')
.classList.contains('i-amphtml-story-player-no-navigation-transition')
).to.be.false;
});

it('revert navigation animation after transition ends', async () => {
const playerEl = win.document.createElement('amp-story-player');
attachPlayerWithStories(playerEl, 2);
playerEl.appendChild(buildCircularWrappingConfig());

const player = new AmpStoryPlayer(win, playerEl);

await player.load();
await nextTick();

player.go(1, 0, {animate: false});

const rootEl = player
.getElement()
.querySelector('.i-amphtml-story-player-main-container');

// Wait for event dispatched to be listened
await new Promise((resolve) => setTimeout(() => resolve(), 50));

expect(
rootEl.classList.contains(
'i-amphtml-story-player-no-navigation-transition'
)
).to.be.false;
});
});
});

0 comments on commit 4d86879

Please sign in to comment.