feat(ivy): provide groundwork for animations in core (#25234)
PR Close #25234
This commit is contained in:

committed by
Kara Erickson

parent
a880686081
commit
82a14dc107
53
packages/core/test/bundling/animation_world/BUILD.bazel
Normal file
53
packages/core/test/bundling/animation_world/BUILD.bazel
Normal file
@ -0,0 +1,53 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("//tools:defaults.bzl", "jasmine_node_test", "ng_module", "ng_rollup_bundle", "ts_library")
|
||||
load("//tools/symbol-extractor:index.bzl", "js_expected_symbol_test")
|
||||
load("//tools/http-server:http_server.bzl", "http_server")
|
||||
|
||||
ng_module(
|
||||
name = "animation_world",
|
||||
srcs = ["index.ts"],
|
||||
tags = ["ivy-only"],
|
||||
deps = [
|
||||
"//packages/common",
|
||||
"//packages/core",
|
||||
"//packages/core/test/bundling/util:reflect_metadata",
|
||||
],
|
||||
)
|
||||
|
||||
ng_rollup_bundle(
|
||||
name = "bundle",
|
||||
# TODO(alexeagle): This is inconsistent.
|
||||
# We try to teach users to always have their workspace at the start of a
|
||||
# path, to disambiguate from other workspaces.
|
||||
# Here, the rule implementation is looking in an execroot where the layout
|
||||
# has an "external" directory for external dependencies.
|
||||
# This should probably start with "angular/" and let the rule deal with it.
|
||||
entry_point = "packages/core/test/bundling/animation_world/index.js",
|
||||
tags = ["ivy-only"],
|
||||
deps = [
|
||||
":animation_world",
|
||||
"//packages/core",
|
||||
],
|
||||
)
|
||||
|
||||
js_expected_symbol_test(
|
||||
name = "symbol_test",
|
||||
src = ":bundle.min_debug.js",
|
||||
golden = ":bundle.golden_symbols.json",
|
||||
tags = [
|
||||
"ivy-local",
|
||||
"ivy-only",
|
||||
],
|
||||
)
|
||||
|
||||
http_server(
|
||||
name = "devserver",
|
||||
data = [
|
||||
"animation_world.css",
|
||||
"base.css",
|
||||
"index.html",
|
||||
":bundle.min.js",
|
||||
":bundle.min_debug.js",
|
||||
],
|
||||
)
|
@ -0,0 +1,49 @@
|
||||
html, body {
|
||||
padding:0;
|
||||
margin:0;
|
||||
font-family: Verdana;
|
||||
}
|
||||
|
||||
animation-world {
|
||||
display:block;
|
||||
margin: 0 auto;
|
||||
max-width:1000px;
|
||||
border:2px solid black;
|
||||
}
|
||||
|
||||
animation-world nav {
|
||||
padding:1em;
|
||||
border-bottom:2px solid black;
|
||||
background:#eee;
|
||||
}
|
||||
|
||||
animation-world .list {
|
||||
display:grid;
|
||||
grid-template-columns: 33% 33% 34%;
|
||||
grid-template-rows: 33% 33% 34%;
|
||||
height:1000px;
|
||||
}
|
||||
animation-world .record {
|
||||
line-height:333px;
|
||||
text-align:center;
|
||||
font-weight: bold;
|
||||
font-size:50px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.record-1 { color:white; background: green;}
|
||||
.record-2 { color:white; background: red;}
|
||||
.record-3 { color:white; background: blue;}
|
||||
.record-4 { color:white; background: orange;}
|
||||
.record-5 { color:white; background: purple;}
|
||||
.record-6 { color:white; background: black;}
|
||||
.record-7 { color:white; background: silver;}
|
||||
.record-8 { color:white; background: teal;}
|
||||
.record-9 { color:white; background: pink;}
|
||||
|
||||
@keyframes fadeInOut {
|
||||
from { opacity: 0; background: white; }
|
||||
33% { opacity: 1; background: black; }
|
||||
66% { opacity: 0.5; background: black; }
|
||||
100% { opacity: 1 }
|
||||
}
|
141
packages/core/test/bundling/animation_world/base.css
Normal file
141
packages/core/test/bundling/animation_world/base.css
Normal file
@ -0,0 +1,141 @@
|
||||
hr {
|
||||
margin: 20px 0;
|
||||
border: 0;
|
||||
border-top: 1px dashed #c5c5c5;
|
||||
border-bottom: 1px dashed #f7f7f7;
|
||||
}
|
||||
|
||||
.learn a {
|
||||
font-weight: normal;
|
||||
text-decoration: none;
|
||||
color: #b83f45;
|
||||
}
|
||||
|
||||
.learn a:hover {
|
||||
text-decoration: underline;
|
||||
color: #787e7e;
|
||||
}
|
||||
|
||||
.learn h3,
|
||||
.learn h4,
|
||||
.learn h5 {
|
||||
margin: 10px 0;
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.learn h3 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.learn h4 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.learn h5 {
|
||||
margin-bottom: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.learn ul {
|
||||
padding: 0;
|
||||
margin: 0 0 30px 25px;
|
||||
}
|
||||
|
||||
.learn li {
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.learn p {
|
||||
font-size: 15px;
|
||||
font-weight: 300;
|
||||
line-height: 1.3;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#issue-count {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.quote {
|
||||
border: none;
|
||||
margin: 20px 0 60px 0;
|
||||
}
|
||||
|
||||
.quote p {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.quote p:before {
|
||||
content: '“';
|
||||
font-size: 50px;
|
||||
opacity: .15;
|
||||
position: absolute;
|
||||
top: -20px;
|
||||
left: 3px;
|
||||
}
|
||||
|
||||
.quote p:after {
|
||||
content: '”';
|
||||
font-size: 50px;
|
||||
opacity: .15;
|
||||
position: absolute;
|
||||
bottom: -42px;
|
||||
right: 3px;
|
||||
}
|
||||
|
||||
.quote footer {
|
||||
position: absolute;
|
||||
bottom: -40px;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.quote footer img {
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.quote footer a {
|
||||
margin-left: 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.speech-bubble {
|
||||
position: relative;
|
||||
padding: 10px;
|
||||
background: rgba(0, 0, 0, .04);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.speech-bubble:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
right: 30px;
|
||||
border: 13px solid transparent;
|
||||
border-top-color: rgba(0, 0, 0, .04);
|
||||
}
|
||||
|
||||
.learn-bar > .learn {
|
||||
position: absolute;
|
||||
width: 272px;
|
||||
top: 8px;
|
||||
left: -300px;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
background-color: rgba(255, 255, 255, .6);
|
||||
transition-property: left;
|
||||
transition-duration: 500ms;
|
||||
}
|
||||
|
||||
@media (min-width: 899px) {
|
||||
.learn-bar {
|
||||
width: auto;
|
||||
padding-left: 300px;
|
||||
}
|
||||
|
||||
.learn-bar > .learn {
|
||||
left: 8px;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
32
packages/core/test/bundling/animation_world/index.html
Normal file
32
packages/core/test/bundling/animation_world/index.html
Normal file
@ -0,0 +1,32 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="base.css">
|
||||
<link rel="stylesheet" href="animation_world.css">
|
||||
<title>Angular Hello World Example</title>
|
||||
</head>
|
||||
<body>
|
||||
<!-- The Angular application will be bootstrapped into this element. -->
|
||||
<animation-world></animation-world>
|
||||
|
||||
<!--
|
||||
Script tag which bootstraps the application. Use `?debug` in URL to select
|
||||
the debug version of the script.
|
||||
|
||||
There are two scripts sources: `bundle.min.js` and `bundle.min_debug.js` You can
|
||||
switch between which bundle the browser loads to experiment with the application.
|
||||
|
||||
- `bundle.min.js`: Is what the site would serve to their users. It has gone
|
||||
through rollup, build-optimizer, and uglify with tree shaking.
|
||||
- `bundle.min_debug.js`: Is what the developer would like to see when debugging
|
||||
the application. It has also done through full pipeline of rollup, build-optimizer,
|
||||
and uglify, however special flags were passed to uglify to prevent inlining and
|
||||
property renaming.
|
||||
-->
|
||||
<script>
|
||||
document.write('<script src="' +
|
||||
(document.location.search.endsWith('debug') ? '/bundle.min_debug.js' : '/bundle.min.js') +
|
||||
'"></' + 'script>');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
136
packages/core/test/bundling/animation_world/index.ts
Normal file
136
packages/core/test/bundling/animation_world/index.ts
Normal file
@ -0,0 +1,136 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import '@angular/core/test/bundling/util/src/reflect_metadata';
|
||||
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {Component, ElementRef, NgModule, ɵPlayState as PlayState, ɵPlayer as Player, ɵPlayerHandler as PlayerHandler, ɵaddPlayer as addPlayer, ɵrenderComponent as renderComponent} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'animation-world',
|
||||
template: `
|
||||
<nav>
|
||||
<button (click)="doAnimate()">Populate List</button>
|
||||
</nav>
|
||||
<div class="list">
|
||||
<div *ngFor="let item of items" class="record" [class]="makeClass(item)">
|
||||
{{ item }}
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
})
|
||||
class AnimationWorldComponent {
|
||||
items: any[] = [1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
private _hostElement: HTMLElement;
|
||||
|
||||
constructor(element: ElementRef) { this._hostElement = element.nativeElement; }
|
||||
|
||||
makeClass(index: number) { return `record-${index}`; }
|
||||
|
||||
doAnimate() {
|
||||
const elements = this._hostElement.querySelectorAll('div.record') as any as HTMLElement[];
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
const element = elements[i];
|
||||
const delay = i * 100;
|
||||
const player = buildAnimationPlayer(element, 'fadeInOut', `500ms ease-out ${delay}ms both`);
|
||||
addPlayer(element, player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({declarations: [AnimationWorldComponent], imports: [CommonModule]})
|
||||
class AnimationWorldModule {
|
||||
}
|
||||
|
||||
|
||||
function buildAnimationPlayer(element: HTMLElement, animationName: string, time: string): Player {
|
||||
return new SimpleKeyframePlayer(element, animationName, time);
|
||||
}
|
||||
|
||||
class SimpleKeyframePlayer implements Player {
|
||||
state = PlayState.Pending;
|
||||
parent: Player|null = null;
|
||||
private _animationStyle: string;
|
||||
private _listeners: {[stateName: string]: (() => any)[]} = {};
|
||||
constructor(private _element: HTMLElement, private _animationName: string, time: string) {
|
||||
this._animationStyle = `${time} ${_animationName}`;
|
||||
}
|
||||
private _start() {
|
||||
this._element.style.animation = this._animationStyle;
|
||||
const animationFn = (event: AnimationEvent) => {
|
||||
if (event.animationName == this._animationName) {
|
||||
this._element.removeEventListener('animationend', animationFn);
|
||||
this.finish();
|
||||
}
|
||||
};
|
||||
this._element.addEventListener('animationend', animationFn);
|
||||
}
|
||||
addEventListener(state: PlayState|string, cb: () => any): void {
|
||||
const key = state.toString();
|
||||
const arr = this._listeners[key] = (this._listeners[key] || []);
|
||||
arr.push(cb);
|
||||
}
|
||||
play(): void {
|
||||
if (this.state <= PlayState.Pending) {
|
||||
this._start();
|
||||
}
|
||||
if (this.state != PlayState.Running) {
|
||||
setAnimationPlayState(this._element, 'running');
|
||||
this.state = PlayState.Running;
|
||||
this._emit(this.state);
|
||||
}
|
||||
}
|
||||
pause(): void {
|
||||
if (this.state != PlayState.Paused) {
|
||||
setAnimationPlayState(this._element, 'paused');
|
||||
this.state = PlayState.Paused;
|
||||
this._emit(this.state);
|
||||
}
|
||||
}
|
||||
finish(): void {
|
||||
if (this.state < PlayState.Finished) {
|
||||
this._element.style.animation = '';
|
||||
this.state = PlayState.Finished;
|
||||
this._emit(this.state);
|
||||
}
|
||||
}
|
||||
destroy(): void {
|
||||
if (this.state < PlayState.Destroyed) {
|
||||
this.finish();
|
||||
this.state = PlayState.Destroyed;
|
||||
this._emit(this.state);
|
||||
}
|
||||
}
|
||||
capture(): any {}
|
||||
private _emit(state: PlayState) {
|
||||
const arr = this._listeners[state.toString()] || [];
|
||||
arr.forEach(cb => cb());
|
||||
}
|
||||
}
|
||||
|
||||
function setAnimationPlayState(element: HTMLElement, state: string) {
|
||||
element.style.animationPlayState = state;
|
||||
}
|
||||
|
||||
class AnimationDebugger implements PlayerHandler {
|
||||
private _players: Player[] = [];
|
||||
|
||||
flushPlayers() {
|
||||
this._players.forEach(player => {
|
||||
if (!player.parent) {
|
||||
player.play();
|
||||
}
|
||||
});
|
||||
this._players.length = 0;
|
||||
}
|
||||
|
||||
queuePlayer(player: Player): void { this._players.push(player); }
|
||||
}
|
||||
|
||||
const playerHandler = new AnimationDebugger();
|
||||
renderComponent(AnimationWorldComponent, {playerHandler});
|
@ -416,6 +416,9 @@
|
||||
{
|
||||
"name": "createEmbeddedViewAndNode"
|
||||
},
|
||||
{
|
||||
"name": "createEmptyStylingContext"
|
||||
},
|
||||
{
|
||||
"name": "createLContainer"
|
||||
},
|
||||
|
@ -1286,6 +1286,9 @@
|
||||
{
|
||||
"name": "createEmbeddedViewAndNode"
|
||||
},
|
||||
{
|
||||
"name": "createEmptyStylingContext"
|
||||
},
|
||||
{
|
||||
"name": "createInjector"
|
||||
},
|
||||
|
Reference in New Issue
Block a user