forked from ampproject/amphtml
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement parallax effect extension (ampproject#7794)
- Loading branch information
Showing
9 changed files
with
615 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
<!doctype html> | ||
<html ⚡> | ||
<head> | ||
<meta charset="utf-8"> | ||
<title>AMP Article with parallax title</title> | ||
<link rel="canonical" href="amps.html"> | ||
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1"> | ||
<script async src="https://cdn.ampproject.org/v0.js"></script> | ||
<style amp-custom> | ||
article, header h1 { | ||
margin: 0px 10px; | ||
} | ||
|
||
header h1 { | ||
position: absolute; | ||
top: 25vh; | ||
padding: 5px; | ||
z-index: 1; | ||
max-width: 70vw; | ||
} | ||
|
||
header h1 span { | ||
background-color: black; | ||
color: white; | ||
line-height: 1.2em; | ||
} | ||
|
||
header amp-img img { | ||
object-fit: cover; | ||
} | ||
|
||
/* | ||
* Vertically center the tite within the image. | ||
*/ | ||
.vertically-center { | ||
transform: translateY(-50%); | ||
} | ||
</style> | ||
<script async custom-element="amp-fx-parallax" src="https://cdn.ampproject.org/v0/amp-fx-parallax-0.1.js"></script> | ||
<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript> | ||
</head> | ||
<body> | ||
<header> | ||
<h1 amp-fx-parallax="1.7"> | ||
<div class="vertically-center"> | ||
<span>Lorem Ipsum Dolor Sit Amet Consectetur Adipiscing<span> | ||
</div> | ||
</h1> | ||
<amp-img height="50vh" layout="fixed-height" src="img/hero@1x.jpg"></amp-img> | ||
</header> | ||
|
||
<article> | ||
<p> | ||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur ullamcorper turpis vel commodo scelerisque. Phasellus | ||
luctus nunc ut elit cursus, et imperdiet diam vehicula. Duis et nisi sed urna blandit bibendum et sit amet erat. Suspendisse | ||
potenti. Curabitur consequat volutpat arcu nec elementum. Etiam a turpis ac libero varius condimentum. Maecenas sollicitudin | ||
felis aliquam tortor vulputate, ac posuere velit semper. | ||
</p> | ||
<p> | ||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur ullamcorper turpis vel commodo scelerisque. Phasellus | ||
luctus nunc ut elit cursus, et imperdiet diam vehicula. Duis et nisi sed urna blandit bibendum et sit amet erat. Suspendisse | ||
potenti. Curabitur consequat volutpat arcu nec elementum. Etiam a turpis ac libero varius condimentum. Maecenas sollicitudin | ||
felis aliquam tortor vulputate, ac posuere velit semper. | ||
</p> | ||
<p> | ||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur ullamcorper turpis vel commodo scelerisque. Phasellus | ||
luctus nunc ut elit cursus, et imperdiet diam vehicula. Duis et nisi sed urna blandit bibendum et sit amet erat. Suspendisse | ||
potenti. Curabitur consequat volutpat arcu nec elementum. Etiam a turpis ac libero varius condimentum. Maecenas sollicitudin | ||
felis aliquam tortor vulputate, ac posuere velit semper. | ||
</p> | ||
<p> | ||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur ullamcorper turpis vel commodo scelerisque. Phasellus | ||
luctus nunc ut elit cursus, et imperdiet diam vehicula. Duis et nisi sed urna blandit bibendum et sit amet erat. Suspendisse | ||
potenti. Curabitur consequat volutpat arcu nec elementum. Etiam a turpis ac libero varius condimentum. Maecenas sollicitudin | ||
felis aliquam tortor vulputate, ac posuere velit semper. | ||
</p> | ||
<p> | ||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur ullamcorper turpis vel commodo scelerisque. Phasellus | ||
luctus nunc ut elit cursus, et imperdiet diam vehicula. Duis et nisi sed urna blandit bibendum et sit amet erat. Suspendisse | ||
potenti. Curabitur consequat volutpat arcu nec elementum. Etiam a turpis ac libero varius condimentum. Maecenas sollicitudin | ||
felis aliquam tortor vulputate, ac posuere velit semper. | ||
</p> | ||
</article> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/** | ||
* Copyright 2017 The AMP HTML Authors. All Rights Reserved. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS-IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import {ampdocServiceFor} from '../../../src/ampdoc'; | ||
import {installParallaxForDoc} from '../../../src/service/parallax-impl'; | ||
import {onDocumentReady} from '../../../src/document-ready'; | ||
|
||
const ampdoc = ampdocServiceFor(AMP.win).getAmpDoc(); | ||
onDocumentReady(ampdoc.win.document, () => { | ||
installParallaxForDoc(ampdoc.getRootNode()); | ||
}); |
202 changes: 202 additions & 0 deletions
202
extensions/amp-fx-parallax/0.1/test/test-amp-fx-parallax.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
/** | ||
* Copyright 2017 The AMP HTML Authors. All Rights Reserved. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS-IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import {createIframePromise} from '../../../../testing/iframe'; | ||
import {installParallaxForDoc} from '../../../../src/service/parallax-impl'; | ||
import {parallaxForDoc} from '../../../../src/parallax'; | ||
import {toggleExperiment} from '../../../../src/experiments'; | ||
import {viewportForDoc} from '../../../../src/viewport'; | ||
import {vsyncFor} from '../../../../src/vsync'; | ||
|
||
describes.sandboxed('amp-fx-parallax', {}, () => { | ||
const DEFAULT_FACTOR = 1.7; | ||
|
||
function addTextChildren(iframe) { | ||
return [iframe.doc.createTextNode('AMP: Accelerated Mobile Pages')]; | ||
} | ||
|
||
function getAmpParallaxElement(opt_childrenCallback, opt_factor, opt_top) { | ||
const factor = opt_factor || DEFAULT_FACTOR; | ||
const top = opt_top || 0; | ||
let viewport; | ||
let parallaxElement; | ||
|
||
return createIframePromise().then(iframe => { | ||
const bodyResizer = iframe.doc.createElement('div'); | ||
bodyResizer.style.height = '4000px'; | ||
bodyResizer.style.width = '1px'; | ||
iframe.doc.body.appendChild(bodyResizer); | ||
|
||
viewport = viewportForDoc(iframe.win.document); | ||
viewport.resize_(); | ||
|
||
toggleExperiment(iframe.win, 'amp-fx-parallax', true); | ||
|
||
parallaxElement = iframe.doc.createElement('div'); | ||
parallaxElement.setAttribute('amp-fx-parallax', factor); | ||
if (opt_childrenCallback) { | ||
const children = opt_childrenCallback(iframe, parallaxElement); | ||
children.forEach(child => { | ||
parallaxElement.appendChild(child); | ||
}); | ||
} | ||
|
||
const parent = iframe.doc.querySelector('#parent'); | ||
parent.appendChild(parallaxElement); | ||
installParallaxForDoc(iframe.doc); | ||
|
||
return new Promise(resolve => { | ||
vsyncFor(iframe.win).mutate(() => { | ||
resolve({ | ||
element: parallaxElement, | ||
iframe, | ||
viewport, | ||
}); | ||
}); | ||
viewport.setScrollTop(top); | ||
}); | ||
}).catch(error => { | ||
return Promise.reject({error, parallaxElement, stack: error.stack}); | ||
}); | ||
} | ||
|
||
it('should move when the user scrolls, if visible', () => { | ||
const scroll = 10; | ||
const expectedParallax = -1 * DEFAULT_FACTOR * scroll; | ||
|
||
return getAmpParallaxElement(addTextChildren) | ||
.then(({element, iframe, viewport}) => { | ||
const parallaxService = parallaxForDoc(iframe.doc); | ||
const top = element.getBoundingClientRect().top; | ||
expect(top).to.equal(viewport.getScrollTop()); | ||
|
||
return new Promise(resolve => { | ||
parallaxService.addScrollListener_(() => { | ||
const top = element.getBoundingClientRect().top; | ||
expect(top).to.equal(expectedParallax); | ||
resolve(); | ||
}); | ||
viewport.setScrollTop(scroll); | ||
}); | ||
}); | ||
}); | ||
|
||
it('should not move after it is outside of the viewport', () => { | ||
const scroll = 100; | ||
const expectedParallax = -1 * DEFAULT_FACTOR * scroll; | ||
|
||
return getAmpParallaxElement(addTextChildren, DEFAULT_FACTOR) | ||
.then(({element, iframe, viewport}) => { | ||
const parallaxService = parallaxForDoc(iframe.doc); | ||
|
||
return new Promise(resolve => { | ||
parallaxService.addScrollListener_(() => { | ||
const top = element.getBoundingClientRect().top; | ||
expect(top).to.not.equal(expectedParallax); | ||
resolve(); | ||
}); | ||
viewport.setScrollTop(scroll); | ||
}); | ||
}); | ||
}); | ||
|
||
it('should move downward with a negative parallax factor', () => { | ||
const scroll = 10; | ||
const expectedParallax = -1 * DEFAULT_FACTOR * scroll; | ||
|
||
return getAmpParallaxElement(addTextChildren, DEFAULT_FACTOR) | ||
.then(({element, iframe, viewport}) => { | ||
const parallaxService = parallaxForDoc(iframe.doc); | ||
return new Promise(resolve => { | ||
parallaxService.addScrollListener_(() => { | ||
const top = element.getBoundingClientRect().top; | ||
expect(top).to.equal(expectedParallax); | ||
resolve(); | ||
}); | ||
viewport.setScrollTop(scroll); | ||
}); | ||
}); | ||
}); | ||
|
||
it('should apply multiple scrolls as if they were one large scroll', () => { | ||
const scroll = 10; | ||
const factor = -1.7; // move downward so it stays in the viewport | ||
const expectedParallax = -1 * factor * 2 * scroll; | ||
|
||
return getAmpParallaxElement(addTextChildren, factor) | ||
.then(({element, iframe, viewport}) => { | ||
const parallaxService = parallaxForDoc(iframe.doc); | ||
return new Promise(resolve => { | ||
parallaxService.addScrollListener_(afterFirstScroll); | ||
viewport.setScrollTop(scroll); | ||
|
||
function afterFirstScroll() { | ||
parallaxService.removeScrollListener_(afterFirstScroll); | ||
parallaxService.addScrollListener_(afterSecondScroll); | ||
viewport.setScrollTop(2 * scroll); | ||
} | ||
|
||
function afterSecondScroll() { | ||
const top = element.getBoundingClientRect().top; | ||
expect(top).to.equal(expectedParallax); | ||
resolve(); | ||
} | ||
}); | ||
}); | ||
}); | ||
|
||
it('should return to its original position when scrolling back', () => { | ||
const factor = -1.7; // move downward so it stays in the viewport | ||
|
||
return getAmpParallaxElement(addTextChildren, factor) | ||
.then(({element, iframe, viewport}) => { | ||
const parallaxService = parallaxForDoc(iframe.doc); | ||
return new Promise(resolve => { | ||
parallaxService.addScrollListener_(afterFirstScroll); | ||
viewport.setScrollTop(10); | ||
|
||
function afterFirstScroll() { | ||
parallaxService.removeScrollListener_(afterFirstScroll); | ||
parallaxService.addScrollListener_(afterSecondScroll); | ||
viewport.setScrollTop(200); | ||
} | ||
|
||
function afterSecondScroll() { | ||
parallaxService.removeScrollListener_(afterSecondScroll); | ||
parallaxService.addScrollListener_(afterThirdScroll); | ||
viewport.setScrollTop(0); | ||
} | ||
|
||
function afterThirdScroll() { | ||
const top = element.getBoundingClientRect().top; | ||
expect(top).to.equal(0); | ||
resolve(); | ||
} | ||
}); | ||
}); | ||
}); | ||
|
||
it('should render moved if the page loads partially scrolled', () => { | ||
const scroll = 10; | ||
const expectedParallax = -1 * DEFAULT_FACTOR * scroll; | ||
|
||
return getAmpParallaxElement(addTextChildren, DEFAULT_FACTOR, scroll) | ||
.then(({element}) => { | ||
const top = element.getBoundingClientRect().top; | ||
expect(top).to.equal(expectedParallax); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
<!--- | ||
Copyright 2017 The AMP HTML Authors. All Rights Reserved. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS-IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
--> | ||
|
||
# <a name="amp-fx-parallax"></a> `amp-fx-parallax` | ||
|
||
<table> | ||
<tr> | ||
<td class="col-fourty"><strong>Description</strong></td> | ||
<td><code>amp-fx-parallax</code> enables a 3D-perspective effect on elements with the attribute.</td> | ||
</tr> | ||
<tr> | ||
<td class="col-fourty" width="40%"><strong>Availability</strong></td> | ||
<td>In development</td> | ||
</tr> | ||
<tr> | ||
<td class="col-fourty"><strong>Required Script</strong></td> | ||
<td><code><script async custom-element="amp-fx-parallax" src="https://cdn.ampproject.org/v0/amp-fx-parallax-0.1.js"></script></code></td> | ||
</tr> | ||
<tr> | ||
<td class="col-fourty"><strong>Examples</strong></td> | ||
<td>In development</td> | ||
</tr> | ||
</table> | ||
|
||
## Behavior | ||
|
||
The `amp-fx-parallax` attribute causes an element to move as if it is nearer or farther relative to the foreground of the page content. As the user scrolls the page, the element scrolls faster or slower depending on the value assigned to the attribute. | ||
|
||
Example: | ||
|
||
```html | ||
<amp-img amp-fx-parallax="0.5" height="50vh" layout="fixed-height" src="hero.jpg"> | ||
</amp-img> | ||
``` | ||
|
||
## Attributes | ||
|
||
**amp-fx-parallax** | ||
|
||
The factor to use when scrolling. A value greater than 1 scrolls the element upward when the user scrolls down the page. A value less than 1 scrolls the element downward when the user scrolls downward. A value of 1 behaves normally. A value of 0 effectively makes the element scroll fixed with the page. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.