Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ amp-animation: add x(), y(), rescale by scope element's transform #28146

Merged
merged 14 commits into from
May 7, 2020
50 changes: 50 additions & 0 deletions examples/amp-story/amp-story-animation.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@
font-family: sans-serif;
font-size: 18px;
}
.box {
background: blue;
width: 80px;
height: 100px;
margin: 0 0 20px;
}
</style>
</head>
<body>
Expand Down Expand Up @@ -68,6 +74,50 @@
</amp-story-animation>
</amp-story-page>

<amp-story-page id="page2">
<amp-story-grid-layer template="vertical">
<div class="box animate-bottom-right"></div>
<div class="box animate-bottom"></div>
<div class="box animate-right"></div>
</amp-story-grid-layer>
<amp-story-animation layout="nodisplay">
<script type="application/json">
[
{
"selector": ".animate-bottom",
"duration": "2s",
"iterations": "infinite",
"keyframes": [
{"transform": "none"},
{"transform": "translate(0, calc(100vh - y() - height()))"},
{"transform": "none"}
]
},
{
"selector": ".animate-right",
"duration": "2s",
"iterations": "infinite",
"keyframes": [
{"transform": "none"},
{"transform": "translate(calc(100vw - x() - width()), 0)"},
{"transform": "none"}
]
},
{
"selector": ".animate-bottom-right",
"duration": "2s",
"iterations": "infinite",
"keyframes": [
{"transform": "none"},
{"transform": "translate(calc(100vw - x() - width()), calc(100vh - y() - height()))"},
{"transform": "none"}
]
}
]
</script>
</amp-story-animation>
</amp-story-page>

<amp-story-bookend
src="bookendv1.json"
layout="nodisplay"
Expand Down
48 changes: 27 additions & 21 deletions extensions/amp-animation/0.1/parsers/css-expr-ast.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
const FINAL_URL_RE = /^(data|https)\:/i;
const DEG_TO_RAD = (2 * Math.PI) / 360;
const GRAD_TO_RAD = Math.PI / 200;
const VAR_CSS_RE = /(calc|min|max|clamp|var|url|rand|index|width|height|num|length)\(/i;
const VAR_CSS_RE = /\b(calc|min|max|clamp|var|url|rand|index|width|height|num|length|x|y)\(/i;
const NORM_CSS_RE = /\d(%|em|rem|vw|vh|vmin|vmax|s|deg|grad)/i;
const INFINITY_RE = /^(infinity|infinite)$/i;
const BOX_DIMENSIONS = ['h', 'w', 'h', 'w'];
Expand Down Expand Up @@ -87,18 +87,18 @@ export class CssContext {
getViewportSize() {}

/**
* Returns the current element's size.
* @return {!{width: number, height: number}}
* Returns the current element's rectangle.
* @return {!../../../src/layout-rect.LayoutRect}
*/
getCurrentElementSize() {}
getCurrentElementRect() {}

/**
* Returns the specified element's size.
* Returns the specified element's rectangle.
* @param {string} unusedSelector
* @param {?string} unusedSelectionMethod
* @return {!{width: number, height: number}}
* @return {!../../../src/layout-rect.LayoutRect}
*/
getElementSize(unusedSelector, unusedSelectionMethod) {}
getElementRect(unusedSelector, unusedSelectionMethod) {}

/**
* Returns the dimension: "w" for width or "h" for height.
Expand Down Expand Up @@ -487,8 +487,8 @@ export class CssLengthNode extends CssNumericNode {
/** @override */
calcPercent(percent, context) {
const dim = context.getDimension();
const size = context.getCurrentElementSize();
const side = getDimSide(dim, size);
const size = context.getCurrentElementRect();
const side = getRectField(dim, size);
return new CssLengthNode((side * percent) / 100, 'px');
}
}
Expand Down Expand Up @@ -847,16 +847,16 @@ export class CssTranslateNode extends CssFuncNode {
/**
* AMP-specific `width()` and `height()` functions.
*/
export class CssDimSizeNode extends CssNode {
export class CssRectNode extends CssNode {
/**
* @param {string} dim
* @param {string} field x, y, width or height
* @param {?string=} opt_selector
* @param {?string=} opt_selectionMethod Either `undefined` or "closest".
*/
constructor(dim, opt_selector, opt_selectionMethod) {
constructor(field, opt_selector, opt_selectionMethod) {
super();
/** @const @private */
this.dim_ = dim;
this.field_ = field;
/** @const @private */
this.selector_ = opt_selector || null;
/** @const @private */
Expand All @@ -875,10 +875,10 @@ export class CssDimSizeNode extends CssNode {

/** @override */
calc(context) {
const size = this.selector_
? context.getElementSize(this.selector_, this.selectionMethod_)
: context.getCurrentElementSize();
return new CssLengthNode(getDimSide(this.dim_, size), 'px');
const rect = this.selector_
? context.getElementRect(this.selector_, this.selectionMethod_)
: context.getCurrentElementRect();
return new CssLengthNode(getRectField(this.field_, rect), 'px');
}
}

Expand Down Expand Up @@ -1381,12 +1381,18 @@ function noCss() {
}

/**
* @param {?string} dim
* @param {!{width: number, height: number}} size
* @param {?string} field
* @param {!../../../src/layout-rect.LayoutRect} rect
* @return {number}
*/
function getDimSide(dim, size) {
return dim == 'w' ? size.width : dim == 'h' ? size.height : 0;
function getRectField(field, rect) {
return field == 'w'
? rect.width
: field == 'h'
? rect.height
: rect[field] != null
? rect[field]
: 0;
alanorozco marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand Down
32 changes: 23 additions & 9 deletions extensions/amp-animation/0.1/parsers/css-expr-impl.jison
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ ident \-?[a-zA-Z_][\-a-zA-Z0-9_]*
{C}{I}{R}{C}{L}{E}\( return 'CIRCLE_START'
{E}{L}{L}{I}{P}{S}{E}\( return 'ELLIPSE_START'
{P}{O}{L}{Y}{G}{O}{N}\( return 'POLYGON_START'
{X}\( return 'X_START'
{Y}\( return 'Y_START'
{ident}\( return 'FUNCTION_START'
{ident} return 'IDENT'
\-\-{ident} return 'VAR_NAME';
Expand Down Expand Up @@ -260,7 +262,7 @@ function:
{$$ = $1;}
| translate_function
{$$ = $1;}
| dim_function
| rect_function
{$$ = $1;}
| num_function
{$$ = $1;}
Expand Down Expand Up @@ -457,25 +459,37 @@ polygon_function:


/**
* AMP-specific `width()` and `height()` functions:
* AMP-specific `width()`, `height()`, `x()` and `y()` functions:
* - `width(".selector")`
* - `height(".selector")`
* - `width(closest(".selector"))`
* - `height(closest(".selector"))`
*/
dim_function:
rect_function:
WIDTH_START ')'
{$$ = new ast.CssDimSizeNode('w');}
{$$ = new ast.CssRectNode('w');}
| HEIGHT_START ')'
{$$ = new ast.CssDimSizeNode('h');}
{$$ = new ast.CssRectNode('h');}
| X_START ')'
{$$ = new ast.CssRectNode('x');}
| Y_START ')'
{$$ = new ast.CssRectNode('y');}
| WIDTH_START STRING ')'
{$$ = new ast.CssDimSizeNode('w', $2.slice(1, -1));}
{$$ = new ast.CssRectNode('w', $2.slice(1, -1));}
| HEIGHT_START STRING ')'
{$$ = new ast.CssDimSizeNode('h', $2.slice(1, -1));}
{$$ = new ast.CssRectNode('h', $2.slice(1, -1));}
| X_START STRING ')'
{$$ = new ast.CssRectNode('x', $2.slice(1, -1));}
| Y_START STRING ')'
{$$ = new ast.CssRectNode('y', $2.slice(1, -1));}
| WIDTH_START CLOSEST_START STRING ')' ')'
{$$ = new ast.CssDimSizeNode('w', $3.slice(1, -1), 'closest');}
{$$ = new ast.CssRectNode('w', $3.slice(1, -1), 'closest');}
| HEIGHT_START CLOSEST_START STRING ')' ')'
{$$ = new ast.CssDimSizeNode('h', $3.slice(1, -1), 'closest');}
{$$ = new ast.CssRectNode('h', $3.slice(1, -1), 'closest');}
| X_START CLOSEST_START STRING ')' ')'
{$$ = new ast.CssRectNode('x', $3.slice(1, -1), 'closest');}
| Y_START CLOSEST_START STRING ')' ')'
{$$ = new ast.CssRectNode('y', $3.slice(1, -1), 'closest');}
;


Expand Down
33 changes: 23 additions & 10 deletions extensions/amp-animation/0.1/test/test-css-expr-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ describes.sandboxed('CSS parse', {}, () => {
`<${pseudoArray(n.args_, n.dimensions_)}>`
);
}
if (n instanceof ast.CssDimSizeNode) {
if (n instanceof ast.CssRectNode) {
return (
`DIM<${n.dim_}` +
`RECT<${n.dim_}` +
`, ${n.selector_ ? '"' + n.selector_ + '"' : null}` +
`, ${n.selectionMethod_}>`
);
Expand Down Expand Up @@ -301,24 +301,37 @@ describes.sandboxed('CSS parse', {}, () => {
).to.equal('CON<A B>');
});

it('should parse a dimension function', () => {
it('should parse a rect function', () => {
// Current.
expect(parsePseudo('width()')).to.equal('DIM<w, null, null>');
expect(parsePseudo('height()')).to.equal('DIM<h, null, null>');
expect(parsePseudo('width()')).to.equal('RECT<w, null, null>');
expect(parsePseudo('height()')).to.equal('RECT<h, null, null>');
expect(parsePseudo('x()')).to.equal('RECT<x, null, null>');
expect(parsePseudo('y()')).to.equal('RECT<y, null, null>');

// Query.
expect(parsePseudo('width(".sel")')).to.equal('DIM<w, ".sel", null>');
expect(parsePseudo('width(".sel")')).to.equal('RECT<w, ".sel", null>');
expect(parsePseudo('WIDTH(".sel > div")')).to.equal(
'DIM<w, ".sel > div", null>'
'RECT<w, ".sel > div", null>'
);
expect(parsePseudo('height(".sel")')).to.equal('DIM<h, ".sel", null>');
expect(parsePseudo('height(".sel")')).to.equal('RECT<h, ".sel", null>');
expect(parsePseudo('x(".sel")')).to.equal('RECT<x, ".sel", null>');
expect(parsePseudo('x(".sel > div")')).to.equal(
'RECT<x, ".sel > div", null>'
);
expect(parsePseudo('y(".sel")')).to.equal('RECT<y, ".sel", null>');

// Closest.
expect(parsePseudo('width(closest(".sel"))')).to.equal(
'DIM<w, ".sel", closest>'
'RECT<w, ".sel", closest>'
);
expect(parsePseudo('height(closest(".sel"))')).to.equal(
'DIM<h, ".sel", closest>'
'RECT<h, ".sel", closest>'
);
expect(parsePseudo('x(closest(".sel"))')).to.equal(
'RECT<x, ".sel", closest>'
);
expect(parsePseudo('y(closest(".sel"))')).to.equal(
'RECT<y, ".sel", closest>'
);
});

Expand Down