mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
@@ -134,6 +134,20 @@
|
||||
<TypeScriptCompile Include="apps\action-bar-demo\pages\page-title-icon.ts">
|
||||
<DependentUpon>page-title-icon.xml</DependentUpon>
|
||||
</TypeScriptCompile>
|
||||
<TypeScriptCompile Include="apps\animations\model.ts" />
|
||||
<TypeScriptCompile Include="apps\tests\ui\animation\animation-tests.ts" />
|
||||
<TypeScriptCompile Include="ui\animation\animation.d.ts" />
|
||||
<TypeScriptCompile Include="ui\animation\animation-common.ts">
|
||||
<DependentUpon>animation.d.ts</DependentUpon>
|
||||
</TypeScriptCompile>
|
||||
<TypeScriptCompile Include="ui\animation\animation.android.ts">
|
||||
<DependentUpon>animation.d.ts</DependentUpon>
|
||||
</TypeScriptCompile>
|
||||
<TypeScriptCompile Include="ui\animation\animation.ios.ts">
|
||||
<DependentUpon>animation.d.ts</DependentUpon>
|
||||
</TypeScriptCompile>
|
||||
<TypeScriptCompile Include="apps\animations\app.ts" />
|
||||
<TypeScriptCompile Include="apps\animations\main-page.ts" />
|
||||
<TypeScriptCompile Include="apps\gallery-app\views\list-picker.ts">
|
||||
<DependentUpon>list-picker.xml</DependentUpon>
|
||||
</TypeScriptCompile>
|
||||
@@ -791,6 +805,13 @@
|
||||
</Content>
|
||||
<Content Include="apps\action-bar-demo\pages\navigation-button.xml" />
|
||||
<Content Include="apps\action-bar-demo\test-icon.png" />
|
||||
<TypeScriptCompile Include="libjs.d.ts" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="apps\animations\app.css" />
|
||||
<Content Include="apps\animations\main-page.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</Content>
|
||||
<Content Include="apps\gallery-app\content\border.xml" />
|
||||
<Content Include="apps\gallery-app\app.css" />
|
||||
<Content Include="apps\gallery-app\views\list-picker.xml" />
|
||||
@@ -798,7 +819,9 @@
|
||||
<Content Include="apps\gallery-app\views\segmented-bar.xml" />
|
||||
<Content Include="apps\gallery-app\views\time-picker.xml" />
|
||||
<Content Include="apps\gallery-app\views\date-picker.xml" />
|
||||
<Content Include="apps\modal-views-demo\login-page.xml" />
|
||||
<Content Include="apps\modal-views-demo\login-page.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</Content>
|
||||
<Content Include="apps\pickers-demo\main-page.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</Content>
|
||||
@@ -1741,7 +1764,9 @@
|
||||
<Content Include="apps\modal-views-demo\package.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="apps\cuteness.unoptimized\package.json">
|
||||
<Content Include="apps\cuteness.unoptimized\package.json" />
|
||||
<Content Include="apps\animations\package.json" />
|
||||
<Content Include="ui\animation\package.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="apps\action-bar-demo\package.json" />
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
Use the frame in the following way:
|
||||
|
||||
### To navigate to the starting page of the application
|
||||
# Ani
|
||||
```javascript
|
||||
// put this in the bootstrap.js
|
||||
var app = require("application");
|
||||
|
||||
3
apps/animations/app.css
Normal file
3
apps/animations/app.css
Normal file
@@ -0,0 +1,3 @@
|
||||
page {
|
||||
/* CSS styles */
|
||||
}
|
||||
8
apps/animations/app.ts
Normal file
8
apps/animations/app.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import application = require("application");
|
||||
import trace = require("trace");
|
||||
|
||||
trace.enable();
|
||||
trace.setCategories(trace.categories.concat(trace.categories.Animation));
|
||||
|
||||
application.mainModule = "main-page";
|
||||
application.start();
|
||||
93
apps/animations/main-page.ts
Normal file
93
apps/animations/main-page.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import observable = require("data/observable");
|
||||
import pages = require("ui/page");
|
||||
import buttonModule = require("ui/button");
|
||||
import abs = require("ui/layouts/absolute-layout");
|
||||
import animationModule = require("ui/animation");
|
||||
import colorModule = require("color");
|
||||
import model = require("./model");
|
||||
|
||||
var vm = new model.ViewModel();
|
||||
|
||||
var page: pages.Page;
|
||||
var panel: abs.AbsoluteLayout;
|
||||
var button1: buttonModule.Button;
|
||||
var button2: buttonModule.Button;
|
||||
var button3: buttonModule.Button;
|
||||
var buttonAnimation: animationModule.Animation;
|
||||
var panelAnimation: animationModule.Animation;
|
||||
|
||||
export function pageLoaded(args: observable.EventData) {
|
||||
page = <pages.Page>args.object;
|
||||
page.bindingContext = vm;
|
||||
panel = page.getViewById<abs.AbsoluteLayout>("panel1");
|
||||
button1 = page.getViewById<buttonModule.Button>("button1");
|
||||
button2 = page.getViewById<buttonModule.Button>("button2");
|
||||
button3 = page.getViewById<buttonModule.Button>("button3");
|
||||
}
|
||||
|
||||
export function onSlideOut(args: observable.EventData) {
|
||||
console.log("onSlideOut");
|
||||
var curve = page.android ? new android.view.animation.AccelerateInterpolator(1) : UIViewAnimationCurve.UIViewAnimationCurveEaseIn;
|
||||
|
||||
var buttonAnimations = [
|
||||
{ target: button1, translate: { x: -240, y: 0 }, scale: { x: 0.5, y: 0.5 }, opacity: 0, duration: vm.duration, delay: 0, iterations: vm.iterations, curve: curve },
|
||||
{ target: button2, translate: { x: -240, y: 0 }, scale: { x: 0.5, y: 0.5 }, opacity: 0, duration: vm.duration, delay: vm.duration, iterations: vm.iterations, curve: curve },
|
||||
{ target: button3, translate: { x: -240, y: 0 }, scale: { x: 0.5, y: 0.5 }, opacity: 0, duration: vm.duration, delay: vm.duration * 2, iterations: vm.iterations, curve: curve },
|
||||
]
|
||||
buttonAnimation = new animationModule.Animation(buttonAnimations, vm.playSequentially);
|
||||
|
||||
panelAnimation = panel.createAnimation({ opacity: 0, scale: { x: 0.5, y: 0.5 }, rotate: -360, backgroundColor: new colorModule.Color("red"), duration: vm.duration, iterations: vm.iterations });
|
||||
|
||||
buttonAnimation.play().finished
|
||||
.then(() => panelAnimation.play().finished)
|
||||
.catch((e) => console.log(e.message));
|
||||
}
|
||||
|
||||
export function onSlideIn(args: observable.EventData) {
|
||||
console.log("onSlideIn");
|
||||
var curve = page.android ? new android.view.animation.DecelerateInterpolator(1) : UIViewAnimationCurve.UIViewAnimationCurveEaseOut;
|
||||
|
||||
panelAnimation = panel.createAnimation({ opacity: 1, scale: { x: 1, y: 1 }, rotate: 0, backgroundColor: new colorModule.Color("yellow"), duration: vm.duration, iterations: vm.iterations });
|
||||
|
||||
var buttonAnimations = [
|
||||
{ target: button3, translate: { x: 0, y: 0 }, scale: { x: 1, y: 1 }, opacity: 1, duration: vm.duration, delay: 0, iterations: vm.iterations, curve: curve },
|
||||
{ target: button2, translate: { x: 0, y: 0 }, scale: { x: 1, y: 1 }, opacity: 1, duration: vm.duration, delay: vm.duration, iterations: vm.iterations, curve: curve },
|
||||
{ target: button1, translate: { x: 0, y: 0 }, scale: { x: 1, y: 1 }, opacity: 1, duration: vm.duration, delay: vm.duration * 2, iterations: vm.iterations, curve: curve },
|
||||
]
|
||||
buttonAnimation = new animationModule.Animation(buttonAnimations, vm.playSequentially);
|
||||
|
||||
panelAnimation.play().finished
|
||||
.then(() => buttonAnimation.play().finished)
|
||||
.catch((e) => console.log(e.message));
|
||||
}
|
||||
|
||||
export function onCancel(args: observable.EventData) {
|
||||
console.log("onCancel");
|
||||
if (panelAnimation.isPlaying) {
|
||||
panelAnimation.cancel();
|
||||
}
|
||||
if (buttonAnimation.isPlaying) {
|
||||
buttonAnimation.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
export function onTap(args: observable.EventData) {
|
||||
console.log((<any>args.object).text);
|
||||
}
|
||||
|
||||
export function onSingle(args: observable.EventData) {
|
||||
console.log("onSingle");
|
||||
button1.animate({
|
||||
opacity: 0.75,
|
||||
backgroundColor: new colorModule.Color("Red"),
|
||||
translate: { x: 100, y: 100 },
|
||||
scale: { x: 2, y: 2 },
|
||||
rotate: 180,
|
||||
duration: vm.duration,
|
||||
delay: 0,
|
||||
iterations: vm.iterations,
|
||||
curve: button1.ios ? UIViewAnimationCurve.UIViewAnimationCurveEaseIn : new android.view.animation.AccelerateInterpolator(1),
|
||||
})
|
||||
.then(() => console.log("Animation finished"))
|
||||
.catch((e) => console.log(e.message));
|
||||
}
|
||||
30
apps/animations/main-page.xml
Normal file
30
apps/animations/main-page.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<Page xmlns="http://www.nativescript.org/tns.xsd" loaded="pageLoaded" id="mainPage">
|
||||
<StackLayout orientation="vertical">
|
||||
<StackLayout orientation="vertical" backgroundColor="LightGray" paddingTop="5" paddingBottom="5">
|
||||
|
||||
<Label text="{{ duration, 'Duration: ' + duration + ' ms' }}" width="180" marginTop="5" marginBottom="5"/>
|
||||
<Slider minValue="0" maxValue="10000" value="{{ duration }}" marginTop="5" marginBottom="5" marginLeft="10" marginRight="10"/>
|
||||
|
||||
<Label text="{{ iterations, 'Iterations: ' + iterations + ' times' }}" width="180" marginTop="5" marginBottom="5"/>
|
||||
<Slider minValue="0" maxValue="10" value="{{ iterations }}" marginTop="5" marginBottom="5" marginLeft="10" marginRight="10"/>
|
||||
|
||||
<StackLayout orientation="horizontal" marginTop="5" marginBottom="5" horizontalAlignment="center">
|
||||
<Label text="Play Sequentially?"/>
|
||||
<Switch marginLeft="10" checked="{{ playSequentially }}"/>
|
||||
</StackLayout>
|
||||
|
||||
<StackLayout orientation="horizontal" marginTop="5" marginBottom="5" horizontalAlignment="center" paddingLeft="5" paddingRight="5">
|
||||
<Button text="Out" tap="onSlideOut" width="75" marginLeft="5" marginRight="5" />
|
||||
<Button text="In" tap="onSlideIn" width="75" marginLeft="5" marginRight="5" />
|
||||
<Button text="Single" tap="onSingle" width="75" marginLeft="5" marginRight="5" />
|
||||
<Button text="Cancel" tap="onCancel" width="75" marginLeft="5" marginRight="5" />
|
||||
</StackLayout>
|
||||
|
||||
</StackLayout>
|
||||
<AbsoluteLayout id="panel1" backgroundColor="Yellow" width="300" height="190" clipToBounds="true" marginTop="10">
|
||||
<Button id="button1" text="Button 1" backgroundColor="White" width="180" height="50" left="60" top="10" tap="onTap"/>
|
||||
<Button id="button2" text="Button 2" backgroundColor="White" width="180" height="50" left="60" top="70" tap="onTap"/>
|
||||
<Button id="button3" text="Button 3" backgroundColor="White" width="180" height="50" left="60" top="130" tap="onTap"/>
|
||||
</AbsoluteLayout>
|
||||
</StackLayout>
|
||||
</Page>
|
||||
37
apps/animations/model.ts
Normal file
37
apps/animations/model.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import observable = require("data/observable");
|
||||
|
||||
export class ViewModel extends observable.Observable {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this._duration = 3000;
|
||||
this._iterations = 1;
|
||||
}
|
||||
|
||||
private _playSequentially: boolean;
|
||||
get playSequentially(): boolean {
|
||||
return this._playSequentially;
|
||||
}
|
||||
set playSequentially(value: boolean) {
|
||||
this._playSequentially = value;
|
||||
this.notify({ object: this, eventName: observable.Observable.propertyChangeEvent, propertyName: "playSequentially", value: value });
|
||||
}
|
||||
|
||||
private _duration: number;
|
||||
get duration(): number {
|
||||
return this._duration;
|
||||
}
|
||||
set duration(value: number) {
|
||||
this._duration = value;
|
||||
this.notify({ object: this, eventName: observable.Observable.propertyChangeEvent, propertyName: "duration", value: value });
|
||||
}
|
||||
|
||||
private _iterations: number;
|
||||
get iterations(): number {
|
||||
return this._iterations;
|
||||
}
|
||||
set iterations(value: number) {
|
||||
this._iterations = value;
|
||||
this.notify({ object: this, eventName: observable.Observable.propertyChangeEvent, propertyName: "iterations", value: value });
|
||||
}
|
||||
}
|
||||
2
apps/animations/package.json
Normal file
2
apps/animations/package.json
Normal file
@@ -0,0 +1,2 @@
|
||||
{ "name" : "animations",
|
||||
"main" : "app.js" }
|
||||
@@ -78,6 +78,7 @@ allTests["WEAK-EVENTS"] = require("./weak-event-listener-tests");
|
||||
allTests["REPEATER"] = require("./ui/repeater/repeater-tests");
|
||||
allTests["SEARCH-BAR"] = require('./ui/search-bar/search-bar-tests');
|
||||
allTests["CONNECTIVITY"] = require("./connectivity-tests");
|
||||
allTests["ANIMATION"] = require("./ui/animation/animation-tests");
|
||||
|
||||
if (!isRunningOnEmulator()) {
|
||||
allTests["LOCATION"] = require("./location-tests");
|
||||
|
||||
243
apps/tests/ui/animation/animation-tests.ts
Normal file
243
apps/tests/ui/animation/animation-tests.ts
Normal file
@@ -0,0 +1,243 @@
|
||||
import TKUnit = require("../../TKUnit");
|
||||
import helper = require("../helper");
|
||||
import pageModule = require("ui/page");
|
||||
import labelModule = require("ui/label");
|
||||
import stackLayoutModule = require("ui/layouts/stack-layout");
|
||||
import colorModule = require("color");
|
||||
|
||||
// <snippet module="ui/animation" title="animation">
|
||||
// # Animation
|
||||
// Animating view properties requires the "ui/animation" module.
|
||||
// ``` JavaScript
|
||||
import animation = require("ui/animation");
|
||||
// ```
|
||||
// </snippet>
|
||||
|
||||
export var test_AnimatingProperties = function (done) {
|
||||
var mainPage: pageModule.Page;
|
||||
var label: labelModule.Label;
|
||||
var pageFactory = function (): pageModule.Page {
|
||||
label = new labelModule.Label();
|
||||
label.text = "label";
|
||||
var stackLayout = new stackLayoutModule.StackLayout();
|
||||
stackLayout.addChild(label);
|
||||
mainPage = new pageModule.Page();
|
||||
mainPage.content = stackLayout;
|
||||
return mainPage;
|
||||
};
|
||||
|
||||
helper.navigate(pageFactory);
|
||||
TKUnit.waitUntilReady(() => { return label.isLoaded });
|
||||
|
||||
// <snippet module="ui/animation" title="animation">
|
||||
// # Animating properties
|
||||
// ``` JavaScript
|
||||
label.animate({
|
||||
opacity: 0.75,
|
||||
backgroundColor: new colorModule.Color("Red"),
|
||||
translate: { x: 100, y: 100 },
|
||||
scale: { x: 2, y: 2 },
|
||||
rotate: 180,
|
||||
duration: 1000,
|
||||
delay: 100,
|
||||
iterations: 3,
|
||||
curve: label.ios ? UIViewAnimationCurve.UIViewAnimationCurveEaseIn : new android.view.animation.AccelerateInterpolator(1),
|
||||
})
|
||||
.then(() => {
|
||||
console.log("Animation finished.");
|
||||
// <hide>
|
||||
helper.goBack();
|
||||
done();
|
||||
// </hide>
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e.message);
|
||||
// <hide>
|
||||
helper.goBack();
|
||||
done(e);
|
||||
// </hide>
|
||||
});
|
||||
// ```
|
||||
// </snippet>
|
||||
}
|
||||
|
||||
export var test_CancellingAnimation = function (done) {
|
||||
var mainPage: pageModule.Page;
|
||||
var label: labelModule.Label;
|
||||
var pageFactory = function (): pageModule.Page {
|
||||
label = new labelModule.Label();
|
||||
label.text = "label";
|
||||
var stackLayout = new stackLayoutModule.StackLayout();
|
||||
stackLayout.addChild(label);
|
||||
mainPage = new pageModule.Page();
|
||||
mainPage.content = stackLayout;
|
||||
return mainPage;
|
||||
};
|
||||
|
||||
helper.navigate(pageFactory);
|
||||
TKUnit.waitUntilReady(() => { return label.isLoaded });
|
||||
|
||||
// <snippet module="ui/animation" title="animation">
|
||||
// # Cancelling animation
|
||||
// ``` JavaScript
|
||||
var animation1 = label.createAnimation({ translate: { x: 100, y: 100 } });
|
||||
animation1.play().finished
|
||||
.then(() => {
|
||||
console.log("Animation finished");
|
||||
// <hide>
|
||||
helper.goBack();
|
||||
done();
|
||||
// </hide>
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log("Animation cancelled");
|
||||
// <hide>
|
||||
helper.goBack();
|
||||
done(e);
|
||||
// </hide>
|
||||
});
|
||||
animation1.cancel();
|
||||
// ```
|
||||
// </snippet>
|
||||
}
|
||||
|
||||
export var test_ChainingAnimations = function (done) {
|
||||
var mainPage: pageModule.Page;
|
||||
var label: labelModule.Label;
|
||||
var pageFactory = function (): pageModule.Page {
|
||||
label = new labelModule.Label();
|
||||
label.text = "label";
|
||||
var stackLayout = new stackLayoutModule.StackLayout();
|
||||
stackLayout.addChild(label);
|
||||
mainPage = new pageModule.Page();
|
||||
mainPage.content = stackLayout;
|
||||
return mainPage;
|
||||
};
|
||||
helper.navigate(pageFactory);
|
||||
TKUnit.waitUntilReady(() => { return label.isLoaded });
|
||||
|
||||
// <snippet module="ui/animation" title="animation">
|
||||
// # Chaining animations
|
||||
// ``` JavaScript
|
||||
label.animate({ opacity: 0 })
|
||||
.then(() => label.animate({ opacity: 1 }))
|
||||
.then(() => label.animate({ translate: { x: 200, y: 200 } }))
|
||||
.then(() => label.animate({ translate: { x: 0, y: 0 } }))
|
||||
.then(() => label.animate({ scale: { x: 5, y: 5 } }))
|
||||
.then(() => label.animate({ scale: { x: 1, y: 1 } }))
|
||||
.then(() => label.animate({ rotate: 180 }))
|
||||
.then(() => label.animate({ rotate: 0 }))
|
||||
.then(() => {
|
||||
console.log("Animation finished");
|
||||
// <hide>
|
||||
helper.goBack();
|
||||
done();
|
||||
// </hide>
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e.message);
|
||||
// <hide>
|
||||
helper.goBack();
|
||||
done(e);
|
||||
// </hide>
|
||||
});
|
||||
// ```
|
||||
// </snippet>
|
||||
}
|
||||
|
||||
export var test_ReusingAnimations = function (done) {
|
||||
var mainPage: pageModule.Page;
|
||||
var label: labelModule.Label;
|
||||
var pageFactory = function (): pageModule.Page {
|
||||
label = new labelModule.Label();
|
||||
label.text = "label";
|
||||
var stackLayout = new stackLayoutModule.StackLayout();
|
||||
stackLayout.addChild(label);
|
||||
mainPage = new pageModule.Page();
|
||||
mainPage.content = stackLayout;
|
||||
return mainPage;
|
||||
};
|
||||
|
||||
helper.navigate(pageFactory);
|
||||
TKUnit.waitUntilReady(() => { return label.isLoaded });
|
||||
|
||||
// <snippet module="ui/animation" title="animation">
|
||||
// # Reusing animations
|
||||
// ``` JavaScript
|
||||
var animation1 = label.createAnimation({ translate: { x: 100, y: 100 } });
|
||||
var animation2 = label.createAnimation({ translate: { x: 0, y: 0 } });
|
||||
|
||||
animation1.play().finished
|
||||
.then(() => animation2.play().finished)
|
||||
.then(() => animation1.play().finished)
|
||||
.then(() => animation2.play().finished)
|
||||
.then(() => animation1.play().finished)
|
||||
.then(() => animation2.play().finished)
|
||||
.then(() => {
|
||||
console.log("Animation finished");
|
||||
// <hide>
|
||||
helper.goBack();
|
||||
done();
|
||||
// </hide>
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e.message);
|
||||
// <hide>
|
||||
helper.goBack();
|
||||
done(e);
|
||||
// </hide>
|
||||
});
|
||||
// ```
|
||||
// </snippet>
|
||||
}
|
||||
|
||||
export var test_AnimatingMultipleViews = function (done) {
|
||||
var mainPage: pageModule.Page;
|
||||
var label1: labelModule.Label;
|
||||
var label2: labelModule.Label;
|
||||
var label3: labelModule.Label;
|
||||
var pageFactory = function (): pageModule.Page {
|
||||
label1 = new labelModule.Label();
|
||||
label1.text = "label1";
|
||||
label2 = new labelModule.Label();
|
||||
label2.text = "label2";
|
||||
label3 = new labelModule.Label();
|
||||
label3.text = "label3";
|
||||
var stackLayout = new stackLayoutModule.StackLayout();
|
||||
stackLayout.addChild(label1);
|
||||
stackLayout.addChild(label2);
|
||||
stackLayout.addChild(label3);
|
||||
mainPage = new pageModule.Page();
|
||||
mainPage.content = stackLayout;
|
||||
return mainPage;
|
||||
};
|
||||
helper.navigate(pageFactory);
|
||||
TKUnit.waitUntilReady(() => { return label1.isLoaded && label2.isLoaded });
|
||||
|
||||
// <snippet module="ui/animation" title="animation">
|
||||
// # Animating multiple views simultaneously
|
||||
// ``` JavaScript
|
||||
var animations: Array<animation.AnimationDefinition> = [
|
||||
{ target: label1, translate: { x: 200, y: 200 }, duration: 1000, delay: 0 },
|
||||
{ target: label2, translate: { x: 200, y: 200 }, duration: 1000, delay: 333 },
|
||||
{ target: label3, translate: { x: 200, y: 200 }, duration: 1000, delay: 666 },
|
||||
];
|
||||
var animation = new animation.Animation(animations);
|
||||
animation.play().finished
|
||||
.then(() => {
|
||||
console.log("Animations finished");
|
||||
// <hide>
|
||||
helper.goBack();
|
||||
done();
|
||||
// </hide>
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e.message);
|
||||
// <hide>
|
||||
helper.goBack();
|
||||
done(e);
|
||||
// </hide>
|
||||
});
|
||||
// ```
|
||||
// </snippet>
|
||||
}
|
||||
1
declarations.d.ts
vendored
1
declarations.d.ts
vendored
@@ -93,6 +93,7 @@ interface Console {
|
||||
log(message: any, ...formatParams: any[]): void;
|
||||
trace(): void;
|
||||
dump(obj: any): void;
|
||||
createDump(obj: any): string;
|
||||
dir(obj: any): void;
|
||||
}
|
||||
|
||||
|
||||
1
trace/trace.d.ts
vendored
1
trace/trace.d.ts
vendored
@@ -75,6 +75,7 @@ declare module "trace" {
|
||||
export var Test: string;
|
||||
export var Binding: string;
|
||||
export var Error: string;
|
||||
export var Animation: string;
|
||||
|
||||
export var All: string;
|
||||
|
||||
|
||||
@@ -113,7 +113,8 @@ export module categories {
|
||||
export var Test = "Test";
|
||||
export var Binding = "Binding";
|
||||
export var Error = "Error";
|
||||
export var All = VisualTreeEvents + "," + Layout + "," + Style + "," + ViewHierarchy + "," + NativeLifecycle + "," + Debug + "," + Navigation + "," + Test + "," + Binding + "," + Error;
|
||||
export var Animation = "Animation";
|
||||
export var All = VisualTreeEvents + "," + Layout + "," + Style + "," + ViewHierarchy + "," + NativeLifecycle + "," + Debug + "," + Navigation + "," + Test + "," + Binding + "," + Error + "," + Animation;
|
||||
|
||||
export var separator = ",";
|
||||
|
||||
|
||||
180
ui/animation/animation-common.ts
Normal file
180
ui/animation/animation-common.ts
Normal file
@@ -0,0 +1,180 @@
|
||||
import definition = require("ui/animation");
|
||||
import viewModule = require("ui/core/view");
|
||||
import trace = require("trace");
|
||||
|
||||
export module Properties {
|
||||
export var opacity = "opacity";
|
||||
export var backgroundColor = "backgroundColor";
|
||||
export var translate = "translate";
|
||||
export var rotate = "rotate";
|
||||
export var scale = "scale";
|
||||
}
|
||||
|
||||
export interface PropertyAnimation {
|
||||
target: viewModule.View;
|
||||
property: string;
|
||||
value: any;
|
||||
duration?: number;
|
||||
delay?: number;
|
||||
iterations?: number;
|
||||
curve?: any;
|
||||
}
|
||||
|
||||
export class Animation implements definition.Animation {
|
||||
public _propertyAnimations: Array<PropertyAnimation>;
|
||||
public _playSequentially: boolean;
|
||||
private _isPlaying: boolean;
|
||||
private _resolve;
|
||||
private _reject;
|
||||
private _animationFinishedPromise: Promise<void>;
|
||||
|
||||
public play(): Animation {
|
||||
if (this.isPlaying) {
|
||||
throw new Error("Animation is already playing.");
|
||||
}
|
||||
|
||||
this._isPlaying = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public cancel(): void {
|
||||
if (!this.isPlaying) {
|
||||
throw new Error("Animation is not currently playing.");
|
||||
}
|
||||
}
|
||||
|
||||
public get finished(): Promise<void> {
|
||||
return this._animationFinishedPromise;
|
||||
}
|
||||
|
||||
public get isPlaying(): boolean {
|
||||
return this._isPlaying;
|
||||
}
|
||||
|
||||
constructor(animationDefinitions: Array<definition.AnimationDefinition>, playSequentially?: boolean) {
|
||||
if (!animationDefinitions || animationDefinitions.length === 0) {
|
||||
throw new Error("No animation definitions specified");
|
||||
}
|
||||
|
||||
trace.write("Analyzing " + animationDefinitions.length + " animation definitions...", trace.categories.Animation);
|
||||
this._propertyAnimations = new Array<PropertyAnimation>();
|
||||
var i = 0;
|
||||
var length = animationDefinitions.length;
|
||||
for (; i < length; i++) {
|
||||
this._propertyAnimations = this._propertyAnimations.concat(Animation._createPropertyAnimations(animationDefinitions[i]));
|
||||
}
|
||||
|
||||
if (this._propertyAnimations.length === 0) {
|
||||
throw new Error("Nothing to animate.");
|
||||
}
|
||||
trace.write("Created " + this._propertyAnimations.length + " individual property animations.", trace.categories.Animation);
|
||||
|
||||
this._playSequentially = playSequentially;
|
||||
var that = this;
|
||||
this._animationFinishedPromise = new Promise<void>((resolve, reject) => {
|
||||
that._resolve = resolve;
|
||||
that._reject = reject;
|
||||
});
|
||||
}
|
||||
|
||||
public _resolveAnimationFinishedPromise() {
|
||||
this._isPlaying = false;
|
||||
this._resolve();
|
||||
}
|
||||
|
||||
public _rejectAnimationFinishedPromise() {
|
||||
this._isPlaying = false;
|
||||
this._reject(new Error("Animation cancelled."));
|
||||
}
|
||||
|
||||
private static _createPropertyAnimations(animationDefinition: definition.AnimationDefinition): Array<PropertyAnimation> {
|
||||
if (!animationDefinition.target) {
|
||||
throw new Error("No animation target specified.");
|
||||
}
|
||||
|
||||
var propertyAnimations = new Array<PropertyAnimation>();
|
||||
|
||||
// opacity
|
||||
if (animationDefinition.opacity !== undefined) {
|
||||
propertyAnimations.push({
|
||||
target: animationDefinition.target,
|
||||
property: Properties.opacity,
|
||||
value: animationDefinition.opacity,
|
||||
duration: animationDefinition.duration,
|
||||
delay: animationDefinition.delay,
|
||||
iterations: animationDefinition.iterations,
|
||||
curve: animationDefinition.curve
|
||||
});
|
||||
}
|
||||
|
||||
// backgroundColor
|
||||
if (animationDefinition.backgroundColor !== undefined) {
|
||||
propertyAnimations.push({
|
||||
target: animationDefinition.target,
|
||||
property: Properties.backgroundColor,
|
||||
value: animationDefinition.backgroundColor,
|
||||
duration: animationDefinition.duration,
|
||||
delay: animationDefinition.delay,
|
||||
iterations: animationDefinition.iterations,
|
||||
curve: animationDefinition.curve
|
||||
});
|
||||
}
|
||||
|
||||
// translate
|
||||
if (animationDefinition.translate !== undefined) {
|
||||
propertyAnimations.push({
|
||||
target: animationDefinition.target,
|
||||
property: Properties.translate,
|
||||
value: animationDefinition.translate,
|
||||
duration: animationDefinition.duration,
|
||||
delay: animationDefinition.delay,
|
||||
iterations: animationDefinition.iterations,
|
||||
curve: animationDefinition.curve
|
||||
});
|
||||
}
|
||||
|
||||
// scale
|
||||
if (animationDefinition.scale !== undefined) {
|
||||
propertyAnimations.push({
|
||||
target: animationDefinition.target,
|
||||
property: Properties.scale,
|
||||
value: animationDefinition.scale,
|
||||
duration: animationDefinition.duration,
|
||||
delay: animationDefinition.delay,
|
||||
iterations: animationDefinition.iterations,
|
||||
curve: animationDefinition.curve
|
||||
});
|
||||
}
|
||||
|
||||
// rotate
|
||||
if (animationDefinition.rotate !== undefined) {
|
||||
propertyAnimations.push({
|
||||
target: animationDefinition.target,
|
||||
property: Properties.rotate,
|
||||
value: animationDefinition.rotate,
|
||||
duration: animationDefinition.duration,
|
||||
delay: animationDefinition.delay,
|
||||
iterations: animationDefinition.iterations,
|
||||
curve: animationDefinition.curve
|
||||
});
|
||||
}
|
||||
|
||||
if (propertyAnimations.length === 0) {
|
||||
throw new Error("No animation property specified.");
|
||||
}
|
||||
|
||||
return propertyAnimations;
|
||||
}
|
||||
|
||||
public static _getAnimationInfo(animation: PropertyAnimation): string {
|
||||
return JSON.stringify({
|
||||
target: animation.target.id,
|
||||
property: animation.property,
|
||||
value: animation.value,
|
||||
duration: animation.duration,
|
||||
delay: animation.delay,
|
||||
iterations: animation.iterations,
|
||||
curve: animation.curve
|
||||
});
|
||||
}
|
||||
}
|
||||
256
ui/animation/animation.android.ts
Normal file
256
ui/animation/animation.android.ts
Normal file
@@ -0,0 +1,256 @@
|
||||
import definition = require("ui/animation");
|
||||
import common = require("ui/animation/animation-common");
|
||||
import utils = require("utils/utils");
|
||||
import color = require("color");
|
||||
import trace = require("trace");
|
||||
import types = require("utils/types");
|
||||
|
||||
// merge the exports of the common file with the exports of this file
|
||||
declare var exports;
|
||||
require("utils/module-merge").merge(common, exports);
|
||||
|
||||
var intType = java.lang.Integer.class.getField("TYPE").get(null);
|
||||
var floatType = java.lang.Float.class.getField("TYPE").get(null);
|
||||
var argbEvaluator: android.animation.ArgbEvaluator = new android.animation.ArgbEvaluator();
|
||||
|
||||
export class Animation extends common.Animation implements definition.Animation {
|
||||
private _animatorListener: android.animation.Animator.AnimatorListener;
|
||||
private _nativeAnimatorsArray: any;
|
||||
private _animatorSet: android.animation.AnimatorSet;
|
||||
private _animators: Array<android.animation.ObjectAnimator>;
|
||||
private _propertyUpdateCallbacks: Array<Function>;
|
||||
private _propertyResetCallbacks: Array<Function>;
|
||||
|
||||
public play(): Animation {
|
||||
super.play();
|
||||
|
||||
var i: number;
|
||||
var length: number;
|
||||
|
||||
this._animators = new Array<android.animation.ObjectAnimator>();
|
||||
this._propertyUpdateCallbacks = new Array<Function>();
|
||||
this._propertyResetCallbacks = new Array<Function>();
|
||||
|
||||
i = 0;
|
||||
length = this._propertyAnimations.length;
|
||||
for (; i < length; i++) {
|
||||
this._createAnimators(this._propertyAnimations[i]);
|
||||
}
|
||||
|
||||
if (this._animators.length === 0) {
|
||||
trace.write("Nothing to animate.", trace.categories.Animation);
|
||||
this._resolveAnimationFinishedPromise();
|
||||
return this;
|
||||
}
|
||||
|
||||
this._nativeAnimatorsArray = java.lang.reflect.Array.newInstance(android.animation.ObjectAnimator.class, this._animators.length);
|
||||
i = 0;
|
||||
length = this._animators.length;
|
||||
for (; i < length; i++) {
|
||||
this._nativeAnimatorsArray[i] = this._animators[i];
|
||||
}
|
||||
|
||||
this._animatorSet = new android.animation.AnimatorSet();
|
||||
this._animatorSet.addListener(this._animatorListener);
|
||||
if (this._playSequentially) {
|
||||
this._animatorSet.playSequentially(this._nativeAnimatorsArray);
|
||||
}
|
||||
else {
|
||||
this._animatorSet.playTogether(this._nativeAnimatorsArray);
|
||||
}
|
||||
|
||||
trace.write("Starting " + this._nativeAnimatorsArray.length + " animations " + (this._playSequentially ? "sequentially." : "together."), trace.categories.Animation);
|
||||
this._animatorSet.start();
|
||||
return this;
|
||||
}
|
||||
|
||||
public cancel(): void {
|
||||
super.cancel();
|
||||
trace.write("Cancelling AnimatorSet.", trace.categories.Animation);
|
||||
this._animatorSet.cancel();
|
||||
}
|
||||
|
||||
constructor(animationDefinitions: Array<definition.AnimationDefinition>, playSequentially?: boolean) {
|
||||
super(animationDefinitions, playSequentially);
|
||||
|
||||
var that = this;
|
||||
this._animatorListener = new android.animation.Animator.AnimatorListener({
|
||||
onAnimationStart: function (animator: android.animation.Animator): void {
|
||||
that._onAndroidAnimationStart();
|
||||
},
|
||||
onAnimationRepeat: function (animator: android.animation.Animator): void {
|
||||
that._onAndroidAnimationRepeat();
|
||||
},
|
||||
onAnimationEnd: function (animator: android.animation.Animator): void {
|
||||
that._onAndroidAnimationEnd();
|
||||
},
|
||||
onAnimationCancel: function (animator: android.animation.Animator): void {
|
||||
that._onAndroidAnimationCancel();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _onAndroidAnimationStart() {
|
||||
trace.write("AndroidAnimation._onAndroidAnimationStart.", trace.categories.Animation);
|
||||
}
|
||||
|
||||
private _onAndroidAnimationRepeat() {
|
||||
trace.write("AndroidAnimation._onAndroidAnimationRepeat.", trace.categories.Animation);
|
||||
}
|
||||
|
||||
private _onAndroidAnimationEnd() {
|
||||
trace.write("AndroidAnimation._onAndroidAnimationEnd.", trace.categories.Animation);
|
||||
|
||||
if (!this.isPlaying) {
|
||||
// It has been cancelled
|
||||
return;
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
var length = this._propertyUpdateCallbacks.length;
|
||||
for (; i < length; i++) {
|
||||
this._propertyUpdateCallbacks[i]();
|
||||
}
|
||||
this._resolveAnimationFinishedPromise();
|
||||
}
|
||||
|
||||
private _onAndroidAnimationCancel() {
|
||||
trace.write("AndroidAnimation._onAndroidAnimationCancel.", trace.categories.Animation);
|
||||
var i = 0;
|
||||
var length = this._propertyResetCallbacks.length;
|
||||
for (; i < length; i++) {
|
||||
this._propertyResetCallbacks[i]();
|
||||
}
|
||||
this._rejectAnimationFinishedPromise();
|
||||
}
|
||||
|
||||
private _createAnimators(propertyAnimation: common.PropertyAnimation): void {
|
||||
trace.write("Creating ObjectAnimator(s) for animation: " + common.Animation._getAnimationInfo(propertyAnimation) + "...", trace.categories.Animation);
|
||||
|
||||
if (types.isNullOrUndefined(propertyAnimation.target)) {
|
||||
throw new Error("Animation target cannot be null or undefined!");
|
||||
}
|
||||
|
||||
if (types.isNullOrUndefined(propertyAnimation.property)) {
|
||||
throw new Error("Animation property cannot be null or undefined!");
|
||||
}
|
||||
|
||||
if (types.isNullOrUndefined(propertyAnimation.value)) {
|
||||
throw new Error("Animation value cannot be null or undefined!");
|
||||
}
|
||||
|
||||
var nativeArray;
|
||||
var nativeView: android.view.View = (<android.view.View>propertyAnimation.target._nativeView);
|
||||
var animators = new Array<android.animation.ObjectAnimator>();
|
||||
var propertyUpdateCallbacks = new Array<Function>();
|
||||
var propertyResetCallbacks = new Array<Function>();
|
||||
var animator: android.animation.ObjectAnimator;
|
||||
var originalValue;
|
||||
var density = utils.layout.getDisplayDensity();
|
||||
switch (propertyAnimation.property) {
|
||||
|
||||
case common.Properties.opacity:
|
||||
originalValue = nativeView.getAlpha();
|
||||
if (propertyAnimation.value !== propertyAnimation.target.opacity) {
|
||||
nativeArray = java.lang.reflect.Array.newInstance(floatType, 1);
|
||||
nativeArray[0] = propertyAnimation.value;
|
||||
animators.push(android.animation.ObjectAnimator.ofFloat(nativeView, "alpha", nativeArray));
|
||||
propertyUpdateCallbacks.push(() => { propertyAnimation.target.opacity = propertyAnimation.value });
|
||||
propertyResetCallbacks.push(() => { nativeView.setAlpha(originalValue); });
|
||||
}
|
||||
break;
|
||||
|
||||
case common.Properties.backgroundColor:
|
||||
originalValue = nativeView.getBackground();
|
||||
if (!color.Color.equals(propertyAnimation.value, propertyAnimation.target.backgroundColor)) {
|
||||
nativeArray = java.lang.reflect.Array.newInstance(intType, 1);
|
||||
nativeArray[0] = (<color.Color>propertyAnimation.value).argb;
|
||||
//https://github.com/NativeScript/android-runtime/issues/168
|
||||
//animator = android.animation.ObjectAnimator.ofObject(nativeView, "backgroundColor", argbEvaluator, nativeArray);
|
||||
animator = android.animation.ObjectAnimator.ofInt(nativeView, "backgroundColor", nativeArray);
|
||||
animator.setEvaluator(argbEvaluator);
|
||||
animators.push(animator);
|
||||
propertyUpdateCallbacks.push(() => { propertyAnimation.target.backgroundColor = propertyAnimation.value; });
|
||||
propertyResetCallbacks.push(() => { nativeView.setBackground(originalValue); });
|
||||
}
|
||||
break;
|
||||
|
||||
case common.Properties.translate:
|
||||
originalValue = nativeView.getTranslationX();
|
||||
if (propertyAnimation.value.x * density !== originalValue) {
|
||||
nativeArray = java.lang.reflect.Array.newInstance(floatType, 1);
|
||||
nativeArray[0] = propertyAnimation.value.x * density;
|
||||
animators.push(android.animation.ObjectAnimator.ofFloat(nativeView, "translationX", nativeArray));
|
||||
propertyResetCallbacks.push(() => { nativeView.setTranslationX(originalValue); });
|
||||
}
|
||||
|
||||
originalValue = nativeView.getTranslationY();
|
||||
if (propertyAnimation.value.y * density !== originalValue) {
|
||||
nativeArray = java.lang.reflect.Array.newInstance(floatType, 1);
|
||||
nativeArray[0] = propertyAnimation.value.y * density;
|
||||
animators.push(android.animation.ObjectAnimator.ofFloat(nativeView, "translationY", nativeArray));
|
||||
propertyResetCallbacks.push(() => { nativeView.setTranslationY(originalValue); });
|
||||
}
|
||||
break;
|
||||
|
||||
case common.Properties.rotate:
|
||||
originalValue = nativeView.getRotation();
|
||||
if (propertyAnimation.value !== originalValue) {
|
||||
nativeArray = java.lang.reflect.Array.newInstance(floatType, 1);
|
||||
nativeArray[0] = propertyAnimation.value;
|
||||
animators.push(android.animation.ObjectAnimator.ofFloat(nativeView, "rotation", nativeArray));
|
||||
propertyResetCallbacks.push(() => { nativeView.setRotation(originalValue); });
|
||||
}
|
||||
break;
|
||||
|
||||
case common.Properties.scale:
|
||||
originalValue = nativeView.getScaleX();
|
||||
if (propertyAnimation.value.x !== originalValue) {
|
||||
nativeArray = java.lang.reflect.Array.newInstance(floatType, 1);
|
||||
nativeArray[0] = propertyAnimation.value.x;
|
||||
animators.push(android.animation.ObjectAnimator.ofFloat(nativeView, "scaleX", nativeArray));
|
||||
propertyResetCallbacks.push(() => { nativeView.setScaleX(originalValue); });
|
||||
}
|
||||
|
||||
originalValue = nativeView.getScaleY();
|
||||
if (propertyAnimation.value.y !== originalValue) {
|
||||
nativeArray = java.lang.reflect.Array.newInstance(floatType, 1);
|
||||
nativeArray[0] = propertyAnimation.value.y;
|
||||
animators.push(android.animation.ObjectAnimator.ofFloat(nativeView, "scaleY", nativeArray));
|
||||
propertyResetCallbacks.push(() => { nativeView.setScaleY(originalValue); });
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error("Cannot animate " + propertyAnimation.property);
|
||||
break;
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
var length = animators.length;
|
||||
for (; i < length; i++) {
|
||||
if (propertyAnimation.duration !== undefined) {
|
||||
animators[i].setDuration(propertyAnimation.duration);
|
||||
}
|
||||
if (propertyAnimation.delay !== undefined) {
|
||||
animators[i].setStartDelay(propertyAnimation.delay);
|
||||
}
|
||||
if (propertyAnimation.iterations !== undefined) {
|
||||
if (propertyAnimation.iterations === Number.POSITIVE_INFINITY) {
|
||||
animators[i].setRepeatCount(android.view.animation.Animation.INFINITE);
|
||||
}
|
||||
else {
|
||||
animators[i].setRepeatCount(propertyAnimation.iterations - 1);
|
||||
}
|
||||
}
|
||||
if (propertyAnimation.curve !== undefined) {
|
||||
animators[i].setInterpolator(propertyAnimation.curve);
|
||||
}
|
||||
trace.write("ObjectAnimator created: " + animators[i], trace.categories.Animation);
|
||||
}
|
||||
|
||||
this._animators = this._animators.concat(animators);
|
||||
this._propertyUpdateCallbacks = this._propertyUpdateCallbacks.concat(propertyUpdateCallbacks);
|
||||
this._propertyResetCallbacks = this._propertyResetCallbacks.concat(propertyResetCallbacks);
|
||||
}
|
||||
}
|
||||
80
ui/animation/animation.d.ts
vendored
Normal file
80
ui/animation/animation.d.ts
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
declare module "ui/animation" {
|
||||
import viewModule = require("ui/core/view");
|
||||
import colorModule = require("color");
|
||||
|
||||
/**
|
||||
* Defines animation options for the View.animate method.
|
||||
*/
|
||||
export interface AnimationDefinition {
|
||||
/**
|
||||
* The view whose property is to be animated.
|
||||
*/
|
||||
target?: viewModule.View;
|
||||
|
||||
/**
|
||||
* Animates the opacity of the view. Value should be a number between 0.0 and 1.0
|
||||
*/
|
||||
opacity?: number;
|
||||
|
||||
/**
|
||||
* Animates the backgroundColor of the view.
|
||||
*/
|
||||
backgroundColor?: colorModule.Color;
|
||||
|
||||
/**
|
||||
* Animates the translate affine transform of the view.
|
||||
*/
|
||||
translate?: Pair;
|
||||
|
||||
/**
|
||||
* Animates the scale affine transform of the view.
|
||||
*/
|
||||
scale?: Pair;
|
||||
|
||||
/**
|
||||
* Animates the rotate affine transform of the view. Value should be a number specifying the rotation amount in degrees.
|
||||
*/
|
||||
rotate?: number;
|
||||
|
||||
/**
|
||||
* The length of the animation in milliseconds. The default duration is 300 milliseconds.
|
||||
*/
|
||||
duration?: number;
|
||||
|
||||
/**
|
||||
* The amount of time, in milliseconds, to delay starting the animation.
|
||||
*/
|
||||
delay?: number;
|
||||
|
||||
/**
|
||||
* Specifies how many times the animation should be played. Default is 1.
|
||||
* iOS animations support fractional iterations, i.e. 1.5.
|
||||
* To repeat an animation infinitely, use Number.POSITIVE_INFINITY
|
||||
*/
|
||||
iterations?: number;
|
||||
|
||||
/**
|
||||
* An optional animation curve of type UIViewAnimationCurve for iOS or android.animation.TimeInterpolator for Android.
|
||||
*/
|
||||
curve?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a pair of values (horizontal and vertical) for translate and scale animations.
|
||||
*/
|
||||
export interface Pair {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a animation set.
|
||||
*/
|
||||
export class Animation {
|
||||
constructor(animationDefinitions: Array<AnimationDefinition>, playSequentially?: boolean);
|
||||
public play: () => Animation;
|
||||
public cancel: () => void;
|
||||
public finished: Promise<void>;
|
||||
public isPlaying: boolean;
|
||||
}
|
||||
}
|
||||
282
ui/animation/animation.ios.ts
Normal file
282
ui/animation/animation.ios.ts
Normal file
@@ -0,0 +1,282 @@
|
||||
import definition = require("ui/animation");
|
||||
import common = require("ui/animation/animation-common");
|
||||
import trace = require("trace");
|
||||
|
||||
// merge the exports of the common file with the exports of this file
|
||||
declare var exports;
|
||||
require("utils/module-merge").merge(common, exports);
|
||||
|
||||
var _transform = "_transform";
|
||||
var _skip = "_skip";
|
||||
|
||||
var FLT_MAX = 340282346638528859811704183484516925440.000000;
|
||||
|
||||
class AnimationDelegateImpl extends NSObject {
|
||||
static new(): AnimationDelegateImpl {
|
||||
return <AnimationDelegateImpl>super.new();
|
||||
}
|
||||
|
||||
private _finishedCallback: Function;
|
||||
|
||||
public initWithFinishedCallback(finishedCallback: Function): AnimationDelegateImpl {
|
||||
this._finishedCallback = finishedCallback;
|
||||
return this;
|
||||
}
|
||||
|
||||
public animationWillStart(animationID: string, context: any): void {
|
||||
trace.write("AnimationDelegateImpl.animationWillStart, animationID: " + animationID, trace.categories.Animation);
|
||||
}
|
||||
|
||||
public animationDidStop(animationID: string, finished: boolean, context: any): void {
|
||||
trace.write("AnimationDelegateImpl.animationDidStop, animationID: " + animationID + ", finished: " + finished, trace.categories.Animation);
|
||||
if (this._finishedCallback) {
|
||||
var cancelled = !finished;
|
||||
// This could either be the master finishedCallback or an nextAnimationCallback depending on the playSequentially argument values.
|
||||
this._finishedCallback(cancelled);
|
||||
}
|
||||
}
|
||||
|
||||
public static ObjCExposedMethods = {
|
||||
"animationWillStart": { returns: interop.types.void, params: [NSString, NSObject] },
|
||||
"animationDidStop": { returns: interop.types.void, params: [NSString, NSNumber, NSObject] }
|
||||
};
|
||||
}
|
||||
|
||||
export class Animation extends common.Animation implements definition.Animation {
|
||||
private _iOSAnimationFunction: Function;
|
||||
private _finishedAnimations: number;
|
||||
private _cancelledAnimations: number;
|
||||
private _mergedPropertyAnimations: Array<common.PropertyAnimation>;
|
||||
|
||||
public play(): Animation {
|
||||
super.play();
|
||||
|
||||
this._finishedAnimations = 0;
|
||||
this._cancelledAnimations = 0;
|
||||
this._iOSAnimationFunction();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public cancel(): void {
|
||||
super.cancel();
|
||||
|
||||
var i = 0;
|
||||
var length = this._mergedPropertyAnimations.length;
|
||||
for (; i < length; i++) {
|
||||
(<UIView>this._mergedPropertyAnimations[i].target._nativeView).layer.removeAllAnimations();
|
||||
if ((<any>this._mergedPropertyAnimations[i])._propertyResetCallback) {
|
||||
(<any>this._mergedPropertyAnimations[i])._propertyResetCallback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constructor(animationDefinitions: Array<definition.AnimationDefinition>, playSequentially?: boolean) {
|
||||
super(animationDefinitions, playSequentially);
|
||||
|
||||
trace.write("Non-merged Property Animations: " + this._propertyAnimations.length, trace.categories.Animation);
|
||||
this._mergedPropertyAnimations = Animation._mergeAffineTransformAnimations(this._propertyAnimations);
|
||||
trace.write("Merged Property Animations: " + this._mergedPropertyAnimations.length, trace.categories.Animation);
|
||||
|
||||
var that = this;
|
||||
var animationFinishedCallback = (cancelled: boolean) => {
|
||||
if (that._playSequentially) {
|
||||
// This function will be called by the last animation when done or by another animation if the user cancels them halfway through.
|
||||
if (cancelled) {
|
||||
that._rejectAnimationFinishedPromise();
|
||||
}
|
||||
else {
|
||||
that._resolveAnimationFinishedPromise();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// This callback will be called by each INDIVIDUAL animation when it finishes or is cancelled.
|
||||
if (cancelled) {
|
||||
that._cancelledAnimations++;
|
||||
}
|
||||
else {
|
||||
that._finishedAnimations++;
|
||||
}
|
||||
|
||||
if (that._cancelledAnimations === that._mergedPropertyAnimations.length) {
|
||||
trace.write(that._cancelledAnimations + " animations cancelled.", trace.categories.Animation);
|
||||
that._rejectAnimationFinishedPromise();
|
||||
}
|
||||
else if (that._finishedAnimations === that._mergedPropertyAnimations.length) {
|
||||
trace.write(that._finishedAnimations + " animations finished.", trace.categories.Animation);
|
||||
that._resolveAnimationFinishedPromise();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this._iOSAnimationFunction = Animation._createiOSAnimationFunction(this._mergedPropertyAnimations, 0, this._playSequentially, animationFinishedCallback);
|
||||
}
|
||||
|
||||
private static _createiOSAnimationFunction(propertyAnimations: Array<common.PropertyAnimation>, index: number, playSequentially: boolean, finishedCallback: (cancelled?: boolean) => void): Function {
|
||||
return (cancelled?: boolean) => {
|
||||
if (cancelled && finishedCallback) {
|
||||
trace.write("Animation " + (index - 1).toString() + " was cancelled. Will skip the rest of animations and call finishedCallback(true).", trace.categories.Animation);
|
||||
finishedCallback(cancelled);
|
||||
return;
|
||||
}
|
||||
|
||||
var animation = propertyAnimations[index];
|
||||
var nativeView = (<UIView>animation.target._nativeView);
|
||||
|
||||
var nextAnimationCallback: Function;
|
||||
var animationDelegate: AnimationDelegateImpl;
|
||||
if (index === propertyAnimations.length - 1) {
|
||||
// This is the last animation, so tell it to call the master finishedCallback when done.
|
||||
animationDelegate = AnimationDelegateImpl.new().initWithFinishedCallback(finishedCallback);
|
||||
}
|
||||
else {
|
||||
nextAnimationCallback = Animation._createiOSAnimationFunction(propertyAnimations, index + 1, playSequentially, finishedCallback);
|
||||
// If animations are to be played sequentially, tell it to start the next animation when done.
|
||||
// If played together, all individual animations will call the master finishedCallback, which increments a counter every time it is called.
|
||||
animationDelegate = AnimationDelegateImpl.new().initWithFinishedCallback(playSequentially ? nextAnimationCallback : finishedCallback);
|
||||
}
|
||||
|
||||
trace.write("UIView.beginAnimationsContext(" + index + "): " + common.Animation._getAnimationInfo(animation), trace.categories.Animation);
|
||||
UIView.beginAnimationsContext(index.toString(), null);
|
||||
|
||||
if (animationDelegate) {
|
||||
UIView.setAnimationDelegate(animationDelegate);
|
||||
UIView.setAnimationWillStartSelector("animationWillStart");
|
||||
UIView.setAnimationDidStopSelector("animationDidStop");
|
||||
}
|
||||
|
||||
if (animation.duration !== undefined) {
|
||||
UIView.setAnimationDuration(animation.duration / 1000.0);
|
||||
}
|
||||
else {
|
||||
UIView.setAnimationDuration(0.3); //Default duration.
|
||||
}
|
||||
if (animation.delay !== undefined) {
|
||||
UIView.setAnimationDelay(animation.delay / 1000.0);
|
||||
}
|
||||
if (animation.iterations !== undefined) {
|
||||
if (animation.iterations === Number.POSITIVE_INFINITY) {
|
||||
UIView.setAnimationRepeatCount(FLT_MAX);
|
||||
}
|
||||
else {
|
||||
UIView.setAnimationRepeatCount(animation.iterations - 1);
|
||||
}
|
||||
}
|
||||
if (animation.curve !== undefined) {
|
||||
UIView.setAnimationCurve(animation.curve);
|
||||
}
|
||||
|
||||
var originalValue;
|
||||
switch (animation.property) {
|
||||
case common.Properties.opacity:
|
||||
originalValue = animation.target.opacity;
|
||||
(<any>animation)._propertyResetCallback = () => { animation.target.opacity = originalValue };
|
||||
animation.target.opacity = animation.value;
|
||||
break;
|
||||
case common.Properties.backgroundColor:
|
||||
originalValue = animation.target.backgroundColor;
|
||||
(<any>animation)._propertyResetCallback = () => { animation.target.backgroundColor = originalValue };
|
||||
animation.target.backgroundColor = animation.value;
|
||||
break;
|
||||
case _transform:
|
||||
originalValue = nativeView.transform;
|
||||
(<any>animation)._propertyResetCallback = () => { nativeView.transform = originalValue };
|
||||
nativeView.transform = animation.value;
|
||||
break;
|
||||
default:
|
||||
throw new Error("Cannot animate " + animation.property);
|
||||
break;
|
||||
}
|
||||
|
||||
trace.write("UIView.commitAnimations " + index, trace.categories.Animation);
|
||||
UIView.commitAnimations();
|
||||
|
||||
if (!playSequentially && nextAnimationCallback) {
|
||||
nextAnimationCallback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static _isAffineTransform(property: string): boolean {
|
||||
return property === _transform
|
||||
|| property === common.Properties.translate
|
||||
|| property === common.Properties.rotate
|
||||
|| property === common.Properties.scale;
|
||||
}
|
||||
|
||||
private static _canBeMerged(animation1: common.PropertyAnimation, animation2: common.PropertyAnimation) {
|
||||
var result =
|
||||
Animation._isAffineTransform(animation1.property) &&
|
||||
Animation._isAffineTransform(animation2.property) &&
|
||||
animation1.target === animation2.target &&
|
||||
animation1.duration === animation2.duration &&
|
||||
animation1.delay === animation2.delay &&
|
||||
animation1.iterations === animation2.iterations &&
|
||||
animation1.curve === animation2.curve;
|
||||
return result;
|
||||
}
|
||||
|
||||
private static _affineTransform(matrix: CGAffineTransform, property: string, value: any): CGAffineTransform {
|
||||
switch (property) {
|
||||
case common.Properties.translate:
|
||||
return CGAffineTransformTranslate(matrix, value.x, value.y);
|
||||
case common.Properties.rotate:
|
||||
return CGAffineTransformRotate(matrix, value * Math.PI / 180);
|
||||
case common.Properties.scale:
|
||||
return CGAffineTransformScale(matrix, value.x, value.y);
|
||||
default:
|
||||
throw new Error("Cannot create transform for" + property);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static _mergeAffineTransformAnimations(propertyAnimations: Array<common.PropertyAnimation>): Array<common.PropertyAnimation> {
|
||||
var result = new Array<common.PropertyAnimation>();
|
||||
|
||||
var i = 0;
|
||||
var j;
|
||||
var length = propertyAnimations.length;
|
||||
for (; i < length; i++) {
|
||||
if (propertyAnimations[i].property !== _skip) {
|
||||
|
||||
if (!Animation._isAffineTransform(propertyAnimations[i].property)) {
|
||||
// This is not an affine transform animation, so there is nothing to merge.
|
||||
result.push(propertyAnimations[i]);
|
||||
}
|
||||
else {
|
||||
|
||||
// This animation has not been merged anywhere. Create a new transform animation.
|
||||
var newTransformAnimation: common.PropertyAnimation = {
|
||||
target: propertyAnimations[i].target,
|
||||
property: _transform,
|
||||
value: Animation._affineTransform(CGAffineTransformIdentity, propertyAnimations[i].property, propertyAnimations[i].value),
|
||||
duration: propertyAnimations[i].duration,
|
||||
delay: propertyAnimations[i].delay,
|
||||
iterations: propertyAnimations[i].iterations,
|
||||
iosUIViewAnimationCurve: propertyAnimations[i].curve
|
||||
};
|
||||
trace.write("Created new transform animation: " + common.Animation._getAnimationInfo(newTransformAnimation), trace.categories.Animation);
|
||||
|
||||
j = i + 1;
|
||||
if (j < length) {
|
||||
// Merge all compatible affine transform animations to the right into this new animation.
|
||||
for (; j < length; j++) {
|
||||
if (Animation._canBeMerged(propertyAnimations[i], propertyAnimations[j])) {
|
||||
trace.write("Merging animations: " + common.Animation._getAnimationInfo(newTransformAnimation) + " + " + common.Animation._getAnimationInfo(propertyAnimations[j]) + " = ", trace.categories.Animation);
|
||||
trace.write("New native transform is: " + NSStringFromCGAffineTransform(newTransformAnimation.value), trace.categories.Animation);
|
||||
newTransformAnimation.value = Animation._affineTransform(newTransformAnimation.value, propertyAnimations[j].property, propertyAnimations[j].value);
|
||||
|
||||
// Mark that it has been merged so we can skip it on our outer loop.
|
||||
propertyAnimations[j].property = _skip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.push(newTransformAnimation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
2
ui/animation/package.json
Normal file
2
ui/animation/package.json
Normal file
@@ -0,0 +1,2 @@
|
||||
{ "name" : "animation",
|
||||
"main" : "animation.js" }
|
||||
@@ -12,6 +12,7 @@ import styleScope = require("ui/styling/style-scope");
|
||||
import enums = require("ui/enums");
|
||||
import utils = require("utils/utils");
|
||||
import color = require("color");
|
||||
import animationModule = require("ui/animation");
|
||||
|
||||
export function getViewById(view: View, id: string): View {
|
||||
if (!view) {
|
||||
@@ -967,4 +968,14 @@ export class View extends proxy.ProxyObject implements definition.View {
|
||||
public focus(): boolean {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public animate(animation: animationModule.AnimationDefinition): Promise<void> {
|
||||
return this.createAnimation(animation).play().finished;
|
||||
}
|
||||
|
||||
public createAnimation(animation: animationModule.AnimationDefinition): animationModule.Animation {
|
||||
var that = this;
|
||||
animation.target = that;
|
||||
return new animationModule.Animation([animation]);
|
||||
}
|
||||
}
|
||||
5
ui/core/view.d.ts
vendored
5
ui/core/view.d.ts
vendored
@@ -5,6 +5,7 @@ declare module "ui/core/view" {
|
||||
import gestures = require("ui/gestures");
|
||||
import color = require("color");
|
||||
import observable = require("data/observable");
|
||||
import animation = require("ui/animation");
|
||||
|
||||
/**
|
||||
* Gets a child view by id.
|
||||
@@ -403,6 +404,9 @@ declare module "ui/core/view" {
|
||||
*/
|
||||
on(event: "unloaded", callback: (args: observable.EventData) => void, thisArg?: any);
|
||||
|
||||
public animate(options: animation.AnimationDefinition): Promise<void>;
|
||||
public createAnimation(options: animation.AnimationDefinition): animation.Animation;
|
||||
|
||||
// Lifecycle events
|
||||
onLoaded(): void;
|
||||
onUnloaded(): void;
|
||||
@@ -521,4 +525,5 @@ declare module "ui/core/view" {
|
||||
*/
|
||||
_applyXmlAttribute(attributeName: string, attrValue: any): boolean;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,31 @@
|
||||
import definition = require("ui/layouts/layout");
|
||||
import view = require("ui/core/view");
|
||||
import dependencyObservable = require("ui/core/dependency-observable");
|
||||
import proxy = require("ui/core/proxy");
|
||||
|
||||
function onClipToBoundsPropertyChanged(data: dependencyObservable.PropertyChangeData) {
|
||||
var nativeView = (<Layout>data.object)._nativeView;
|
||||
if (!nativeView) {
|
||||
return;
|
||||
}
|
||||
var value = <boolean>data.newValue;
|
||||
|
||||
if (nativeView instanceof UIView) {
|
||||
(<UIView>nativeView).clipsToBounds = value;
|
||||
}
|
||||
else if (nativeView instanceof android.view.ViewGroup) {
|
||||
(<android.view.ViewGroup>nativeView).setClipChildren(value);
|
||||
}
|
||||
}
|
||||
|
||||
var clipToBoundsProperty = new dependencyObservable.Property(
|
||||
"clipToBounds",
|
||||
"Layout",
|
||||
new proxy.PropertyMetadata(undefined, dependencyObservable.PropertyMetadataSettings.None, onClipToBoundsPropertyChanged)
|
||||
);
|
||||
|
||||
export class Layout extends view.CustomLayoutView implements definition.Layout, view.AddChildFromBuilder {
|
||||
|
||||
public static clipToBoundsProperty = new dependencyObservable.Property("clipToBounds", "Layout",
|
||||
new dependencyObservable.PropertyMetadata(true, dependencyObservable.PropertyMetadataSettings.None, Layout.onClipToBoundsPropertyChanged));
|
||||
public static clipToBoundsProperty = clipToBoundsProperty;
|
||||
|
||||
private _subViews: Array<view.View> = new Array<view.View>();
|
||||
|
||||
@@ -96,15 +116,10 @@ export class Layout extends view.CustomLayoutView implements definition.Layout,
|
||||
this.style.paddingLeft = value;
|
||||
}
|
||||
|
||||
private static onClipToBoundsPropertyChanged(data: dependencyObservable.PropertyChangeData) {
|
||||
var layout = <Layout>data.object;
|
||||
var nativeView: Object = layout._nativeView;
|
||||
var value = <boolean>data.newValue;
|
||||
if (nativeView instanceof android.view.ViewGroup) {
|
||||
(<android.view.ViewGroup>nativeView).setClipChildren(value);
|
||||
}
|
||||
else if (nativeView instanceof UIView) {
|
||||
(<UIView>nativeView).clipsToBounds = value;
|
||||
}
|
||||
get clipToBounds(): boolean {
|
||||
return this._getValue(Layout.clipToBoundsProperty);
|
||||
}
|
||||
set clipToBounds(value: boolean) {
|
||||
this._setValue(Layout.clipToBoundsProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user