diff --git a/CrossPlatformModules.csproj b/CrossPlatformModules.csproj
index 4f26631e7..8c8503fa0 100644
--- a/CrossPlatformModules.csproj
+++ b/CrossPlatformModules.csproj
@@ -79,6 +79,9 @@
data-binding.xml
+
+
+
@@ -107,6 +110,9 @@
+
+ Designer
+
Always
@@ -1858,6 +1864,7 @@
PreserveNewest
+
@@ -1950,7 +1957,7 @@
False
-
+
\ No newline at end of file
diff --git a/apps/tests/ui/animation/animation-tests.ts b/apps/tests/ui/animation/animation-tests.ts
index 2b983a335..e8342c87e 100644
--- a/apps/tests/ui/animation/animation-tests.ts
+++ b/apps/tests/ui/animation/animation-tests.ts
@@ -61,7 +61,7 @@ export var test_AnimatingProperties = function (done) {
//
}
- export var test_CancellingAnimation = function (done) {
+export var test_CancellingAnimation = function (done) {
var mainPage: pageModule.Page;
var label: labelModule.Label;
var pageFactory = function (): pageModule.Page {
@@ -241,3 +241,146 @@ export var test_ChainingAnimations = function (done) {
// ```
//
}
+
+export var test_AnimateOpacity = 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 });
+
+ label.animate({opacity: 0.75})
+ .then(() => {
+ TKUnit.assert(label.opacity === 0.75);
+ helper.goBack();
+ done();
+ })
+ .catch((e) => {
+ helper.goBack();
+ done(e);
+ });
+}
+
+export var test_AnimateBackgroundColor = 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 });
+ var red = new colorModule.Color("Red");
+
+ label.animate({ backgroundColor: red })
+ .then(() => {
+ TKUnit.assert(label.backgroundColor.equals(red));
+ helper.goBack();
+ done();
+ })
+ .catch((e) => {
+ helper.goBack();
+ done(e);
+ });
+}
+
+export var test_AnimateTranslate = 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 });
+
+ label.animate({ translate: {x: 100, y: 200} })
+ .then(() => {
+ TKUnit.assert(label.translateX === 100);
+ TKUnit.assert(label.translateY === 200);
+ helper.goBack();
+ done();
+ })
+ .catch((e) => {
+ helper.goBack();
+ done(e);
+ });
+}
+
+export var test_AnimateScale = 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 });
+
+ label.animate({ scale: { x: 1, y: 2 } })
+ .then(() => {
+ TKUnit.assert(label.scaleX === 1);
+ TKUnit.assert(label.scaleY === 2);
+ helper.goBack();
+ done();
+ })
+ .catch((e) => {
+ helper.goBack();
+ done(e);
+ });
+}
+
+export var test_AnimateRotate = 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 });
+
+ label.animate({ rotate: 123 })
+ .then(() => {
+ TKUnit.assert(label.rotate === 123);
+ helper.goBack();
+ done();
+ })
+ .catch((e) => {
+ helper.goBack();
+ done(e);
+ });
+}
diff --git a/apps/transforms/app.ts b/apps/transforms/app.ts
new file mode 100644
index 000000000..2baebd169
--- /dev/null
+++ b/apps/transforms/app.ts
@@ -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();
diff --git a/apps/transforms/main-page.ts b/apps/transforms/main-page.ts
new file mode 100644
index 000000000..94782be09
--- /dev/null
+++ b/apps/transforms/main-page.ts
@@ -0,0 +1,8 @@
+import observable = require("data/observable");
+import pages = require("ui/page");
+import model = require("./model");
+
+export function pageLoaded(args: observable.EventData) {
+ var page = args.object;
+ page.bindingContext = new model.ViewModel();
+}
\ No newline at end of file
diff --git a/apps/transforms/main-page.xml b/apps/transforms/main-page.xml
new file mode 100644
index 000000000..abf6e9e8e
--- /dev/null
+++ b/apps/transforms/main-page.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/transforms/model.ts b/apps/transforms/model.ts
new file mode 100644
index 000000000..a103b8a96
--- /dev/null
+++ b/apps/transforms/model.ts
@@ -0,0 +1,52 @@
+import observable = require("data/observable");
+
+export class ViewModel extends observable.Observable {
+ constructor() {
+ super();
+ }
+
+ private _translateX = 0;;
+ get translateX(): number {
+ return this._translateX;
+ }
+ set translateX(value: number) {
+ this._translateX = value;
+ this.notify({ object: this, eventName: observable.Observable.propertyChangeEvent, propertyName: "translateX", value: value });
+ }
+
+ private _translateY = 0;
+ get translateY(): number {
+ return this._translateY;
+ }
+ set translateY(value: number) {
+ this._translateY = value;
+ this.notify({ object: this, eventName: observable.Observable.propertyChangeEvent, propertyName: "translateY", value: value });
+ }
+
+ private _scaleX = 1;
+ get scaleX(): number {
+ return this._scaleX;
+ }
+ set scaleX(value: number) {
+ this._scaleX = value;
+ this.notify({ object: this, eventName: observable.Observable.propertyChangeEvent, propertyName: "scaleX", value: value });
+ }
+
+ private _scaleY = 1;
+ get scaleY(): number {
+ return this._scaleY;
+ }
+ set scaleY(value: number) {
+ this._scaleY = value;
+ this.notify({ object: this, eventName: observable.Observable.propertyChangeEvent, propertyName: "scaleY", value: value });
+ }
+
+ private _rotate = 0;
+ get rotate(): number {
+ return this._rotate;
+ }
+ set rotate(value: number) {
+ this._rotate = value;
+ this.notify({ object: this, eventName: observable.Observable.propertyChangeEvent, propertyName: "rotate", value: value });
+ }
+}
\ No newline at end of file
diff --git a/apps/transforms/package.json b/apps/transforms/package.json
new file mode 100644
index 000000000..24aa2dcee
--- /dev/null
+++ b/apps/transforms/package.json
@@ -0,0 +1,2 @@
+{ "name" : "transforms",
+ "main" : "app.js" }
\ No newline at end of file
diff --git a/ui/animation/animation.android.ts b/ui/animation/animation.android.ts
index 906ce56ea..e3c8b06b2 100644
--- a/ui/animation/animation.android.ts
+++ b/ui/animation/animation.android.ts
@@ -183,6 +183,7 @@ export class Animation extends common.Animation implements definition.Animation
nativeArray = java.lang.reflect.Array.newInstance(floatType, 1);
nativeArray[0] = propertyAnimation.value.x * density;
animators.push(android.animation.ObjectAnimator.ofFloat(nativeView, "translationX", nativeArray));
+ propertyUpdateCallbacks.push(() => { propertyAnimation.target.translateX = propertyAnimation.value.x; });
propertyResetCallbacks.push(() => { nativeView.setTranslationX(originalValue); });
}
@@ -191,6 +192,7 @@ export class Animation extends common.Animation implements definition.Animation
nativeArray = java.lang.reflect.Array.newInstance(floatType, 1);
nativeArray[0] = propertyAnimation.value.y * density;
animators.push(android.animation.ObjectAnimator.ofFloat(nativeView, "translationY", nativeArray));
+ propertyUpdateCallbacks.push(() => { propertyAnimation.target.translateY = propertyAnimation.value.y; });
propertyResetCallbacks.push(() => { nativeView.setTranslationY(originalValue); });
}
break;
@@ -201,6 +203,7 @@ export class Animation extends common.Animation implements definition.Animation
nativeArray = java.lang.reflect.Array.newInstance(floatType, 1);
nativeArray[0] = propertyAnimation.value;
animators.push(android.animation.ObjectAnimator.ofFloat(nativeView, "rotation", nativeArray));
+ propertyUpdateCallbacks.push(() => { propertyAnimation.target.rotate = propertyAnimation.value; });
propertyResetCallbacks.push(() => { nativeView.setRotation(originalValue); });
}
break;
@@ -211,6 +214,7 @@ export class Animation extends common.Animation implements definition.Animation
nativeArray = java.lang.reflect.Array.newInstance(floatType, 1);
nativeArray[0] = propertyAnimation.value.x;
animators.push(android.animation.ObjectAnimator.ofFloat(nativeView, "scaleX", nativeArray));
+ propertyUpdateCallbacks.push(() => { propertyAnimation.target.scaleX = propertyAnimation.value.x; });
propertyResetCallbacks.push(() => { nativeView.setScaleX(originalValue); });
}
@@ -219,6 +223,7 @@ export class Animation extends common.Animation implements definition.Animation
nativeArray = java.lang.reflect.Array.newInstance(floatType, 1);
nativeArray[0] = propertyAnimation.value.y;
animators.push(android.animation.ObjectAnimator.ofFloat(nativeView, "scaleY", nativeArray));
+ propertyUpdateCallbacks.push(() => { propertyAnimation.target.scaleY = propertyAnimation.value.y; });
propertyResetCallbacks.push(() => { nativeView.setScaleY(originalValue); });
}
break;
diff --git a/ui/animation/animation.ios.ts b/ui/animation/animation.ios.ts
index 36c186f42..b2f9a1b0f 100644
--- a/ui/animation/animation.ios.ts
+++ b/ui/animation/animation.ios.ts
@@ -102,6 +102,28 @@ export class Animation extends common.Animation implements definition.Animation
}
else if (that._finishedAnimations === that._mergedPropertyAnimations.length) {
trace.write(that._finishedAnimations + " animations finished.", trace.categories.Animation);
+
+ // Update our properties on the view.
+ var i = 0;
+ var len = that._propertyAnimations.length;
+ var a: common.PropertyAnimation;
+ for (; i < len; i++) {
+ a = that._propertyAnimations[i];
+ switch (a.property) {
+ case common.Properties.translate:
+ a.target.translateX = a.value.x;
+ a.target.translateY = a.value.y;
+ break;
+ case common.Properties.rotate:
+ a.target.rotate = a.value;
+ break;
+ case common.Properties.scale:
+ a.target.scaleX = a.value.x;
+ a.target.scaleY = a.value.y;
+ break;
+ }
+ }
+
that._resolveAnimationFinishedPromise();
}
}
diff --git a/ui/core/view-common.ts b/ui/core/view-common.ts
index b757d3611..6b21c0d14 100644
--- a/ui/core/view-common.ts
+++ b/ui/core/view-common.ts
@@ -98,6 +98,36 @@ var cssClassProperty = new dependencyObservable.Property(
new proxy.PropertyMetadata(undefined, dependencyObservable.PropertyMetadataSettings.AffectsStyle, onCssClassPropertyChanged)
);
+var translateXProperty = new dependencyObservable.Property(
+ "translateX",
+ "View",
+ new proxy.PropertyMetadata(0)
+ );
+
+var translateYProperty = new dependencyObservable.Property(
+ "translateY",
+ "View",
+ new proxy.PropertyMetadata(0)
+ );
+
+var scaleXProperty = new dependencyObservable.Property(
+ "scaleX",
+ "View",
+ new proxy.PropertyMetadata(1)
+ );
+
+var scaleYProperty = new dependencyObservable.Property(
+ "scaleY",
+ "View",
+ new proxy.PropertyMetadata(1)
+ );
+
+var rotateProperty = new dependencyObservable.Property(
+ "rotate",
+ "View",
+ new proxy.PropertyMetadata(0)
+ );
+
var isEnabledProperty = new dependencyObservable.Property(
"isEnabled",
"View",
@@ -116,6 +146,11 @@ export class View extends proxy.ProxyObject implements definition.View {
public static idProperty = idProperty;
public static cssClassProperty = cssClassProperty;
+ public static translateXProperty = translateXProperty;
+ public static translateYProperty = translateYProperty;
+ public static scaleXProperty = scaleXProperty;
+ public static scaleYProperty = scaleYProperty;
+ public static rotateProperty = rotateProperty;
public static isEnabledProperty = isEnabledProperty;
public static isUserInteractionEnabledProperty = isUserInteractionEnabledProperty;
@@ -374,6 +409,41 @@ export class View extends proxy.ProxyObject implements definition.View {
//END Style property shortcuts
+ get translateX(): number {
+ return this._getValue(View.translateXProperty);
+ }
+ set translateX(value: number) {
+ this._setValue(View.translateXProperty, value);
+ }
+
+ get translateY(): number {
+ return this._getValue(View.translateYProperty);
+ }
+ set translateY(value: number) {
+ this._setValue(View.translateYProperty, value);
+ }
+
+ get scaleX(): number {
+ return this._getValue(View.scaleXProperty);
+ }
+ set scaleX(value: number) {
+ this._setValue(View.scaleXProperty, value);
+ }
+
+ get scaleY(): number {
+ return this._getValue(View.scaleYProperty);
+ }
+ set scaleY(value: number) {
+ this._setValue(View.scaleYProperty, value);
+ }
+
+ get rotate(): number {
+ return this._getValue(View.rotateProperty);
+ }
+ set rotate(value: number) {
+ this._setValue(View.rotateProperty, value);
+ }
+
get isEnabled(): boolean {
return this._getValue(View.isEnabledProperty);
}
diff --git a/ui/core/view.android.ts b/ui/core/view.android.ts
index bcf46ce39..7c38301a4 100644
--- a/ui/core/view.android.ts
+++ b/ui/core/view.android.ts
@@ -20,6 +20,36 @@ function onIdPropertyChanged(data: dependencyObservable.PropertyChangeData) {
}
(viewCommon.View.idProperty.metadata).onSetNativeValue = onIdPropertyChanged;
+function onTranslateXPropertyChanged(data: dependencyObservable.PropertyChangeData) {
+ var view = data.object;
+ view._nativeView.setTranslationX(data.newValue * utils.layout.getDisplayDensity());
+}
+(viewCommon.View.translateXProperty.metadata).onSetNativeValue = onTranslateXPropertyChanged;
+
+function onTranslateYPropertyChanged(data: dependencyObservable.PropertyChangeData) {
+ var view = data.object;
+ view._nativeView.setTranslationY(data.newValue * utils.layout.getDisplayDensity());
+}
+(viewCommon.View.translateYProperty.metadata).onSetNativeValue = onTranslateYPropertyChanged;
+
+function onScaleXPropertyChanged(data: dependencyObservable.PropertyChangeData) {
+ var view = data.object;
+ view._nativeView.setScaleX(data.newValue);
+}
+(viewCommon.View.scaleXProperty.metadata).onSetNativeValue = onScaleXPropertyChanged;
+
+function onScaleYPropertyChanged(data: dependencyObservable.PropertyChangeData) {
+ var view = data.object;
+ view._nativeView.setScaleY(data.newValue);
+}
+(viewCommon.View.scaleYProperty.metadata).onSetNativeValue = onScaleYPropertyChanged;
+
+function onRotatePropertyChanged(data: dependencyObservable.PropertyChangeData) {
+ var view = data.object;
+ view._nativeView.setRotation(data.newValue);
+}
+(viewCommon.View.rotateProperty.metadata).onSetNativeValue = onRotatePropertyChanged;
+
function onIsEnabledPropertyChanged(data: dependencyObservable.PropertyChangeData) {
var view = data.object;
view._nativeView.setEnabled(data.newValue);
diff --git a/ui/core/view.d.ts b/ui/core/view.d.ts
index a085a25df..6981bbbbf 100644
--- a/ui/core/view.d.ts
+++ b/ui/core/view.d.ts
@@ -227,6 +227,31 @@ declare module "ui/core/view" {
//----------Style property shortcuts----------
+ /**
+ * Gets or sets the translateX affine transform of the view.
+ */
+ translateX: number;
+
+ /**
+ * Gets or sets the translateY affine transform of the view.
+ */
+ translateY: number;
+
+ /**
+ * Gets or sets the scaleX affine transform of the view.
+ */
+ scaleX: number;
+
+ /**
+ * Gets or sets the scaleY affine transform of the view.
+ */
+ scaleY: number;
+
+ /**
+ * Gets or sets the rotate affine transform of the view.
+ */
+ rotate: number;
+
/**
* Gets or sets a value indicating whether the the view is enabled. This affects the appearance of the view.
*/
diff --git a/ui/core/view.ios.ts b/ui/core/view.ios.ts
index c020387ce..0dad170ad 100644
--- a/ui/core/view.ios.ts
+++ b/ui/core/view.ios.ts
@@ -18,6 +18,66 @@ function onIdPropertyChanged(data: dependencyObservable.PropertyChangeData) {
}
(viewCommon.View.idProperty.metadata).onSetNativeValue = onIdPropertyChanged;
+function onTranslateXPropertyChanged(data: dependencyObservable.PropertyChangeData) {
+ var view = data.object;
+ var newTransform = CGAffineTransformIdentity;
+ newTransform = CGAffineTransformTranslate(newTransform, data.newValue, view.translateY);
+ newTransform = CGAffineTransformRotate(newTransform, view.rotate * Math.PI / 180);
+ newTransform = CGAffineTransformScale(newTransform, view.scaleX, view.scaleY);
+ if (!CGAffineTransformEqualToTransform(view._nativeView.transform, newTransform)) {
+ view._nativeView.transform = newTransform;
+ }
+}
+(viewCommon.View.translateXProperty.metadata).onSetNativeValue = onTranslateXPropertyChanged;
+
+function onTranslateYPropertyChanged(data: dependencyObservable.PropertyChangeData) {
+ var view = data.object;
+ var newTransform = CGAffineTransformIdentity;
+ newTransform = CGAffineTransformTranslate(newTransform, view.translateX, data.newValue);
+ newTransform = CGAffineTransformRotate(newTransform, view.rotate * Math.PI / 180);
+ newTransform = CGAffineTransformScale(newTransform, view.scaleX, view.scaleY);
+ if (!CGAffineTransformEqualToTransform(view._nativeView.transform, newTransform)) {
+ view._nativeView.transform = newTransform;
+ }
+}
+(viewCommon.View.translateYProperty.metadata).onSetNativeValue = onTranslateYPropertyChanged;
+
+function onScaleXPropertyChanged(data: dependencyObservable.PropertyChangeData) {
+ var view = data.object;
+ var newTransform = CGAffineTransformIdentity;
+ newTransform = CGAffineTransformTranslate(newTransform, view.translateX, view.translateY);
+ newTransform = CGAffineTransformRotate(newTransform, view.rotate * Math.PI / 180);
+ newTransform = CGAffineTransformScale(newTransform, data.newValue, view.scaleY);
+ if (!CGAffineTransformEqualToTransform(view._nativeView.transform, newTransform)) {
+ view._nativeView.transform = newTransform;
+ }
+}
+(viewCommon.View.scaleXProperty.metadata).onSetNativeValue = onScaleXPropertyChanged;
+
+function onScaleYPropertyChanged(data: dependencyObservable.PropertyChangeData) {
+ var view = data.object;
+ var newTransform = CGAffineTransformIdentity;
+ newTransform = CGAffineTransformTranslate(newTransform, view.translateX, view.translateY);
+ newTransform = CGAffineTransformRotate(newTransform, view.rotate * Math.PI / 180);
+ newTransform = CGAffineTransformScale(newTransform, view.scaleX, data.newValue);
+ if (!CGAffineTransformEqualToTransform(view._nativeView.transform, newTransform)) {
+ view._nativeView.transform = newTransform;
+ }
+}
+(viewCommon.View.scaleYProperty.metadata).onSetNativeValue = onScaleYPropertyChanged;
+
+function onRotatePropertyChanged(data: dependencyObservable.PropertyChangeData) {
+ var view = data.object;
+ var newTransform = CGAffineTransformIdentity;
+ newTransform = CGAffineTransformTranslate(newTransform, view.translateX, view.translateY);
+ newTransform = CGAffineTransformRotate(newTransform, data.newValue * Math.PI / 180);
+ newTransform = CGAffineTransformScale(newTransform, view.scaleX, view.scaleY);
+ if (!CGAffineTransformEqualToTransform(view._nativeView.transform, newTransform)) {
+ view._nativeView.transform = newTransform;
+ }
+}
+(viewCommon.View.rotateProperty.metadata).onSetNativeValue = onRotatePropertyChanged;
+
function onIsEnabledPropertyChanged(data: dependencyObservable.PropertyChangeData) {
var view = data.object;
if (!view._nativeView) {