mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-16 20:11:24 +08:00
implemented CSS animations
fixing animation tests
This commit is contained in:
@ -87,7 +87,7 @@ allTests["SEARCH-BAR"] = require('./ui/search-bar/search-bar-tests');
|
||||
allTests["CONNECTIVITY"] = require("./connectivity-tests");
|
||||
allTests["SEGMENTED-BAR"] = require("./ui/segmented-bar/segmented-bar-tests");
|
||||
allTests["ANIMATION"] = require("./ui/animation/animation-tests");
|
||||
|
||||
allTests["CSS-ANIMATION"] = require("./ui/animation/css-animation-tests");
|
||||
if (!isRunningOnEmulator()) {
|
||||
allTests["LOCATION"] = require("./location-tests");
|
||||
}
|
||||
|
302
apps/tests/ui/animation/css-animation-tests.ts
Normal file
302
apps/tests/ui/animation/css-animation-tests.ts
Normal file
@ -0,0 +1,302 @@
|
||||
import TKUnit = require("../../TKUnit");
|
||||
import page = require("ui/page");
|
||||
import styleScope = require("ui/styling/style-scope");
|
||||
import keyframeAnimation = require("ui/animation/keyframe-animation");
|
||||
import enums = require("ui/enums");
|
||||
import helper = require("../../ui/helper");
|
||||
import stackModule = require("ui/layouts/stack-layout");
|
||||
import labelModule = require("ui/label");
|
||||
import color = require("color");
|
||||
import selectorModule = require("ui/styling/css-selector");
|
||||
|
||||
function createAnimationFromCSS(css: string, name: string): keyframeAnimation.KeyframeAnimationInfo {
|
||||
let scope = new styleScope.StyleScope();
|
||||
scope.css = css;
|
||||
scope.ensureSelectors();
|
||||
let selector = findSelectorInScope(scope, name);
|
||||
if (selector !== undefined) {
|
||||
let animation = selector.animations[0];
|
||||
return animation;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function findSelectorInScope(scope: styleScope.StyleScope, name: string): selectorModule.CssSelector {
|
||||
let selector = undefined;
|
||||
for (let sel of (<any>scope)._mergedCssSelectors) {
|
||||
if (sel.expression === name) {
|
||||
selector = sel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return selector;
|
||||
}
|
||||
|
||||
exports.test_ReadAnimationProperties = function () {
|
||||
let css = ".test { " +
|
||||
"animation-name: first; " +
|
||||
"animation-duration: 4s; " +
|
||||
"animation-timing-function: ease-in; " +
|
||||
"animation-delay: 1.5; " +
|
||||
"animation-iteration-count: 10; " +
|
||||
"animation-direction: reverse; " +
|
||||
"animation-fill-mode: forwards; " +
|
||||
" }";
|
||||
let animation = createAnimationFromCSS(css, "test");
|
||||
TKUnit.assertEqual(animation.name, "first");
|
||||
TKUnit.assertEqual(animation.duration, 4000);
|
||||
TKUnit.assertEqual(animation.curve, enums.AnimationCurve.easeIn);
|
||||
TKUnit.assertEqual(animation.delay, 1500);
|
||||
TKUnit.assertEqual(animation.iterations, 10);
|
||||
TKUnit.assertTrue(animation.isForwards);
|
||||
TKUnit.assertTrue(animation.isReverse);
|
||||
};
|
||||
exports.test_ReadTheAnimationProperty = function () {
|
||||
let animation = createAnimationFromCSS(".test { animation: second 0.2s ease-out 1 2 }", "test");
|
||||
TKUnit.assertEqual(animation.name, "second");
|
||||
TKUnit.assertEqual(animation.duration, 200);
|
||||
TKUnit.assertEqual(animation.curve, enums.AnimationCurve.easeOut);
|
||||
TKUnit.assertEqual(animation.delay, 1000);
|
||||
TKUnit.assertEqual(animation.iterations, 2);
|
||||
};
|
||||
exports.test_ReadAnimationCurve = function () {
|
||||
let animation = createAnimationFromCSS(".test { animation-timing-function: ease-in; }", "test");
|
||||
TKUnit.assertEqual(animation.curve, enums.AnimationCurve.easeIn);
|
||||
animation = createAnimationFromCSS(".test { animation-timing-function: ease-out; }", "test");
|
||||
TKUnit.assertEqual(animation.curve, enums.AnimationCurve.easeOut);
|
||||
animation = createAnimationFromCSS(".test { animation-timing-function: linear; }", "test");
|
||||
TKUnit.assertEqual(animation.curve, enums.AnimationCurve.linear);
|
||||
animation = createAnimationFromCSS(".test { animation-timing-function: ease-in-out; }", "test");
|
||||
TKUnit.assertEqual(animation.curve, enums.AnimationCurve.easeInOut);
|
||||
animation = createAnimationFromCSS(".test { animation-timing-function: spring; }", "test");
|
||||
TKUnit.assertEqual(animation.curve, enums.AnimationCurve.spring);
|
||||
animation = createAnimationFromCSS(".test { animation-timing-function: cubic-bezier(0.1, 1.0, 0.5, 0.5); }", "test");
|
||||
let curve = animation.curve;
|
||||
TKUnit.assert(curve.x1 === 0.1 && curve.y1 === 1.0 && curve.x2 === 0.5 && curve.y2 === 0.5);
|
||||
};
|
||||
exports.test_ReadIterations = function () {
|
||||
let animation = createAnimationFromCSS(".test { animation-iteration-count: 5; }", "test");
|
||||
TKUnit.assertEqual(animation.iterations, 5);
|
||||
animation = createAnimationFromCSS(".test { animation-iteration-count: infinite; }", "test");
|
||||
TKUnit.assertEqual(animation.iterations, Number.MAX_VALUE);
|
||||
};
|
||||
exports.test_ReadFillMode = function () {
|
||||
let animation = createAnimationFromCSS(".test { animation-iteration-count: 5; }", "test");
|
||||
TKUnit.assertFalse(animation.isForwards);
|
||||
animation = createAnimationFromCSS(".test { animation-fill-mode: forwards; }", "test");
|
||||
TKUnit.assertTrue(animation.isForwards);
|
||||
animation = createAnimationFromCSS(".test { animation-fill-mode: backwards; }", "test");
|
||||
TKUnit.assertFalse(animation.isForwards);
|
||||
};
|
||||
exports.test_ReadDirection = function () {
|
||||
let animation = createAnimationFromCSS(".test { animation-iteration-count: 5; }", "test");
|
||||
TKUnit.assertFalse(animation.isReverse);
|
||||
animation = createAnimationFromCSS(".test { animation-direction: reverse; }", "test");
|
||||
TKUnit.assertTrue(animation.isReverse);
|
||||
animation = createAnimationFromCSS(".test { animation-direction: normal; }", "test");
|
||||
TKUnit.assertFalse(animation.isReverse);
|
||||
};
|
||||
exports.test_ReadKeyframe = function () {
|
||||
let scope = new styleScope.StyleScope();
|
||||
scope.css = ".test { animation-name: test; } @keyframes test { from { background-color: red; } to { background-color: blue; } }";
|
||||
scope.ensureSelectors();
|
||||
let selector = findSelectorInScope(scope, "test");
|
||||
TKUnit.assert(selector !== undefined, "CSS selector was not created!");
|
||||
let animation = selector.animations[0];
|
||||
TKUnit.assertEqual(animation.name, "test", "Wrong animation name!");
|
||||
TKUnit.assertEqual(animation.keyframes.length, 2, "Keyframes not parsed correctly!");
|
||||
TKUnit.assertEqual(animation.keyframes[0].duration, 0, "First keyframe duration should be 0");
|
||||
TKUnit.assertEqual(animation.keyframes[1].duration, 1, "Second keyframe duration should be 1");
|
||||
TKUnit.assertEqual(animation.keyframes[0].declarations.length, 1, "Keyframe declarations are not correct");
|
||||
TKUnit.assertEqual(animation.keyframes[0].declarations[0].property, "backgroundColor", "Keyframe declarations are not correct");
|
||||
};
|
||||
exports.test_ReadScale = function () {
|
||||
let animation = createAnimationFromCSS(".test { animation-name: test; } @keyframes test { to { transform: scaleX(5),scaleY(10); } }", "test");
|
||||
let scale = animation.keyframes[0].declarations[0].value;
|
||||
TKUnit.assertEqual(animation.keyframes[0].declarations[0].property, "scale");
|
||||
TKUnit.assert(scale.x === 5 && scale.y === 10);
|
||||
animation = createAnimationFromCSS(".test { animation-name: test; } @keyframes test { to { transform: scale(-5, 12.3pt); } }", "test");
|
||||
scale = animation.keyframes[0].declarations[0].value;
|
||||
TKUnit.assertEqual(animation.keyframes[0].declarations[0].property, "scale");
|
||||
TKUnit.assert(scale.x === -5 && scale.y === 12.3);
|
||||
animation = createAnimationFromCSS(".test { animation-name: test; } @keyframes test { to { transform: scaleY(10); } }", "test");
|
||||
scale = animation.keyframes[0].declarations[0].value;
|
||||
TKUnit.assertEqual(animation.keyframes[0].declarations[0].property, "scale");
|
||||
TKUnit.assert(scale.x === 1 && scale.y === 10);
|
||||
animation = createAnimationFromCSS(".test { animation-name: test; } @keyframes test { to { transform: scale3d(10, 20, 30); } }", "test");
|
||||
scale = animation.keyframes[0].declarations[0].value;
|
||||
TKUnit.assertEqual(animation.keyframes[0].declarations[0].property, "scale");
|
||||
TKUnit.assert(scale.x === 10 && scale.y === 20);
|
||||
};
|
||||
exports.test_ReadTranslate = function () {
|
||||
let animation = createAnimationFromCSS(".test { animation-name: test; } @keyframes test { to { transform: translateX(5),translateY(10); } }", "test");
|
||||
let translate = animation.keyframes[0].declarations[0].value;
|
||||
TKUnit.assertEqual(animation.keyframes[0].declarations[0].property, "translate");
|
||||
TKUnit.assert(translate.x === 5 && translate.y === 10);
|
||||
animation = createAnimationFromCSS(".test { animation-name: test; } @keyframes test { to { transform: translate(-5, 12.3pt); } }", "test");
|
||||
translate = animation.keyframes[0].declarations[0].value;
|
||||
TKUnit.assertEqual(animation.keyframes[0].declarations[0].property, "translate");
|
||||
TKUnit.assert(translate.x === -5 && translate.y === 12.3);
|
||||
animation = createAnimationFromCSS(".test { animation-name: test; } @keyframes test { to { transform: translateX(10); } }", "test");
|
||||
translate = animation.keyframes[0].declarations[0].value;
|
||||
TKUnit.assertEqual(animation.keyframes[0].declarations[0].property, "translate");
|
||||
TKUnit.assert(translate.x === 10 && translate.y === 0);
|
||||
animation = createAnimationFromCSS(".test { animation-name: test; } @keyframes test { to { transform: translate3d(10, 20, 30); } }", "test");
|
||||
translate = animation.keyframes[0].declarations[0].value;
|
||||
TKUnit.assertEqual(animation.keyframes[0].declarations[0].property, "translate");
|
||||
TKUnit.assert(translate.x === 10 && translate.y === 20);
|
||||
};
|
||||
exports.test_ReadRotate = function () {
|
||||
let animation = createAnimationFromCSS(".test { animation-name: test; } @keyframes test { to { transform: rotate(5); } }", "test");
|
||||
TKUnit.assertEqual(animation.keyframes[0].declarations[0].property, "rotate");
|
||||
TKUnit.assertEqual(animation.keyframes[0].declarations[0].value, 5);
|
||||
animation = createAnimationFromCSS(".test { animation-name: test; } @keyframes test { to { transform: rotate(45deg); } }", "test");
|
||||
TKUnit.assertEqual(animation.keyframes[0].declarations[0].property, "rotate");
|
||||
TKUnit.assertEqual(animation.keyframes[0].declarations[0].value, 45);
|
||||
animation = createAnimationFromCSS(".test { animation-name: test; } @keyframes test { to { transform: rotate(0.7853981634rad); } }", "test");
|
||||
TKUnit.assertEqual(animation.keyframes[0].declarations[0].property, "rotate");
|
||||
TKUnit.assertTrue(animation.keyframes[0].declarations[0].value - 45 < 0.1);
|
||||
};
|
||||
exports.test_ReadTransform = function () {
|
||||
let css = ".test { animation-name: test; } @keyframes test { to { transform: rotate(10),scaleX(5),translate(2,4); } }";
|
||||
let animation = createAnimationFromCSS(css, "test");
|
||||
let rotate = animation.keyframes[0].declarations[0].value;
|
||||
let scale = animation.keyframes[0].declarations[1].value;
|
||||
let translate = animation.keyframes[0].declarations[2].value;
|
||||
TKUnit.assertEqual(rotate, 10);
|
||||
TKUnit.assert(scale.x === 5 && scale.y === 1);
|
||||
TKUnit.assert(translate.x === 2 && translate.y === 4);
|
||||
animation = createAnimationFromCSS(".test { animation-name: test; } @keyframes test { to { transform: none; } }", "test");
|
||||
rotate = animation.keyframes[0].declarations[0].value;
|
||||
scale = animation.keyframes[0].declarations[1].value;
|
||||
translate = animation.keyframes[0].declarations[2].value;
|
||||
TKUnit.assertEqual(rotate, 0);
|
||||
TKUnit.assert(scale.x === 1 && scale.y === 1);
|
||||
TKUnit.assert(translate.x === 0 && translate.y === 0);
|
||||
};
|
||||
exports.test_ReadAnimationWithUnsortedKeyframes = function () {
|
||||
let css = ".test { animation-name: test; } " +
|
||||
"@keyframes test { " +
|
||||
"from { opacity: 0; } " +
|
||||
"20%, 60% { opacity: 0.5; } " +
|
||||
"40%, 80% { opacity: 0.3; } " +
|
||||
"to { opacity: 1; } " +
|
||||
"}";
|
||||
let animation = createAnimationFromCSS(css, "test");
|
||||
TKUnit.assertEqual(animation.keyframes.length, 6);
|
||||
TKUnit.assertEqual(animation.keyframes[0].declarations[0].value, 0);
|
||||
TKUnit.assertEqual(animation.keyframes[1].declarations[0].value, 0.5);
|
||||
TKUnit.assertEqual(animation.keyframes[2].declarations[0].value, 0.3);
|
||||
TKUnit.assertEqual(animation.keyframes[3].declarations[0].value, 0.5);
|
||||
TKUnit.assertEqual(animation.keyframes[4].declarations[0].value, 0.3);
|
||||
TKUnit.assertEqual(animation.keyframes[5].declarations[0].value, 1);
|
||||
TKUnit.assertEqual(animation.keyframes[0].duration, 0);
|
||||
TKUnit.assertEqual(animation.keyframes[1].duration, 0.2);
|
||||
TKUnit.assertEqual(animation.keyframes[2].duration, 0.4);
|
||||
TKUnit.assertEqual(animation.keyframes[3].duration, 0.6);
|
||||
TKUnit.assertEqual(animation.keyframes[4].duration, 0.8);
|
||||
TKUnit.assertEqual(animation.keyframes[5].duration, 1);
|
||||
};
|
||||
exports.test_ReadAnimationsWithCSSImport = function () {
|
||||
let css = "@import '~/ui/animation/test.css'; .test { animation-name: test; }";
|
||||
let animation = createAnimationFromCSS(css, "test");
|
||||
TKUnit.assertEqual(animation.keyframes.length, 3);
|
||||
TKUnit.assertEqual(animation.keyframes[1].declarations[0].property, "backgroundColor");
|
||||
};
|
||||
exports.test_LoadTwoAnimationsWithTheSameName = function () {
|
||||
let scope = new styleScope.StyleScope();
|
||||
scope.css = "@keyframes a1 { from { opacity: 0; } to { opacity: 1; } } @keyframes a1 { from { opacity: 0; } to { opacity: 0.5; } } .a { animation-name: a1; }";
|
||||
scope.ensureSelectors();
|
||||
let selector = findSelectorInScope(scope, "a");
|
||||
let animation = selector.animations[0];
|
||||
TKUnit.assertEqual(animation.keyframes.length, 2);
|
||||
TKUnit.assertEqual(animation.keyframes[1].declarations[0].value, 0.5);
|
||||
scope = new styleScope.StyleScope();
|
||||
scope.css = "@keyframes k { from { opacity: 0; } to { opacity: 1; } } .a { animation-name: k; animation-duration: 2; } .a { animation-name: k; animation-duration: 3; }";
|
||||
scope.ensureSelectors();
|
||||
selector = findSelectorInScope(scope, "a");
|
||||
TKUnit.assertEqual(selector.animations[0].keyframes.length, 2);
|
||||
TKUnit.assertEqual(selector.animations[0].keyframes.length, 2);
|
||||
};
|
||||
exports.test_LoadAnimationProgrammatically = function () {
|
||||
let stack = new stackModule.StackLayout();
|
||||
helper.buildUIAndRunTest(stack, function (views) {
|
||||
let page = views[1];
|
||||
page.css = "@keyframes a { from { opacity: 1; } to { opacity: 0; } }";
|
||||
let animation = page.getKeyframeAnimationWithName("a");
|
||||
TKUnit.assertEqual(animation.keyframes.length, 2);
|
||||
TKUnit.assertEqual(animation.keyframes[1].declarations[0].property, "opacity");
|
||||
TKUnit.assertEqual(animation.keyframes[1].declarations[0].value, 0);
|
||||
});
|
||||
};
|
||||
exports.test_ExecuteCSSAnimation = function () {
|
||||
let mainPage;
|
||||
let label;
|
||||
let pageFactory = function () {
|
||||
label = new labelModule.Label();
|
||||
label.text = "label";
|
||||
let stackLayout = new stackModule.StackLayout();
|
||||
stackLayout.addChild(label);
|
||||
mainPage = new page.Page();
|
||||
mainPage.css = "@keyframes k { from { background-color: red; } to { background-color: green; } } .l { animation-name: k; animation-duration: 0.5s; animation-fill-mode: forwards; }";
|
||||
mainPage.content = stackLayout;
|
||||
return mainPage;
|
||||
};
|
||||
helper.navigate(pageFactory);
|
||||
TKUnit.waitUntilReady(function () { return label.isLoaded; });
|
||||
label.className = "l";
|
||||
TKUnit.waitUntilReady(function () { return new color.Color("green").equals(label.backgroundColor); }, 1);
|
||||
TKUnit.assert(new color.Color("green").equals(label.backgroundColor));
|
||||
helper.goBack();
|
||||
};
|
||||
exports.test_ExecuteFillMode = function () {
|
||||
let mainPage;
|
||||
let label;
|
||||
let pageFactory = function () {
|
||||
label = new labelModule.Label();
|
||||
label.text = "label";
|
||||
let stackLayout = new stackModule.StackLayout();
|
||||
stackLayout.addChild(label);
|
||||
mainPage = new page.Page();
|
||||
mainPage.css = "@keyframes k { from { background-color: red; } to { background-color: green; } } " +
|
||||
".l { animation-name: k; animation-duration: 0.5s; animation-fill-mode: none; } " +
|
||||
".l2 { animation-name: k; animation-duration: 0.5s; animation-fill-mode: forwards; }";
|
||||
mainPage.content = stackLayout;
|
||||
return mainPage;
|
||||
};
|
||||
helper.navigate(pageFactory);
|
||||
TKUnit.waitUntilReady(function () { return label.isLoaded; });
|
||||
TKUnit.assertEqual(label.backgroundColor, undefined);
|
||||
label.className = "l";
|
||||
TKUnit.wait(2);
|
||||
TKUnit.assertEqual(label.backgroundColor, undefined);
|
||||
label.className = "l2";
|
||||
TKUnit.waitUntilReady(function() { return new color.Color("green").equals(label.backgroundColor); }, 1);
|
||||
TKUnit.assert(new color.Color("green").equals(label.backgroundColor));
|
||||
helper.goBack();
|
||||
helper.goBack();
|
||||
};
|
||||
exports.test_ReadTwoAnimations = function () {
|
||||
let scope = new styleScope.StyleScope();
|
||||
scope.css = ".test { animation: one 0.2s ease-out 1 2, two 2s ease-in; }";
|
||||
scope.ensureSelectors();
|
||||
let selector = findSelectorInScope(scope, "test");
|
||||
TKUnit.assertEqual(selector.animations.length, 2);
|
||||
TKUnit.assertEqual(selector.animations[0].curve, enums.AnimationCurve.easeOut);
|
||||
TKUnit.assertEqual(selector.animations[1].curve, enums.AnimationCurve.easeIn);
|
||||
TKUnit.assertEqual(selector.animations[1].name, "two");
|
||||
TKUnit.assertEqual(selector.animations[1].duration, 2000);
|
||||
};
|
||||
exports.test_AnimationCurveInKeyframes = function () {
|
||||
let scope = new styleScope.StyleScope();
|
||||
scope.css = "@keyframes an { from { animation-timing-function: linear; background-color: red; } 50% { background-color: green; } to { background-color: black; } } .test { animation-name: an; animation-timing-function: ease-in; }";
|
||||
scope.ensureSelectors();
|
||||
let selector = findSelectorInScope(scope, "test");
|
||||
let animation = selector.animations[0];
|
||||
TKUnit.assertEqual(animation.keyframes[0].curve, enums.AnimationCurve.linear);
|
||||
TKUnit.assertEqual(animation.keyframes[1].curve, undefined);
|
||||
TKUnit.assertEqual(animation.keyframes[1].curve, undefined);
|
||||
let realAnimation = keyframeAnimation.KeyframeAnimation.keyframeAnimationFromInfo(animation, 2);
|
||||
TKUnit.assertEqual(realAnimation.animations[1].curve, enums.AnimationCurve.linear);
|
||||
TKUnit.assertEqual(realAnimation.animations[2].curve, enums.AnimationCurve.easeIn);
|
||||
};
|
5
apps/tests/ui/animation/test.css
Normal file
5
apps/tests/ui/animation/test.css
Normal file
@ -0,0 +1,5 @@
|
||||
@keyframes test {
|
||||
from { background-color: red; }
|
||||
50% { background-color: yellow; }
|
||||
to { background-color: green; }
|
||||
}
|
@ -179,7 +179,6 @@ export var test_isAddedToNativeVisualTree_IsUpdated = function () {
|
||||
|
||||
views[1]._addView(newButton);
|
||||
TKUnit.assert(newButton._isAddedToNativeVisualTree);
|
||||
|
||||
views[1]._removeView(newButton);
|
||||
TKUnit.assert(!newButton._isAddedToNativeVisualTree);
|
||||
}
|
||||
|
@ -233,6 +233,7 @@
|
||||
"apps/tests/ui/action-bar/action-bar-tests.ios.ts",
|
||||
"apps/tests/ui/activity-indicator/activity-indicator-tests.ts",
|
||||
"apps/tests/ui/animation/animation-tests.ts",
|
||||
"apps/tests/ui/animation/css-animation-tests.ts",
|
||||
"apps/tests/ui/bindable-tests.ts",
|
||||
"apps/tests/ui/binding-expressions-tests.ts",
|
||||
"apps/tests/ui/bindingContext_testPage.ts",
|
||||
@ -498,6 +499,8 @@
|
||||
"ui/animation/animation.android.ts",
|
||||
"ui/animation/animation.d.ts",
|
||||
"ui/animation/animation.ios.ts",
|
||||
"ui/animation/keyframe-animation.d.ts",
|
||||
"ui/animation/keyframe-animation.ts",
|
||||
"ui/border/border.d.ts",
|
||||
"ui/border/border.ts",
|
||||
"ui/builder/binding-builder.d.ts",
|
||||
|
@ -5,18 +5,19 @@ import color = require("color");
|
||||
import trace = require("trace");
|
||||
import types = require("utils/types");
|
||||
import enums = require("ui/enums");
|
||||
import styleModule = require("ui/styling/style");
|
||||
|
||||
global.moduleMerge(common, exports);
|
||||
|
||||
var argbEvaluator: android.animation.ArgbEvaluator;
|
||||
let argbEvaluator: android.animation.ArgbEvaluator;
|
||||
function ensureArgbEvaluator() {
|
||||
if (!argbEvaluator) {
|
||||
argbEvaluator = new android.animation.ArgbEvaluator();
|
||||
}
|
||||
}
|
||||
|
||||
var keyPrefix = "ui.animation.";
|
||||
var propertyKeys = {};
|
||||
let keyPrefix = "ui.animation.";
|
||||
let propertyKeys = {};
|
||||
propertyKeys[common.Properties.backgroundColor] = Symbol(keyPrefix + common.Properties.backgroundColor);
|
||||
propertyKeys[common.Properties.opacity] = Symbol(keyPrefix + common.Properties.opacity);
|
||||
propertyKeys[common.Properties.rotate] = Symbol(keyPrefix + common.Properties.rotate);
|
||||
@ -30,12 +31,13 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
private _animators: Array<android.animation.Animator>;
|
||||
private _propertyUpdateCallbacks: Array<Function>;
|
||||
private _propertyResetCallbacks: Array<Function>;
|
||||
private _valueSource: number;
|
||||
|
||||
public play(): definition.AnimationPromise {
|
||||
var animationFinishedPromise = super.play();
|
||||
|
||||
var i: number;
|
||||
var length: number;
|
||||
let i: number;
|
||||
let length: number;
|
||||
|
||||
this._animators = new Array<android.animation.Animator>();
|
||||
this._propertyUpdateCallbacks = new Array<Function>();
|
||||
@ -78,7 +80,11 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
constructor(animationDefinitions: Array<definition.AnimationDefinition>, playSequentially?: boolean) {
|
||||
super(animationDefinitions, playSequentially);
|
||||
|
||||
var that = this;
|
||||
if (animationDefinitions.length > 0 && (<any>animationDefinitions[0]).valueSource !== undefined) {
|
||||
this._valueSource = (<any>animationDefinitions[0]).valueSource;
|
||||
}
|
||||
|
||||
let that = this;
|
||||
this._animatorListener = new android.animation.Animator.AnimatorListener({
|
||||
onAnimationStart: function (animator: android.animation.Animator): void {
|
||||
trace.write("MainAnimatorListener.onAndroidAnimationStart(" + animator +")", trace.categories.Animation);
|
||||
@ -103,8 +109,8 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
return;
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
var length = this._propertyUpdateCallbacks.length;
|
||||
let i = 0;
|
||||
let length = this._propertyUpdateCallbacks.length;
|
||||
for (; i < length; i++) {
|
||||
this._propertyUpdateCallbacks[i]();
|
||||
}
|
||||
@ -112,8 +118,8 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
}
|
||||
|
||||
private _onAndroidAnimationCancel() {
|
||||
var i = 0;
|
||||
var length = this._propertyResetCallbacks.length;
|
||||
let i = 0;
|
||||
let length = this._propertyResetCallbacks.length;
|
||||
for (; i < length; i++) {
|
||||
this._propertyResetCallbacks[i]();
|
||||
}
|
||||
@ -135,18 +141,18 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
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.Animator>();
|
||||
var propertyUpdateCallbacks = new Array<Function>();
|
||||
var propertyResetCallbacks = new Array<Function>();
|
||||
var originalValue1;
|
||||
var originalValue2;
|
||||
var density = utils.layout.getDisplayDensity();
|
||||
var xyObjectAnimators: any;
|
||||
var animatorSet: android.animation.AnimatorSet;
|
||||
let nativeArray;
|
||||
let nativeView: android.view.View = (<android.view.View>propertyAnimation.target._nativeView);
|
||||
let animators = new Array<android.animation.Animator>();
|
||||
let propertyUpdateCallbacks = new Array<Function>();
|
||||
let propertyResetCallbacks = new Array<Function>();
|
||||
let originalValue1;
|
||||
let originalValue2;
|
||||
let density = utils.layout.getDisplayDensity();
|
||||
let xyObjectAnimators: any;
|
||||
let animatorSet: android.animation.AnimatorSet;
|
||||
|
||||
var key = propertyKeys[propertyAnimation.property];
|
||||
let key = propertyKeys[propertyAnimation.property];
|
||||
if (key) {
|
||||
propertyAnimation.target[key] = propertyAnimation;
|
||||
}
|
||||
@ -159,13 +165,22 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
}
|
||||
}
|
||||
|
||||
let valueSource = this._valueSource;
|
||||
|
||||
switch (propertyAnimation.property) {
|
||||
|
||||
case common.Properties.opacity:
|
||||
originalValue1 = nativeView.getAlpha();
|
||||
nativeArray = (<any>Array).create("float", 1);
|
||||
nativeArray[0] = propertyAnimation.value;
|
||||
propertyUpdateCallbacks.push(checkAnimation(() => { propertyAnimation.target.opacity = propertyAnimation.value }));
|
||||
if (this._valueSource !== undefined) {
|
||||
propertyUpdateCallbacks.push(checkAnimation(() => {
|
||||
propertyAnimation.target.style._setValue(styleModule.opacityProperty, propertyAnimation.value, valueSource);
|
||||
}));
|
||||
}
|
||||
else {
|
||||
propertyUpdateCallbacks.push(checkAnimation(() => { propertyAnimation.target.opacity = propertyAnimation.value; }));
|
||||
}
|
||||
propertyResetCallbacks.push(checkAnimation(() => { nativeView.setAlpha(originalValue1); }));
|
||||
animators.push(android.animation.ObjectAnimator.ofFloat(nativeView, "alpha", nativeArray));
|
||||
break;
|
||||
@ -176,15 +191,23 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
nativeArray = (<any>Array).create(java.lang.Object, 2);
|
||||
nativeArray[0] = propertyAnimation.target.backgroundColor ? java.lang.Integer.valueOf((<color.Color>propertyAnimation.target.backgroundColor).argb) : java.lang.Integer.valueOf(-1);
|
||||
nativeArray[1] = java.lang.Integer.valueOf((<color.Color>propertyAnimation.value).argb);
|
||||
var animator = android.animation.ValueAnimator.ofObject(argbEvaluator, nativeArray);
|
||||
let animator = android.animation.ValueAnimator.ofObject(argbEvaluator, nativeArray);
|
||||
animator.addUpdateListener(new android.animation.ValueAnimator.AnimatorUpdateListener({
|
||||
onAnimationUpdate(animator: android.animation.ValueAnimator) {
|
||||
var argb = (<java.lang.Integer>animator.getAnimatedValue()).intValue();
|
||||
propertyAnimation.target.backgroundColor = new color.Color(argb);
|
||||
let argb = (<java.lang.Integer>animator.getAnimatedValue()).intValue();
|
||||
propertyAnimation.target.style._setValue(styleModule.backgroundColorProperty, new color.Color(argb), valueSource);
|
||||
}
|
||||
}));
|
||||
|
||||
if (this._valueSource !== undefined) {
|
||||
let valueSource = this._valueSource;
|
||||
propertyUpdateCallbacks.push(checkAnimation(() => {
|
||||
propertyAnimation.target.style._setValue(styleModule.backgroundColorProperty, propertyAnimation.value, valueSource);
|
||||
}));
|
||||
}
|
||||
else {
|
||||
propertyUpdateCallbacks.push(checkAnimation(() => { propertyAnimation.target.backgroundColor = propertyAnimation.value; }));
|
||||
}
|
||||
propertyResetCallbacks.push(checkAnimation(() => { nativeView.setBackground(originalValue1); }));
|
||||
animators.push(animator);
|
||||
break;
|
||||
@ -205,10 +228,18 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
originalValue1 = nativeView.getTranslationX();
|
||||
originalValue2 = nativeView.getTranslationY();
|
||||
|
||||
if (this._valueSource !== undefined) {
|
||||
propertyUpdateCallbacks.push(checkAnimation(() => {
|
||||
propertyAnimation.target.style._setValue(styleModule.translateXProperty, propertyAnimation.value.x, valueSource);
|
||||
propertyAnimation.target.style._setValue(styleModule.translateYProperty, propertyAnimation.value.y, valueSource);
|
||||
}));
|
||||
}
|
||||
else {
|
||||
propertyUpdateCallbacks.push(checkAnimation(() => {
|
||||
propertyAnimation.target.translateX = propertyAnimation.value.x;
|
||||
propertyAnimation.target.translateY = propertyAnimation.value.y;
|
||||
}));
|
||||
}
|
||||
|
||||
propertyResetCallbacks.push(checkAnimation(() => {
|
||||
nativeView.setTranslationX(originalValue1);
|
||||
@ -237,10 +268,18 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
originalValue1 = nativeView.getScaleX();
|
||||
originalValue2 = nativeView.getScaleY();
|
||||
|
||||
if (this._valueSource !== undefined) {
|
||||
propertyUpdateCallbacks.push(checkAnimation(() => {
|
||||
propertyAnimation.target.style._setValue(styleModule.scaleXProperty, propertyAnimation.value.x, valueSource);
|
||||
propertyAnimation.target.style._setValue(styleModule.scaleYProperty, propertyAnimation.value.y, valueSource);
|
||||
}));
|
||||
}
|
||||
else {
|
||||
propertyUpdateCallbacks.push(checkAnimation(() => {
|
||||
propertyAnimation.target.scaleX = propertyAnimation.value.x;
|
||||
propertyAnimation.target.scaleY = propertyAnimation.value.y;
|
||||
}));
|
||||
}
|
||||
|
||||
propertyResetCallbacks.push(checkAnimation(() => {
|
||||
nativeView.setScaleY(originalValue1);
|
||||
@ -257,7 +296,14 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
originalValue1 = nativeView.getRotation();
|
||||
nativeArray = (<any>Array).create("float", 1);
|
||||
nativeArray[0] = propertyAnimation.value;
|
||||
if (this._valueSource !== undefined) {
|
||||
propertyUpdateCallbacks.push(checkAnimation(() => {
|
||||
propertyAnimation.target.style._setValue(styleModule.rotateProperty, propertyAnimation.value, valueSource);
|
||||
}));
|
||||
}
|
||||
else {
|
||||
propertyUpdateCallbacks.push(checkAnimation(() => { propertyAnimation.target.rotate = propertyAnimation.value; }));
|
||||
}
|
||||
propertyResetCallbacks.push(checkAnimation(() => { nativeView.setRotation(originalValue1); }));
|
||||
animators.push(android.animation.ObjectAnimator.ofFloat(nativeView, "rotation", nativeArray));
|
||||
break;
|
||||
@ -266,8 +312,8 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
throw new Error("Cannot animate " + propertyAnimation.property);
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
var length = animators.length;
|
||||
let i = 0;
|
||||
let length = animators.length;
|
||||
for (; i < length; i++) {
|
||||
|
||||
// Duration
|
||||
@ -302,11 +348,11 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
}
|
||||
}
|
||||
|
||||
var easeIn = new android.view.animation.AccelerateInterpolator(1);
|
||||
var easeOut = new android.view.animation.DecelerateInterpolator(1);
|
||||
var easeInOut = new android.view.animation.AccelerateDecelerateInterpolator();
|
||||
var linear = new android.view.animation.LinearInterpolator();
|
||||
var bounce = new android.view.animation.BounceInterpolator();
|
||||
let easeIn = new android.view.animation.AccelerateInterpolator(1);
|
||||
let easeOut = new android.view.animation.DecelerateInterpolator(1);
|
||||
let easeInOut = new android.view.animation.AccelerateDecelerateInterpolator();
|
||||
let linear = new android.view.animation.LinearInterpolator();
|
||||
let bounce = new android.view.animation.BounceInterpolator();
|
||||
export function _resolveAnimationCurve(curve: any): any {
|
||||
switch (curve) {
|
||||
case enums.AnimationCurve.easeIn:
|
||||
@ -324,11 +370,13 @@ export function _resolveAnimationCurve(curve: any): any {
|
||||
case enums.AnimationCurve.spring:
|
||||
trace.write("Animation curve resolved to android.view.animation.BounceInterpolator().", trace.categories.Animation);
|
||||
return bounce;
|
||||
case enums.AnimationCurve.ease:
|
||||
return (<any>android).support.v4.view.animation.PathInterpolatorCompat.create(0.25, 0.1, 0.25, 1.0);
|
||||
default:
|
||||
trace.write("Animation curve resolved to original: " + curve, trace.categories.Animation);
|
||||
if (curve instanceof common.CubicBezierAnimationCurve) {
|
||||
var animationCurve = <common.CubicBezierAnimationCurve>curve;
|
||||
var interpolator = (<any>android).support.v4.view.animation.PathInterpolatorCompat.create(animationCurve.x1, animationCurve.y1, animationCurve.x2, animationCurve.y2);
|
||||
let animationCurve = <common.CubicBezierAnimationCurve>curve;
|
||||
let interpolator = (<any>android).support.v4.view.animation.PathInterpolatorCompat.create(animationCurve.x1, animationCurve.y1, animationCurve.x2, animationCurve.y2);
|
||||
return interpolator;
|
||||
}
|
||||
return curve;
|
||||
|
@ -3,13 +3,15 @@ import common = require("./animation-common");
|
||||
import viewModule = require("ui/core/view");
|
||||
import trace = require("trace");
|
||||
import enums = require("ui/enums");
|
||||
import style = require("ui/styling/style");
|
||||
import dependencyObservable = require("ui/core/dependency-observable");
|
||||
|
||||
global.moduleMerge(common, exports);
|
||||
|
||||
var _transform = "_transform";
|
||||
var _skip = "_skip";
|
||||
let _transform = "_transform";
|
||||
let _skip = "_skip";
|
||||
|
||||
var FLT_MAX = 340282346638528859811704183484516925440.000000;
|
||||
let FLT_MAX = 340282346638528859811704183484516925440.000000;
|
||||
|
||||
declare var CASpringAnimation:any;
|
||||
|
||||
@ -29,19 +31,54 @@ class AnimationDelegateImpl extends NSObject {
|
||||
|
||||
private _finishedCallback: Function;
|
||||
private _propertyAnimation: common.PropertyAnimation;
|
||||
private _valueSource: number;
|
||||
|
||||
public static initWithFinishedCallback(finishedCallback: Function, propertyAnimation: common.PropertyAnimation): AnimationDelegateImpl {
|
||||
public static initWithFinishedCallback(finishedCallback: Function, propertyAnimation: common.PropertyAnimation, valueSource: number): AnimationDelegateImpl {
|
||||
let delegate = <AnimationDelegateImpl>AnimationDelegateImpl.new();
|
||||
delegate._finishedCallback = finishedCallback;
|
||||
delegate._propertyAnimation = propertyAnimation;
|
||||
delegate._valueSource = valueSource;
|
||||
return delegate;
|
||||
}
|
||||
|
||||
animationDidStart(anim: CAAnimation): void {
|
||||
var value = this._propertyAnimation.value;
|
||||
let value = this._propertyAnimation.value;
|
||||
|
||||
(<any>this._propertyAnimation.target)._suspendPresentationLayerUpdates();
|
||||
|
||||
if (this._valueSource !== undefined) {
|
||||
let targetStyle = this._propertyAnimation.target.style;
|
||||
switch (this._propertyAnimation.property) {
|
||||
case common.Properties.backgroundColor:
|
||||
targetStyle._setValue(style.backgroundColorProperty, value, this._valueSource);
|
||||
break;
|
||||
case common.Properties.opacity:
|
||||
targetStyle._setValue(style.opacityProperty, value, this._valueSource);
|
||||
break;
|
||||
case common.Properties.rotate:
|
||||
targetStyle._setValue(style.rotateProperty, value, this._valueSource);
|
||||
break;
|
||||
case common.Properties.translate:
|
||||
targetStyle._setValue(style.translateXProperty, value.x, this._valueSource);
|
||||
targetStyle._setValue(style.translateYProperty, value.y, this._valueSource);
|
||||
break;
|
||||
case common.Properties.scale:
|
||||
targetStyle._setValue(style.scaleXProperty, value.x, this._valueSource);
|
||||
targetStyle._setValue(style.scaleYProperty, value.y, this._valueSource);
|
||||
break;
|
||||
case _transform:
|
||||
if (value[common.Properties.translate] !== undefined) {
|
||||
targetStyle._setValue(style.translateXProperty, value[common.Properties.translate].x, this._valueSource);
|
||||
targetStyle._setValue(style.translateYProperty, value[common.Properties.translate].y, this._valueSource);
|
||||
}
|
||||
if (value[common.Properties.scale] !== undefined) {
|
||||
targetStyle._setValue(style.scaleXProperty, value[common.Properties.scale].x, this._valueSource);
|
||||
targetStyle._setValue(style.scaleYProperty, value[common.Properties.scale].y, this._valueSource);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (this._propertyAnimation.property) {
|
||||
case common.Properties.backgroundColor:
|
||||
this._propertyAnimation.target.backgroundColor = value;
|
||||
@ -52,6 +89,14 @@ class AnimationDelegateImpl extends NSObject {
|
||||
case common.Properties.rotate:
|
||||
this._propertyAnimation.target.rotate = value;
|
||||
break;
|
||||
case common.Properties.translate:
|
||||
this._propertyAnimation.target.translateX = value.x;
|
||||
this._propertyAnimation.target.translateY = value.y;
|
||||
break;
|
||||
case common.Properties.scale:
|
||||
this._propertyAnimation.target.scaleX = value.x;
|
||||
this._propertyAnimation.target.scaleY = value.y;
|
||||
break;
|
||||
case _transform:
|
||||
if (value[common.Properties.translate] !== undefined) {
|
||||
this._propertyAnimation.target.translateX = value[common.Properties.translate].x;
|
||||
@ -63,6 +108,7 @@ class AnimationDelegateImpl extends NSObject {
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
(<any>this._propertyAnimation.target)._resumePresentationLayerUpdates();
|
||||
}
|
||||
@ -71,11 +117,6 @@ class AnimationDelegateImpl extends NSObject {
|
||||
if (this._finishedCallback) {
|
||||
this._finishedCallback(!finished);
|
||||
}
|
||||
if (!finished) {
|
||||
if ((<any>this._propertyAnimation)._propertyResetCallback) {
|
||||
(<any>this._propertyAnimation)._propertyResetCallback((<any>this._propertyAnimation)._originalValue);
|
||||
}
|
||||
}
|
||||
if (finished && this.nextAnimation) {
|
||||
this.nextAnimation();
|
||||
}
|
||||
@ -87,9 +128,10 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
private _finishedAnimations: number;
|
||||
private _cancelledAnimations: number;
|
||||
private _mergedPropertyAnimations: Array<common.PropertyAnimation>;
|
||||
private _valueSource: number;
|
||||
|
||||
public play(): definition.AnimationPromise {
|
||||
var animationFinishedPromise = super.play();
|
||||
let animationFinishedPromise = super.play();
|
||||
this._finishedAnimations = 0;
|
||||
this._cancelledAnimations = 0;
|
||||
this._iOSAnimationFunction();
|
||||
@ -99,22 +141,34 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
public cancel(): void {
|
||||
super.cancel();
|
||||
|
||||
var i = 0;
|
||||
var length = this._mergedPropertyAnimations.length;
|
||||
let i = 0;
|
||||
let 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((<any>this._mergedPropertyAnimations[i])._originalValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constructor(animationDefinitions: Array<definition.AnimationDefinition>, playSequentially?: boolean) {
|
||||
super(animationDefinitions, playSequentially);
|
||||
|
||||
if (animationDefinitions.length > 0 && (<any>animationDefinitions[0]).valueSource !== undefined) {
|
||||
this._valueSource = (<any>animationDefinitions[0]).valueSource;
|
||||
}
|
||||
|
||||
if (!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);
|
||||
}
|
||||
else {
|
||||
this._mergedPropertyAnimations = this._propertyAnimations;
|
||||
}
|
||||
|
||||
var that = this;
|
||||
var animationFinishedCallback = (cancelled: boolean) => {
|
||||
let that = this;
|
||||
let 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) {
|
||||
@ -144,10 +198,10 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
}
|
||||
};
|
||||
|
||||
this._iOSAnimationFunction = Animation._createiOSAnimationFunction(this._mergedPropertyAnimations, 0, this._playSequentially, animationFinishedCallback);
|
||||
this._iOSAnimationFunction = Animation._createiOSAnimationFunction(this._mergedPropertyAnimations, 0, this._playSequentially, this._valueSource, animationFinishedCallback);
|
||||
}
|
||||
|
||||
private static _createiOSAnimationFunction(propertyAnimations: Array<common.PropertyAnimation>, index: number, playSequentially: boolean, finishedCallback: (cancelled?: boolean) => void): Function {
|
||||
private static _createiOSAnimationFunction(propertyAnimations: Array<common.PropertyAnimation>, index: number, playSequentially: boolean, valueSource: number, finishedCallback: (cancelled?: boolean) => void): Function {
|
||||
return (cancelled?: boolean) => {
|
||||
|
||||
if (cancelled && finishedCallback) {
|
||||
@ -156,34 +210,34 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
return;
|
||||
}
|
||||
|
||||
var animation = propertyAnimations[index];
|
||||
var args = Animation._getNativeAnimationArguments(animation);
|
||||
let animation = propertyAnimations[index];
|
||||
let args = Animation._getNativeAnimationArguments(animation, valueSource);
|
||||
|
||||
if (animation.curve === enums.AnimationCurve.spring) {
|
||||
Animation._createNativeSpringAnimation(propertyAnimations, index, playSequentially, args, animation, finishedCallback);
|
||||
Animation._createNativeSpringAnimation(propertyAnimations, index, playSequentially, args, animation, valueSource, finishedCallback);
|
||||
}
|
||||
else {
|
||||
Animation._createNativeAnimation(propertyAnimations, index, playSequentially, args, animation, finishedCallback);
|
||||
Animation._createNativeAnimation(propertyAnimations, index, playSequentially, args, animation, valueSource, finishedCallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static _getNativeAnimationArguments(animation: common.PropertyAnimation): AnimationInfo {
|
||||
private static _getNativeAnimationArguments(animation: common.PropertyAnimation, valueSource: number): AnimationInfo {
|
||||
|
||||
var nativeView = <UIView>animation.target._nativeView;
|
||||
var presentationLayer = nativeView.layer.presentationLayer();
|
||||
var propertyNameToAnimate = animation.property;
|
||||
var value = animation.value;
|
||||
var originalValue;
|
||||
let nativeView = <UIView>animation.target._nativeView;
|
||||
let presentationLayer = nativeView.layer.presentationLayer();
|
||||
let propertyNameToAnimate = animation.property;
|
||||
let value = animation.value;
|
||||
let originalValue;
|
||||
|
||||
var tempRotate = animation.target.rotate * Math.PI / 180;
|
||||
var abs
|
||||
let tempRotate = animation.target.rotate * Math.PI / 180;
|
||||
let abs;
|
||||
|
||||
switch (animation.property) {
|
||||
case common.Properties.backgroundColor:
|
||||
(<any>animation)._originalValue = animation.target.backgroundColor;
|
||||
(<any>animation)._propertyResetCallback = (value) => { animation.target.backgroundColor = value };
|
||||
if (presentationLayer != null) {
|
||||
(<any>animation)._propertyResetCallback = (value) => { animation.target.backgroundColor = value; };
|
||||
if (presentationLayer != null && valueSource !== dependencyObservable.ValueSource.Css) {
|
||||
originalValue = presentationLayer.backgroundColor;
|
||||
}
|
||||
else {
|
||||
@ -197,8 +251,8 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
break;
|
||||
case common.Properties.opacity:
|
||||
(<any>animation)._originalValue = animation.target.opacity;
|
||||
(<any>animation)._propertyResetCallback = (value) => { animation.target.opacity = value };
|
||||
if (presentationLayer != null) {
|
||||
(<any>animation)._propertyResetCallback = (value) => { animation.target.opacity = value; };
|
||||
if (presentationLayer != null && valueSource !== dependencyObservable.ValueSource.Css) {
|
||||
originalValue = presentationLayer.opacity;
|
||||
}
|
||||
else {
|
||||
@ -207,9 +261,9 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
break;
|
||||
case common.Properties.rotate:
|
||||
(<any>animation)._originalValue = animation.target.rotate;
|
||||
(<any>animation)._propertyResetCallback = (value) => { animation.target.rotate = value };
|
||||
(<any>animation)._propertyResetCallback = (value) => { animation.target.rotate = value; };
|
||||
propertyNameToAnimate = "transform.rotation";
|
||||
if (presentationLayer != null) {
|
||||
if (presentationLayer != null && valueSource !== dependencyObservable.ValueSource.Css) {
|
||||
originalValue = presentationLayer.valueForKeyPath("transform.rotation");
|
||||
}
|
||||
else {
|
||||
@ -224,8 +278,8 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
case common.Properties.translate:
|
||||
(<any>animation)._originalValue = { x:animation.target.translateX, y:animation.target.translateY };
|
||||
(<any>animation)._propertyResetCallback = (value) => { animation.target.translateX = value.x; animation.target.translateY = value.y; };
|
||||
propertyNameToAnimate = "transform"
|
||||
if (presentationLayer != null) {
|
||||
propertyNameToAnimate = "transform";
|
||||
if (presentationLayer != null && valueSource !== dependencyObservable.ValueSource.Css) {
|
||||
originalValue = NSValue.valueWithCATransform3D(presentationLayer.transform);
|
||||
}
|
||||
else {
|
||||
@ -236,8 +290,8 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
case common.Properties.scale:
|
||||
(<any>animation)._originalValue = { x:animation.target.scaleX, y:animation.target.scaleY };
|
||||
(<any>animation)._propertyResetCallback = (value) => { animation.target.scaleX = value.x; animation.target.scaleY = value.y; };
|
||||
propertyNameToAnimate = "transform"
|
||||
if (presentationLayer != null) {
|
||||
propertyNameToAnimate = "transform";
|
||||
if (presentationLayer != null && valueSource !== dependencyObservable.ValueSource.Css) {
|
||||
originalValue = NSValue.valueWithCATransform3D(presentationLayer.transform);
|
||||
}
|
||||
else {
|
||||
@ -246,7 +300,7 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
value = NSValue.valueWithCATransform3D(CATransform3DScale(nativeView.layer.transform, value.x, value.y, 1));
|
||||
break;
|
||||
case _transform:
|
||||
if (presentationLayer != null) {
|
||||
if (presentationLayer != null && valueSource !== dependencyObservable.ValueSource.Css) {
|
||||
originalValue = NSValue.valueWithCATransform3D(presentationLayer.transform);
|
||||
}
|
||||
else {
|
||||
@ -260,24 +314,24 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
animation.target.scaleX = value.xs;
|
||||
animation.target.scaleY = value.ys;
|
||||
};
|
||||
propertyNameToAnimate = "transform"
|
||||
propertyNameToAnimate = "transform";
|
||||
value = NSValue.valueWithCATransform3D(Animation._createNativeAffineTransform(animation));
|
||||
break;
|
||||
default:
|
||||
throw new Error("Cannot animate " + animation.property);
|
||||
}
|
||||
|
||||
var duration = 0.3;
|
||||
let duration = 0.3;
|
||||
if (animation.duration !== undefined) {
|
||||
duration = animation.duration / 1000.0;
|
||||
}
|
||||
|
||||
var delay = undefined;
|
||||
let delay = undefined;
|
||||
if (animation.delay) {
|
||||
delay = animation.delay / 1000.0;
|
||||
}
|
||||
|
||||
var repeatCount = undefined;
|
||||
let repeatCount = undefined;
|
||||
if (animation.iterations !== undefined) {
|
||||
if (animation.iterations === Number.POSITIVE_INFINITY) {
|
||||
repeatCount = FLT_MAX;
|
||||
@ -297,10 +351,10 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
};
|
||||
}
|
||||
|
||||
private static _createNativeAnimation(propertyAnimations: Array<common.PropertyAnimation>, index: number, playSequentially: boolean, args: AnimationInfo, animation: common.PropertyAnimation, finishedCallback: (cancelled?: boolean) => void) {
|
||||
private static _createNativeAnimation(propertyAnimations: Array<common.PropertyAnimation>, index: number, playSequentially: boolean, args: AnimationInfo, animation: common.PropertyAnimation, valueSource: number, finishedCallback: (cancelled?: boolean) => void) {
|
||||
|
||||
var nativeView = <UIView>animation.target._nativeView;
|
||||
var nativeAnimation = CABasicAnimation.animationWithKeyPath(args.propertyNameToAnimate);
|
||||
let nativeView = <UIView>animation.target._nativeView;
|
||||
let nativeAnimation = CABasicAnimation.animationWithKeyPath(args.propertyNameToAnimate);
|
||||
nativeAnimation.fromValue = args.fromValue;
|
||||
nativeAnimation.toValue = args.toValue;
|
||||
nativeAnimation.duration = args.duration;
|
||||
@ -314,14 +368,14 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
nativeAnimation.timingFunction = animation.curve;
|
||||
}
|
||||
|
||||
var animationDelegate = AnimationDelegateImpl.initWithFinishedCallback(finishedCallback, animation);
|
||||
let animationDelegate = AnimationDelegateImpl.initWithFinishedCallback(finishedCallback, animation, valueSource);
|
||||
nativeAnimation.setValueForKey(animationDelegate, "delegate");
|
||||
|
||||
nativeView.layer.addAnimationForKey(nativeAnimation, args.propertyNameToAnimate);
|
||||
|
||||
var callback = undefined;
|
||||
let callback = undefined;
|
||||
if (index + 1 < propertyAnimations.length) {
|
||||
callback = Animation._createiOSAnimationFunction(propertyAnimations, index+1, playSequentially, finishedCallback);
|
||||
callback = Animation._createiOSAnimationFunction(propertyAnimations, index + 1, playSequentially, valueSource, finishedCallback);
|
||||
if (!playSequentially) {
|
||||
callback();
|
||||
}
|
||||
@ -331,14 +385,14 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
}
|
||||
}
|
||||
|
||||
private static _createNativeSpringAnimation(propertyAnimations: Array<common.PropertyAnimation>, index: number, playSequentially: boolean, args: AnimationInfo, animation: common.PropertyAnimation, finishedCallback: (cancelled?: boolean) => void) {
|
||||
private static _createNativeSpringAnimation(propertyAnimations: Array<common.PropertyAnimation>, index: number, playSequentially: boolean, args: AnimationInfo, animation: common.PropertyAnimation, valueSource: number, finishedCallback: (cancelled?: boolean) => void) {
|
||||
|
||||
var nativeView = <UIView>animation.target._nativeView;
|
||||
let nativeView = <UIView>animation.target._nativeView;
|
||||
|
||||
var callback = undefined;
|
||||
var nextAnimation;
|
||||
let callback = undefined;
|
||||
let nextAnimation;
|
||||
if (index + 1 < propertyAnimations.length) {
|
||||
callback = Animation._createiOSAnimationFunction(propertyAnimations, index + 1, playSequentially, finishedCallback);
|
||||
callback = Animation._createiOSAnimationFunction(propertyAnimations, index + 1, playSequentially, valueSource, finishedCallback);
|
||||
if (!playSequentially) {
|
||||
callback();
|
||||
}
|
||||
@ -347,7 +401,7 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
}
|
||||
}
|
||||
|
||||
var delay = 0;
|
||||
let delay = 0;
|
||||
if (args.delay) {
|
||||
delay = args.delay;
|
||||
}
|
||||
@ -396,7 +450,7 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
}
|
||||
}
|
||||
if (finishedCallback) {
|
||||
var cancelled = !finished;
|
||||
let cancelled = !finished;
|
||||
finishedCallback(cancelled);
|
||||
}
|
||||
if (finished && nextAnimation) {
|
||||
@ -406,18 +460,18 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
}
|
||||
|
||||
private static _createNativeAffineTransform(animation: common.PropertyAnimation): CATransform3D {
|
||||
var value = animation.value;
|
||||
var result:CATransform3D = CATransform3DIdentity;
|
||||
let value = animation.value;
|
||||
let result:CATransform3D = CATransform3DIdentity;
|
||||
|
||||
if (value[common.Properties.translate] !== undefined) {
|
||||
var x = value[common.Properties.translate].x;
|
||||
var y = value[common.Properties.translate].y;
|
||||
let x = value[common.Properties.translate].x;
|
||||
let y = value[common.Properties.translate].y;
|
||||
result = CATransform3DTranslate(result, x, y, 0);
|
||||
}
|
||||
|
||||
if (value[common.Properties.scale] !== undefined) {
|
||||
var x = value[common.Properties.scale].x;
|
||||
var y = value[common.Properties.scale].y;
|
||||
let x = value[common.Properties.scale].x;
|
||||
let y = value[common.Properties.scale].y;
|
||||
result = CATransform3DScale(result, x, y, 1);
|
||||
}
|
||||
|
||||
@ -431,7 +485,7 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
}
|
||||
|
||||
private static _canBeMerged(animation1: common.PropertyAnimation, animation2: common.PropertyAnimation) {
|
||||
var result =
|
||||
let result =
|
||||
Animation._isAffineTransform(animation1.property) &&
|
||||
Animation._isAffineTransform(animation2.property) &&
|
||||
animation1.target === animation2.target &&
|
||||
@ -443,11 +497,11 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
}
|
||||
|
||||
private static _mergeAffineTransformAnimations(propertyAnimations: Array<common.PropertyAnimation>): Array<common.PropertyAnimation> {
|
||||
var result = new Array<common.PropertyAnimation>();
|
||||
let result = new Array<common.PropertyAnimation>();
|
||||
|
||||
var i = 0;
|
||||
var j;
|
||||
var length = propertyAnimations.length;
|
||||
let i = 0;
|
||||
let j;
|
||||
let length = propertyAnimations.length;
|
||||
for (; i < length; i++) {
|
||||
if (propertyAnimations[i][_skip]) {
|
||||
continue;
|
||||
@ -465,7 +519,7 @@ export class Animation extends common.Animation implements definition.Animation
|
||||
// rotate: 90,
|
||||
// scale: {x: 2, y: 2 }
|
||||
// }
|
||||
var newTransformAnimation: common.PropertyAnimation = {
|
||||
let newTransformAnimation: common.PropertyAnimation = {
|
||||
target: propertyAnimations[i].target,
|
||||
property: _transform,
|
||||
value: {},
|
||||
@ -510,12 +564,14 @@ export function _resolveAnimationCurve(curve: any): any {
|
||||
return CAMediaTimingFunction.functionWithName(kCAMediaTimingFunctionLinear);
|
||||
case enums.AnimationCurve.spring:
|
||||
return curve;
|
||||
case enums.AnimationCurve.ease:
|
||||
return CAMediaTimingFunction.functionWithControlPoints(0.25, 0.1, 0.25, 1.0);
|
||||
default:
|
||||
if (curve instanceof CAMediaTimingFunction) {
|
||||
return curve;
|
||||
}
|
||||
else if (curve instanceof common.CubicBezierAnimationCurve) {
|
||||
var animationCurve = <common.CubicBezierAnimationCurve>curve;
|
||||
let animationCurve = <common.CubicBezierAnimationCurve>curve;
|
||||
return CAMediaTimingFunction.functionWithControlPoints(animationCurve.x1, animationCurve.y1, animationCurve.x2, animationCurve.y2);
|
||||
}
|
||||
return undefined;
|
||||
@ -524,12 +580,12 @@ export function _resolveAnimationCurve(curve: any): any {
|
||||
|
||||
export function _getTransformMismatchErrorMessage(view: viewModule.View): string {
|
||||
// Order is important: translate, rotate, scale
|
||||
var result: CGAffineTransform = CGAffineTransformIdentity;
|
||||
let result: CGAffineTransform = CGAffineTransformIdentity;
|
||||
result = CGAffineTransformTranslate(result, view.translateX, view.translateY);
|
||||
result = CGAffineTransformRotate(result, view.rotate * Math.PI / 180);
|
||||
result = CGAffineTransformScale(result, view.scaleX, view.scaleY);
|
||||
var viewTransform = NSStringFromCGAffineTransform(result);
|
||||
var nativeTransform = NSStringFromCGAffineTransform(view._nativeView.transform);
|
||||
let viewTransform = NSStringFromCGAffineTransform(result);
|
||||
let nativeTransform = NSStringFromCGAffineTransform(view._nativeView.transform);
|
||||
|
||||
if (viewTransform !== nativeTransform) {
|
||||
return "View and Native transforms do not match. View: " + viewTransform + "; Native: " + nativeTransform;
|
||||
|
94
ui/animation/keyframe-animation.d.ts
vendored
Normal file
94
ui/animation/keyframe-animation.d.ts
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
declare module "ui/animation/keyframe-animation" {
|
||||
|
||||
import view = require("ui/core/view");
|
||||
|
||||
export interface KeyframeDeclaration {
|
||||
property: string;
|
||||
value: any;
|
||||
}
|
||||
|
||||
export interface KeyframeInfo {
|
||||
duration: number;
|
||||
curve: any;
|
||||
declarations: Array<KeyframeDeclaration>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines animation options for the View.animate method.
|
||||
*/
|
||||
export class KeyframeAnimationInfo {
|
||||
|
||||
/**
|
||||
* The animation name.
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* 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. Possible values are contained in the [AnimationCurve enumeration](../enums/AnimationCurve/README.md).
|
||||
* Alternatively, you can pass an instance of type UIViewAnimationCurve for iOS or android.animation.TimeInterpolator for Android.
|
||||
*/
|
||||
curve: any;
|
||||
|
||||
/**
|
||||
* Determines whether the animation values will be applied on the animated object after the animation finishes.
|
||||
*/
|
||||
isForwards: boolean;
|
||||
|
||||
/**
|
||||
* If true the animation will be played backwards.
|
||||
*/
|
||||
isReverse: boolean;
|
||||
|
||||
/**
|
||||
* Return animation keyframes.
|
||||
*/
|
||||
keyframes: Array<KeyframeInfo>;
|
||||
}
|
||||
|
||||
export class KeyframeAnimation {
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Returns true if the application is currently running.
|
||||
*/
|
||||
isPlaying: boolean;
|
||||
|
||||
/**
|
||||
* Plays the animation.
|
||||
*/
|
||||
public play: (view: view.View) => Promise<void>;
|
||||
|
||||
/**
|
||||
* Creates a keyframe animation from animation definition.
|
||||
*/
|
||||
public static keyframeAnimationFromInfo(info: KeyframeAnimationInfo, valueSourceModifier: number);
|
||||
}
|
||||
}
|
196
ui/animation/keyframe-animation.ts
Normal file
196
ui/animation/keyframe-animation.ts
Normal file
@ -0,0 +1,196 @@
|
||||
import definition = require("ui/animation/keyframe-animation");
|
||||
import view = require("ui/core/view");
|
||||
import enums = require("ui/enums");
|
||||
import style = require("ui/styling/style");
|
||||
|
||||
export class KeyframeDeclaration implements definition.KeyframeDeclaration {
|
||||
public property: string;
|
||||
public value: any;
|
||||
}
|
||||
|
||||
export class KeyframeInfo implements definition.KeyframeInfo {
|
||||
public duration: number;
|
||||
public curve: any;
|
||||
public declarations: Array<KeyframeDeclaration>;
|
||||
}
|
||||
|
||||
export class KeyframeAnimationInfo implements definition.KeyframeAnimationInfo {
|
||||
public name: string = "";
|
||||
public duration: number = 0.3;
|
||||
public delay: number = 0;
|
||||
public iterations: number = 1;
|
||||
public curve: any = enums.AnimationCurve.ease;
|
||||
public isForwards: boolean = false;
|
||||
public isReverse: boolean = false;
|
||||
public keyframes: Array<KeyframeInfo>;
|
||||
}
|
||||
|
||||
export class KeyframeAnimation {
|
||||
public animations: Array<Object>;
|
||||
public delay: number = 0;
|
||||
public iterations: number = 1;
|
||||
|
||||
private _resolve;
|
||||
private _reject;
|
||||
private _isPlaying: boolean;
|
||||
private _isForwards: boolean;
|
||||
|
||||
public static keyframeAnimationFromInfo(info: KeyframeAnimationInfo, valueSourceModifier: number) {
|
||||
let animations = new Array<Object>();
|
||||
let length = info.keyframes.length;
|
||||
let startDuration = 0;
|
||||
if (info.isReverse) {
|
||||
for (let index = length - 1; index >= 0; index --) {
|
||||
let keyframe = info.keyframes[index];
|
||||
startDuration = KeyframeAnimation.parseKeyframe(info, keyframe, animations, startDuration, valueSourceModifier);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (let index = 0; index < length; index ++) {
|
||||
let keyframe = info.keyframes[index];
|
||||
startDuration = KeyframeAnimation.parseKeyframe(info, keyframe, animations, startDuration, valueSourceModifier);
|
||||
}
|
||||
for (let index = length - 1; index > 0; index --) {
|
||||
let a1 = animations[index];
|
||||
let a2 = animations[index - 1];
|
||||
if (a2["curve"] !== undefined) {
|
||||
a1["curve"] = a2["curve"];
|
||||
a2["curve"] = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let index = 1; index < length; index++) {
|
||||
let a = animations[index];
|
||||
if (a["curve"] === undefined) {
|
||||
a["curve"] = info.curve;
|
||||
}
|
||||
}
|
||||
let animation: KeyframeAnimation = new KeyframeAnimation();
|
||||
animation.delay = info.delay;
|
||||
animation.iterations = info.iterations;
|
||||
animation.animations = animations;
|
||||
animation._isForwards = info.isForwards;
|
||||
return animation;
|
||||
}
|
||||
|
||||
private static parseKeyframe(info: KeyframeAnimationInfo, keyframe: KeyframeInfo, animations: Array<Object>, startDuration: number, valueSourceModifier: number): number {
|
||||
let animation = {};
|
||||
for (let declaration of keyframe.declarations) {
|
||||
animation[declaration.property] = declaration.value;
|
||||
}
|
||||
let duration = keyframe.duration;
|
||||
if (duration === 0) {
|
||||
duration = 0.01;
|
||||
}
|
||||
else {
|
||||
duration = (info.duration * duration) - startDuration;
|
||||
startDuration += duration;
|
||||
}
|
||||
animation["duration"] = info.isReverse ? info.duration - duration : duration;
|
||||
animation["curve"] = keyframe.curve;
|
||||
animation["forceLayer"] = true;
|
||||
animation["valueSource"] = valueSourceModifier;
|
||||
animations.push(animation);
|
||||
return startDuration;
|
||||
}
|
||||
|
||||
public get isPlaying(): boolean {
|
||||
return this._isPlaying;
|
||||
}
|
||||
|
||||
public play(view: view.View): Promise<void> {
|
||||
if (this._isPlaying) {
|
||||
throw new Error("Animation is already playing.");
|
||||
}
|
||||
|
||||
let animationFinishedPromise = new Promise<void>((resolve, reject) => {
|
||||
this._resolve = resolve;
|
||||
this._reject = reject;
|
||||
});
|
||||
|
||||
this._isPlaying = true;
|
||||
|
||||
if (this.delay !== 0) {
|
||||
let that = this;
|
||||
setTimeout(function (){ that.animate(view, 0, that.iterations); }, that.delay, that);
|
||||
}
|
||||
else {
|
||||
this.animate(view, 0, this.iterations);
|
||||
}
|
||||
|
||||
return animationFinishedPromise;
|
||||
}
|
||||
|
||||
private animate(view: view.View, index: number, iterations: number) {
|
||||
if (index === 0) {
|
||||
let animation = this.animations[0];
|
||||
let modifier = animation["valueSource"];
|
||||
|
||||
if ("backgroundColor" in animation) {
|
||||
view.style._setValue(style.backgroundColorProperty, animation["backgroundColor"], modifier);
|
||||
}
|
||||
if ("scale" in animation) {
|
||||
view.style._setValue(style.scaleXProperty, animation["scale"].x, modifier);
|
||||
view.style._setValue(style.scaleYProperty, animation["scale"].y, modifier);
|
||||
}
|
||||
if ("translate" in animation) {
|
||||
view.style._setValue(style.translateXProperty, animation["translate"].x, modifier);
|
||||
view.style._setValue(style.translateYProperty, animation["translate"].y, modifier);
|
||||
}
|
||||
if ("rotate" in animation) {
|
||||
view.style._setValue(style.rotateProperty, animation["rotate"], modifier);
|
||||
}
|
||||
if ("opacity" in animation) {
|
||||
view.style._setValue(style.opacityProperty, animation["opacity"], modifier);
|
||||
}
|
||||
|
||||
let that = this;
|
||||
setTimeout(function () { that.animate(view, 1, iterations); }, 1, that);
|
||||
}
|
||||
else if (index < 0 || index >= this.animations.length) {
|
||||
iterations -= 1;
|
||||
if (iterations > 0) {
|
||||
this.animate(view, 0, iterations);
|
||||
}
|
||||
else {
|
||||
if (this._isForwards === false) {
|
||||
let animation = this.animations[this.animations.length - 1];
|
||||
let modifier = animation["valueSource"];
|
||||
if ("backgroundColor" in animation) {
|
||||
view.style._resetValue(style.backgroundColorProperty, modifier);
|
||||
}
|
||||
if ("scale" in animation) {
|
||||
view.style._resetValue(style.scaleXProperty, modifier);
|
||||
view.style._resetValue(style.scaleYProperty, modifier);
|
||||
}
|
||||
if ("translate" in animation) {
|
||||
view.style._resetValue(style.translateXProperty, modifier);
|
||||
view.style._resetValue(style.translateYProperty, modifier);
|
||||
}
|
||||
if ("rotate" in animation) {
|
||||
view.style._resetValue(style.rotateProperty, modifier);
|
||||
}
|
||||
if ("opacity" in animation) {
|
||||
view.style._resetValue(style.opacityProperty, modifier);
|
||||
}
|
||||
}
|
||||
this._resolveAnimationFinishedPromise();
|
||||
}
|
||||
}
|
||||
else {
|
||||
view.animate(this.animations[index]).then(() => {
|
||||
this.animate(view, index + 1, iterations);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public _resolveAnimationFinishedPromise() {
|
||||
this._isPlaying = false;
|
||||
this._resolve();
|
||||
}
|
||||
|
||||
public _rejectAnimationFinishedPromise() {
|
||||
this._isPlaying = false;
|
||||
this._reject(new Error("Animation cancelled."));
|
||||
}
|
||||
}
|
@ -36,6 +36,24 @@ export class Button extends common.Button {
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
this._android.setOnTouchListener(new android.view.View.OnTouchListener(
|
||||
<utils.Owned & android.view.View.IOnTouchListener>{
|
||||
get owner() {
|
||||
return that.get();
|
||||
},
|
||||
|
||||
onTouch: function(v, ev) {
|
||||
if (ev.getAction() === 0) { // down
|
||||
this.owner._goToVisualState("highlighted");
|
||||
}
|
||||
else if (ev.getAction() === 1) { // up
|
||||
this.owner._goToVisualState("normal");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
public _onTextPropertyChanged(data: dependencyObservable.PropertyChangeData) {
|
||||
|
@ -6,6 +6,7 @@ import view = require("ui/core/view");
|
||||
import utils = require("utils/utils");
|
||||
import enums = require("ui/enums");
|
||||
import dependencyObservable = require("ui/core/dependency-observable");
|
||||
import styleScope = require("../styling/style-scope");
|
||||
|
||||
class TapHandlerImpl extends NSObject {
|
||||
private _owner: WeakRef<Button>;
|
||||
@ -47,6 +48,22 @@ export class Button extends common.Button {
|
||||
});
|
||||
}
|
||||
|
||||
public onLoaded() {
|
||||
super.onLoaded();
|
||||
if (this.parent !== null && this.page !== null) {
|
||||
let rootPage = this.page;
|
||||
let scope: styleScope.StyleScope = (<any>rootPage)._getStyleScope();
|
||||
if (scope.getVisualStates(this) !== undefined) {
|
||||
this._stateChangedHandler.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public onUnloaded() {
|
||||
super.onUnloaded();
|
||||
this._stateChangedHandler.stop();
|
||||
}
|
||||
|
||||
get ios(): UIButton {
|
||||
return this._ios;
|
||||
}
|
||||
|
4
ui/core/control-state-change.d.ts
vendored
4
ui/core/control-state-change.d.ts
vendored
@ -10,5 +10,9 @@
|
||||
* @param callback A callback called when a visual state of the UIControl is changed.
|
||||
*/
|
||||
constructor(control: any /* UIControl */, callback: (state: string) => void);
|
||||
|
||||
start();
|
||||
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
@ -21,22 +21,31 @@ export class ControlStateChangeListener implements definition.ControlStateChange
|
||||
private _observer: NSObject;
|
||||
private _states: string[];
|
||||
private _control: UIControl;
|
||||
private _observing: boolean = false;
|
||||
|
||||
private _callback: (state: string) => void;
|
||||
|
||||
constructor(control: UIControl, callback: (state: string) => void) {
|
||||
this._observer = ObserverClass.alloc();
|
||||
this._observer["_owner"] = this;
|
||||
|
||||
// TODO: Commenting for now, needs revision later since we must detach the observers upon control deallocation
|
||||
//control.addObserverForKeyPathOptionsContext(this._observer, "selected", NSKeyValueObservingOptions.NSKeyValueObservingOptionNew, null);
|
||||
//control.addObserverForKeyPathOptionsContext(this._observer, "enabled", NSKeyValueObservingOptions.NSKeyValueObservingOptionNew, null);
|
||||
//control.addObserverForKeyPathOptionsContext(this._observer, "highlighted", NSKeyValueObservingOptions.NSKeyValueObservingOptionNew, null);
|
||||
|
||||
this._control = control;
|
||||
this._callback = callback;
|
||||
}
|
||||
|
||||
public start() {
|
||||
if (!this._observing) {
|
||||
this._control.addObserverForKeyPathOptionsContext(this._observer, "highlighted", NSKeyValueObservingOptions.NSKeyValueObservingOptionNew, null);
|
||||
this._observing = true;
|
||||
}
|
||||
}
|
||||
|
||||
public stop() {
|
||||
if (this._observing) {
|
||||
this._observing = false;
|
||||
this._control.removeObserverForKeyPath(this._observer, "highlighted");
|
||||
}
|
||||
}
|
||||
|
||||
private _onEnabledChanged() {
|
||||
this._updateState();
|
||||
}
|
||||
@ -52,11 +61,8 @@ export class ControlStateChangeListener implements definition.ControlStateChange
|
||||
private _updateState() {
|
||||
var state = visualStateConstants.Normal;
|
||||
if (this._control.highlighted) {
|
||||
state = visualStateConstants.Pressed;
|
||||
} else if (this._control.highlighted) {
|
||||
// TODO:
|
||||
state = "highlighted";
|
||||
}
|
||||
|
||||
this._callback(state);
|
||||
}
|
||||
}
|
@ -636,6 +636,71 @@ export class ViewStyler implements style.Styler {
|
||||
(<android.view.View>view._nativeView).setPadding(left, top, right, bottom);
|
||||
}
|
||||
|
||||
// Rotate
|
||||
private static setRotateProperty(view: View, newValue: any) {
|
||||
view.rotate = newValue;
|
||||
}
|
||||
|
||||
private static resetRotateProperty(view: View, nativeValue: any) {
|
||||
view.rotate = nativeValue;
|
||||
}
|
||||
|
||||
private static getRotateProperty(view: View): any {
|
||||
return view.rotate;
|
||||
}
|
||||
|
||||
//ScaleX
|
||||
private static setScaleXProperty(view: View, newValue: any) {
|
||||
view.scaleX = newValue;
|
||||
}
|
||||
|
||||
private static resetScaleXProperty(view: View, nativeValue: any) {
|
||||
view.scaleX = nativeValue;
|
||||
}
|
||||
|
||||
private static getScaleXProperty(view: View): any {
|
||||
return view.scaleX;
|
||||
}
|
||||
|
||||
//ScaleY
|
||||
private static setScaleYProperty(view: View, newValue: any) {
|
||||
view.scaleY = newValue;
|
||||
}
|
||||
|
||||
private static resetScaleYProperty(view: View, nativeValue: any) {
|
||||
view.scaleY = nativeValue;
|
||||
}
|
||||
|
||||
private static getScaleYProperty(view: View): any {
|
||||
return view.scaleY;
|
||||
}
|
||||
|
||||
//TranslateX
|
||||
private static setTranslateXProperty(view: View, newValue: any) {
|
||||
view.translateX = newValue;
|
||||
}
|
||||
|
||||
private static resetTranslateXProperty(view: View, nativeValue: any) {
|
||||
view.translateX = nativeValue;
|
||||
}
|
||||
|
||||
private static getTranslateXProperty(view: View): any {
|
||||
return view.translateX;
|
||||
}
|
||||
|
||||
//TranslateY
|
||||
private static setTranslateYProperty(view: View, newValue: any) {
|
||||
view.translateY = newValue;
|
||||
}
|
||||
|
||||
private static resetTranslateYProperty(view: View, nativeValue: any) {
|
||||
view.translateY = nativeValue;
|
||||
}
|
||||
|
||||
private static getTranslateYProperty(view: View): any {
|
||||
return view.translateY;
|
||||
}
|
||||
|
||||
public static registerHandlers() {
|
||||
style.registerHandler(style.visibilityProperty, new style.StylePropertyChangedHandler(
|
||||
ViewStyler.setVisibilityProperty,
|
||||
@ -679,6 +744,31 @@ export class ViewStyler implements style.Styler {
|
||||
style.registerHandler(style.nativePaddingsProperty, new style.StylePropertyChangedHandler(
|
||||
ViewStyler.setPaddingProperty,
|
||||
ViewStyler.resetPaddingProperty), "LayoutBase");
|
||||
|
||||
style.registerHandler(style.rotateProperty, new style.StylePropertyChangedHandler(
|
||||
ViewStyler.setRotateProperty,
|
||||
ViewStyler.resetRotateProperty,
|
||||
ViewStyler.getRotateProperty));
|
||||
|
||||
style.registerHandler(style.scaleXProperty, new style.StylePropertyChangedHandler(
|
||||
ViewStyler.setScaleXProperty,
|
||||
ViewStyler.resetScaleXProperty,
|
||||
ViewStyler.getScaleXProperty));
|
||||
|
||||
style.registerHandler(style.scaleYProperty, new style.StylePropertyChangedHandler(
|
||||
ViewStyler.setScaleYProperty,
|
||||
ViewStyler.resetScaleYProperty,
|
||||
ViewStyler.getScaleYProperty));
|
||||
|
||||
style.registerHandler(style.translateXProperty, new style.StylePropertyChangedHandler(
|
||||
ViewStyler.setTranslateXProperty,
|
||||
ViewStyler.resetTranslateXProperty,
|
||||
ViewStyler.getTranslateXProperty));
|
||||
|
||||
style.registerHandler(style.translateYProperty, new style.StylePropertyChangedHandler(
|
||||
ViewStyler.setTranslateYProperty,
|
||||
ViewStyler.resetTranslateYProperty,
|
||||
ViewStyler.getTranslateYProperty));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -517,6 +517,71 @@ export class ViewStyler implements style.Styler {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Rotate
|
||||
private static setRotateProperty(view: View, newValue: any) {
|
||||
view.rotate = newValue;
|
||||
}
|
||||
|
||||
private static resetRotateProperty(view: View, nativeValue: any) {
|
||||
view.rotate = nativeValue;
|
||||
}
|
||||
|
||||
private static getRotateProperty(view: View): any {
|
||||
return view.rotate;
|
||||
}
|
||||
|
||||
//ScaleX
|
||||
private static setScaleXProperty(view: View, newValue: any) {
|
||||
view.scaleX = newValue;
|
||||
}
|
||||
|
||||
private static resetScaleXProperty(view: View, nativeValue: any) {
|
||||
view.scaleX = nativeValue;
|
||||
}
|
||||
|
||||
private static getScaleXProperty(view: View): any {
|
||||
return view.scaleX;
|
||||
}
|
||||
|
||||
//ScaleY
|
||||
private static setScaleYProperty(view: View, newValue: any) {
|
||||
view.scaleY = newValue;
|
||||
}
|
||||
|
||||
private static resetScaleYProperty(view: View, nativeValue: any) {
|
||||
view.scaleY = nativeValue;
|
||||
}
|
||||
|
||||
private static getScaleYProperty(view: View): any {
|
||||
return view.scaleY;
|
||||
}
|
||||
|
||||
//TranslateX
|
||||
private static setTranslateXProperty(view: View, newValue: any) {
|
||||
view.translateX = newValue;
|
||||
}
|
||||
|
||||
private static resetTranslateXProperty(view: View, nativeValue: any) {
|
||||
view.translateX = nativeValue;
|
||||
}
|
||||
|
||||
private static getTranslateXProperty(view: View): any {
|
||||
return view.translateX;
|
||||
}
|
||||
|
||||
//TranslateY
|
||||
private static setTranslateYProperty(view: View, newValue: any) {
|
||||
view.translateY = newValue;
|
||||
}
|
||||
|
||||
private static resetTranslateYProperty(view: View, nativeValue: any) {
|
||||
view.translateY = nativeValue;
|
||||
}
|
||||
|
||||
private static getTranslateYProperty(view: View): any {
|
||||
return view.translateY;
|
||||
}
|
||||
|
||||
public static registerHandlers() {
|
||||
style.registerHandler(style.backgroundInternalProperty, new style.StylePropertyChangedHandler(
|
||||
ViewStyler.setBackgroundInternalProperty,
|
||||
@ -545,6 +610,31 @@ export class ViewStyler implements style.Styler {
|
||||
ViewStyler.setBorderRadiusProperty,
|
||||
ViewStyler.resetBorderRadiusProperty,
|
||||
ViewStyler.getBorderRadiusProperty));
|
||||
|
||||
style.registerHandler(style.rotateProperty, new style.StylePropertyChangedHandler(
|
||||
ViewStyler.setRotateProperty,
|
||||
ViewStyler.resetRotateProperty,
|
||||
ViewStyler.getRotateProperty));
|
||||
|
||||
style.registerHandler(style.scaleXProperty, new style.StylePropertyChangedHandler(
|
||||
ViewStyler.setScaleXProperty,
|
||||
ViewStyler.resetScaleXProperty,
|
||||
ViewStyler.getScaleXProperty));
|
||||
|
||||
style.registerHandler(style.scaleYProperty, new style.StylePropertyChangedHandler(
|
||||
ViewStyler.setScaleYProperty,
|
||||
ViewStyler.resetScaleYProperty,
|
||||
ViewStyler.getScaleYProperty));
|
||||
|
||||
style.registerHandler(style.translateXProperty, new style.StylePropertyChangedHandler(
|
||||
ViewStyler.setTranslateXProperty,
|
||||
ViewStyler.resetTranslateXProperty,
|
||||
ViewStyler.getTranslateXProperty));
|
||||
|
||||
style.registerHandler(style.translateYProperty, new style.StylePropertyChangedHandler(
|
||||
ViewStyler.setTranslateYProperty,
|
||||
ViewStyler.resetTranslateYProperty,
|
||||
ViewStyler.getTranslateYProperty));
|
||||
}
|
||||
}
|
||||
|
||||
|
7
ui/enums/enums.d.ts
vendored
7
ui/enums/enums.d.ts
vendored
@ -523,7 +523,12 @@
|
||||
/**
|
||||
* Represents an animation curve type.
|
||||
*/
|
||||
module AnimationCurve {
|
||||
export module AnimationCurve {
|
||||
|
||||
/**
|
||||
* Default value. Specifies a transition effect with a slow start, then fast, then end slowly (equivalent to cubic-bezier(0.25,0.1,0.25,1))
|
||||
*/
|
||||
export var ease: string;
|
||||
|
||||
/**
|
||||
* An ease-in curve causes the animation to begin slowly, and then speed up as it progresses.
|
||||
|
@ -1,5 +1,3 @@
|
||||
import animationModule = require("ui/animation");
|
||||
|
||||
export module KeyboardType {
|
||||
export var datetime = "datetime";
|
||||
export var phone = "phone";
|
||||
@ -159,13 +157,17 @@ export module BackgroundRepeat {
|
||||
export var noRepeat: string = "no-repeat";
|
||||
}
|
||||
|
||||
var animationModule;
|
||||
|
||||
export module AnimationCurve {
|
||||
export var ease = "ease";
|
||||
export var easeIn = "easeIn";
|
||||
export var easeOut = "easeOut";
|
||||
export var easeInOut = "easeInOut";
|
||||
export var linear = "linear";
|
||||
export var spring = "spring";
|
||||
export function cubicBezier(x1: number, y1: number, x2: number, y2: number): animationModule.CubicBezierAnimationCurve {
|
||||
export function cubicBezier(x1: number, y1: number, x2: number, y2: number): Object {
|
||||
animationModule = animationModule || require("ui/animation");
|
||||
return new animationModule.CubicBezierAnimationCurve(x1, y1 ,x2, y2);
|
||||
}
|
||||
}
|
||||
|
@ -138,6 +138,21 @@ export class ListView extends common.ListView {
|
||||
}
|
||||
}
|
||||
|
||||
get _childrenCount(): number {
|
||||
let keys = Object.keys(this._realizedItems);
|
||||
return keys.length;
|
||||
}
|
||||
|
||||
public _eachChildView(callback: (child: viewModule.View) => boolean): void {
|
||||
let keys = Object.keys(this._realizedItems);
|
||||
let length = keys.length;
|
||||
for (let i = 0; i < length; i++) {
|
||||
let key = keys[i];
|
||||
let view: viewModule.View = this._realizedItems[key];
|
||||
callback(view);
|
||||
}
|
||||
}
|
||||
|
||||
public _getRealizedView(convertView: android.view.View, index: number) {
|
||||
if (!convertView) {
|
||||
return this._getItemTemplateContent(index);
|
||||
|
@ -241,6 +241,16 @@ export class ListView extends common.ListView {
|
||||
return this._ios;
|
||||
}
|
||||
|
||||
get _childrenCount(): number {
|
||||
return this._map.size;
|
||||
}
|
||||
|
||||
public _eachChildView(callback: (child: view.View) => boolean): void {
|
||||
this._map.forEach(function(view, key) {
|
||||
callback(view);
|
||||
}, this._map);
|
||||
}
|
||||
|
||||
public scrollToIndex(index: number) {
|
||||
if (this._ios) {
|
||||
this._ios.scrollToRowAtIndexPathAtScrollPositionAnimated(NSIndexPath.indexPathForItemInSection(index, 0),
|
||||
|
@ -8,6 +8,7 @@ import * as style from "../styling/style";
|
||||
import * as fileSystemModule from "file-system";
|
||||
import * as frameModule from "ui/frame";
|
||||
import proxy = require("ui/core/proxy");
|
||||
import keyframeAnimation = require("ui/animation/keyframe-animation");
|
||||
|
||||
var fs: typeof fileSystemModule;
|
||||
function ensureFS() {
|
||||
@ -175,6 +176,15 @@ export class Page extends ContentView implements dts.Page {
|
||||
}
|
||||
}
|
||||
|
||||
public removeCssSelectors(selectorExpression: string) {
|
||||
this._styleScope.removeSelectors(selectorExpression);
|
||||
this._refreshCss();
|
||||
}
|
||||
|
||||
public getKeyframeAnimationWithName(animationName: string): keyframeAnimation.KeyframeAnimationInfo {
|
||||
return this._styleScope.getKeyframeAnimationWithName(animationName);
|
||||
}
|
||||
|
||||
get frame(): frameModule.Frame {
|
||||
return <frameModule.Frame>this.parent;
|
||||
}
|
||||
|
12
ui/page/page.d.ts
vendored
12
ui/page/page.d.ts
vendored
@ -8,6 +8,7 @@ declare module "ui/page" {
|
||||
import frame = require("ui/frame");
|
||||
import actionBar = require("ui/action-bar");
|
||||
import dependencyObservable = require("ui/core/dependency-observable");
|
||||
import keyframeAnimation = require("ui/animation/keyframe-animation");
|
||||
|
||||
//@private
|
||||
import styleScope = require("ui/styling/style-scope");
|
||||
@ -120,6 +121,17 @@ declare module "ui/page" {
|
||||
*/
|
||||
addCssFile(cssFileName: string): void;
|
||||
|
||||
/**
|
||||
* Removes all selectors matching the specified selector expression.
|
||||
* @param selectorExpression - A valid selector expression.
|
||||
*/
|
||||
removeCssSelectors(selectorExpression: string): void;
|
||||
|
||||
/**
|
||||
* Returns a CSS keyframe animation with the specified name, if it exists.
|
||||
*/
|
||||
getKeyframeAnimationWithName(animationName: string): keyframeAnimation.KeyframeAnimationInfo;
|
||||
|
||||
/**
|
||||
* A property that is used to pass a data from another page (while navigate to).
|
||||
*/
|
||||
|
@ -1,5 +1,6 @@
|
||||
import enums = require("ui/enums");
|
||||
import color = require("color");
|
||||
import types = require("utils/types");
|
||||
|
||||
export function colorConverter(value: string): color.Color {
|
||||
return new color.Color(value);
|
||||
@ -73,3 +74,96 @@ export function opacityConverter(value: string): number {
|
||||
return result;
|
||||
}
|
||||
|
||||
export function timeConverter(value: string): number {
|
||||
var result = parseFloat(value);
|
||||
if (value.indexOf("ms") === -1) {
|
||||
result = result*1000;
|
||||
}
|
||||
result = Math.max(0.0, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function bezieArgumentConverter(value: string): number {
|
||||
var result = parseFloat(value);
|
||||
result = Math.max(0.0, result);
|
||||
result = Math.min(1.0, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function animationTimingFunctionConverter(value: string): Object {
|
||||
let result: Object = enums.AnimationCurve.ease;
|
||||
switch (value) {
|
||||
case "ease":
|
||||
result = enums.AnimationCurve.ease;
|
||||
break;
|
||||
case "linear":
|
||||
result = enums.AnimationCurve.linear;
|
||||
break;
|
||||
case "ease-in":
|
||||
result = enums.AnimationCurve.easeIn;
|
||||
break;
|
||||
case "ease-out":
|
||||
result = enums.AnimationCurve.easeOut;
|
||||
break;
|
||||
case "ease-in-out":
|
||||
result = enums.AnimationCurve.easeInOut;
|
||||
break;
|
||||
case "spring":
|
||||
result = enums.AnimationCurve.spring;
|
||||
break;
|
||||
default:
|
||||
if (value.indexOf("cubic-bezier(") === 0) {
|
||||
let bezierArr = value.substring(13).split(/[,]+/);
|
||||
if (bezierArr.length !== 4) {
|
||||
throw new Error("Invalid value for animation: " + value);
|
||||
}
|
||||
result = enums.AnimationCurve.cubicBezier(bezieArgumentConverter(bezierArr[0]),
|
||||
bezieArgumentConverter(bezierArr[1]),
|
||||
bezieArgumentConverter(bezierArr[2]),
|
||||
bezieArgumentConverter(bezierArr[3]));
|
||||
}
|
||||
else {
|
||||
throw new Error("Invalid value for animation: " + value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function transformConverter(value: any): Object {
|
||||
if (value === "none") {
|
||||
let operations = {};
|
||||
operations[value] = value;
|
||||
return operations;
|
||||
}
|
||||
else if (types.isString(value)) {
|
||||
let operations = {};
|
||||
let operator = "";
|
||||
let pos = 0;
|
||||
while (pos < value.length) {
|
||||
if (value[pos] === " " || value[pos] === ",") {
|
||||
pos ++;
|
||||
}
|
||||
else if (value[pos] === "(") {
|
||||
let start = pos + 1;
|
||||
while (pos < value.length && value[pos] !== ")") {
|
||||
pos ++;
|
||||
}
|
||||
let operand = value.substring(start, pos);
|
||||
operations[operator] = operand.trim();
|
||||
operator = "";
|
||||
pos ++;
|
||||
}
|
||||
else {
|
||||
operator += value[pos ++];
|
||||
}
|
||||
}
|
||||
return operations;
|
||||
}
|
||||
else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
202
ui/styling/css-animation-parser.ts
Normal file
202
ui/styling/css-animation-parser.ts
Normal file
@ -0,0 +1,202 @@
|
||||
import animationModule = require("ui/animation");
|
||||
import keyframeAnimationModule = require("ui/animation/keyframe-animation");
|
||||
import cssParser = require("css");
|
||||
import converters = require("../styling/converters");
|
||||
import types = require("utils/types");
|
||||
import colorModule = require("color");
|
||||
import styleProperty = require("ui/styling/style-property");
|
||||
|
||||
interface TransformInfo {
|
||||
scale: animationModule.Pair;
|
||||
translate: animationModule.Pair;
|
||||
}
|
||||
|
||||
let animationProperties = {
|
||||
"animation-name": (info, declaration) => info.name = declaration.value,
|
||||
"animation-duration": (info, declaration) => info.duration = converters.timeConverter(declaration.value),
|
||||
"animation-delay": (info, declaration) => info.delay = converters.timeConverter(declaration.value),
|
||||
"animation-timing-function": (info, declaration) => info.curve = converters.animationTimingFunctionConverter(declaration.value),
|
||||
"animation-iteration-count": (info, declaration) => declaration.value === "infinite" ? info.iterations = Number.MAX_VALUE : info.iterations = converters.numberConverter(declaration.value),
|
||||
"animation-direction": (info, declaration) => info.isReverse = declaration.value === "reverse",
|
||||
"animation-fill-mode": (info, declaration) => info.isForwards = declaration.value === "forwards"
|
||||
};
|
||||
|
||||
export class CssAnimationParser {
|
||||
|
||||
public static keyframeAnimationsFromCSSDeclarations(declarations: cssParser.Declaration[]): Array<keyframeAnimationModule.KeyframeAnimationInfo> {
|
||||
let animations: Array<keyframeAnimationModule.KeyframeAnimationInfo> = new Array<keyframeAnimationModule.KeyframeAnimationInfo>();
|
||||
let animationInfo: keyframeAnimationModule.KeyframeAnimationInfo = undefined;
|
||||
if (declarations === null || declarations === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
for (let declaration of declarations) {
|
||||
if (declaration.property === "animation") {
|
||||
CssAnimationParser.keyframeAnimationsFromCSSProperty(declaration.value, animations);
|
||||
}
|
||||
else {
|
||||
let propertyHandler = animationProperties[declaration.property];
|
||||
if (propertyHandler) {
|
||||
if (animationInfo === undefined) {
|
||||
animationInfo = new keyframeAnimationModule.KeyframeAnimationInfo();
|
||||
animations.push(animationInfo);
|
||||
}
|
||||
propertyHandler(animationInfo, declaration);
|
||||
}
|
||||
}
|
||||
}
|
||||
return animations.length === 0 ? undefined : animations;
|
||||
}
|
||||
|
||||
public static keyframesArrayFromCSS(cssKeyframes: Object): Array<keyframeAnimationModule.KeyframeInfo> {
|
||||
let parsedKeyframes = new Array<keyframeAnimationModule.KeyframeInfo>();
|
||||
for (let keyframe of (<any>cssKeyframes).keyframes) {
|
||||
let declarations = CssAnimationParser.parseKeyframeDeclarations(keyframe);
|
||||
for (let time of keyframe.values) {
|
||||
if (time === "from") {
|
||||
time = 0;
|
||||
}
|
||||
else if (time === "to") {
|
||||
time = 1;
|
||||
}
|
||||
else {
|
||||
time = parseFloat(time) / 100;
|
||||
if (time < 0) {
|
||||
time = 0;
|
||||
}
|
||||
if (time > 100) {
|
||||
time = 100;
|
||||
}
|
||||
}
|
||||
let current = parsedKeyframes[time];
|
||||
if (current === undefined) {
|
||||
current = <keyframeAnimationModule.KeyframeInfo>{};
|
||||
current.duration = time;
|
||||
parsedKeyframes[time] = current;
|
||||
}
|
||||
for (let declaration of <any>keyframe.declarations) {
|
||||
if (declaration.property === "animation-timing-function") {
|
||||
current.curve = converters.animationTimingFunctionConverter(declaration.value);
|
||||
}
|
||||
}
|
||||
current.declarations = declarations;
|
||||
}
|
||||
}
|
||||
let array = new Array();
|
||||
for (let parsedKeyframe in parsedKeyframes) {
|
||||
array.push(parsedKeyframes[parsedKeyframe]);
|
||||
}
|
||||
array.sort(function (a, b) { return a.duration - b.duration; });
|
||||
return array;
|
||||
}
|
||||
|
||||
private static keyframeAnimationsFromCSSProperty(value: any, animations: Array<keyframeAnimationModule.KeyframeAnimationInfo>) {
|
||||
if (types.isString(value)) {
|
||||
let values = value.split(/[,]+/);
|
||||
for (let parsedValue of values) {
|
||||
let animationInfo = new keyframeAnimationModule.KeyframeAnimationInfo();
|
||||
let arr = (<string>parsedValue).trim().split(/[ ]+/);
|
||||
|
||||
if (arr.length > 0) {
|
||||
animationInfo.name = arr[0];
|
||||
}
|
||||
if (arr.length > 1) {
|
||||
animationInfo.duration = converters.timeConverter(arr[1]);
|
||||
}
|
||||
if (arr.length > 2) {
|
||||
animationInfo.curve = converters.animationTimingFunctionConverter(arr[2]);
|
||||
}
|
||||
if (arr.length > 3) {
|
||||
animationInfo.delay = converters.timeConverter(arr[3]);
|
||||
}
|
||||
if (arr.length > 4) {
|
||||
animationInfo.iterations = parseInt(arr[4]);
|
||||
}
|
||||
if (arr.length > 5) {
|
||||
animationInfo.isReverse = arr[4] === "reverse";
|
||||
}
|
||||
if (arr.length > 6) {
|
||||
animationInfo.isForwards = arr[5] === "forwards";
|
||||
}
|
||||
if (arr.length > 7) {
|
||||
throw new Error("Invalid value for animation: " + value);
|
||||
}
|
||||
animations.push(animationInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static parseKeyframeDeclarations(keyframe: Object): Array<keyframeAnimationModule.KeyframeDeclaration> {
|
||||
let declarations = {};
|
||||
let transforms = { scale: undefined, translate: undefined };
|
||||
for (let declaration of (<any>keyframe).declarations) {
|
||||
let property = styleProperty.getPropertyByCssName(declaration.property);
|
||||
if (property) {
|
||||
let val = declaration.value;
|
||||
if (property.name === "opacity") {
|
||||
val = parseFloat(val);
|
||||
}
|
||||
else if (property.name === "backgroundColor") {
|
||||
val = new colorModule.Color(val);
|
||||
}
|
||||
declarations[property.name] = val;
|
||||
}
|
||||
else {
|
||||
let pairs = styleProperty.getShorthandPairs(declaration.property, declaration.value);
|
||||
if (pairs) {
|
||||
for (let j = 0; j < pairs.length; j++) {
|
||||
let pair = pairs[j];
|
||||
if (!this.preprocessAnimationValues(pair, transforms)) {
|
||||
declarations[pair.property.name] = pair.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (transforms.scale !== undefined) {
|
||||
declarations["scale"] = transforms.scale;
|
||||
}
|
||||
if (transforms.translate !== undefined) {
|
||||
declarations["translate"] = transforms.translate;
|
||||
}
|
||||
let array = new Array<keyframeAnimationModule.KeyframeDeclaration>();
|
||||
for (let declaration in declarations) {
|
||||
let keyframeDeclaration = <keyframeAnimationModule.KeyframeDeclaration>{};
|
||||
keyframeDeclaration.property = declaration;
|
||||
keyframeDeclaration.value = declarations[declaration];
|
||||
array.push(keyframeDeclaration);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
private static preprocessAnimationValues(pair: styleProperty.KeyValuePair<styleProperty.Property, any>, transforms: TransformInfo) {
|
||||
if (pair.property.name === "scaleX") {
|
||||
if (transforms.scale === undefined) {
|
||||
transforms.scale = { x: 1, y: 1 };
|
||||
}
|
||||
transforms.scale.x = pair.value;
|
||||
return true;
|
||||
}
|
||||
if (pair.property.name === "scaleY") {
|
||||
if (transforms.scale === undefined) {
|
||||
transforms.scale = { x: 1, y: 1 };
|
||||
}
|
||||
transforms.scale.y = pair.value;
|
||||
return true;
|
||||
}
|
||||
if (pair.property.name === "translateX") {
|
||||
if (transforms.translate === undefined) {
|
||||
transforms.translate = { x: 0, y: 0 };
|
||||
}
|
||||
transforms.translate.x = pair.value;
|
||||
return true;
|
||||
}
|
||||
if (pair.property.name === "translateY") {
|
||||
if (transforms.translate === undefined) {
|
||||
transforms.translate = { x: 0, y: 0 };
|
||||
}
|
||||
transforms.translate.y = pair.value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
5
ui/styling/css-selector.d.ts
vendored
5
ui/styling/css-selector.d.ts
vendored
@ -2,6 +2,7 @@
|
||||
import view = require("ui/core/view");
|
||||
import cssParser = require("css");
|
||||
import styleProperty = require("ui/styling/style-property");
|
||||
import keyframeAnimation = require("ui/animation/keyframe-animation");
|
||||
|
||||
export class CssSelector {
|
||||
constructor(expression: string, declarations: cssParser.Declaration[]);
|
||||
@ -13,9 +14,11 @@
|
||||
|
||||
specificity: number;
|
||||
|
||||
animations: Array<keyframeAnimation.KeyframeAnimationInfo>;
|
||||
|
||||
matches(view: view.View): boolean;
|
||||
|
||||
apply(view: view.View);
|
||||
apply(view: view.View, valueSourceModifier: number);
|
||||
|
||||
eachSetter(callback: (property: styleProperty.Property, resolvedValue: any) => void);
|
||||
}
|
||||
|
@ -5,14 +5,18 @@ import * as trace from "trace";
|
||||
import * as styleProperty from "ui/styling/style-property";
|
||||
import * as types from "utils/types";
|
||||
import * as utils from "utils/utils";
|
||||
import keyframeAnimation = require("ui/animation/keyframe-animation");
|
||||
import cssAnimationParser = require("./css-animation-parser");
|
||||
import {getSpecialPropertySetter} from "ui/builder/special-properties";
|
||||
|
||||
var ID_SPECIFICITY = 1000000;
|
||||
var ATTR_SPECIFITY = 10000;
|
||||
var CLASS_SPECIFICITY = 100;
|
||||
var TYPE_SPECIFICITY = 1;
|
||||
let ID_SPECIFICITY = 1000000;
|
||||
let ATTR_SPECIFITY = 10000;
|
||||
let CLASS_SPECIFICITY = 100;
|
||||
let TYPE_SPECIFICITY = 1;
|
||||
|
||||
export class CssSelector {
|
||||
public animations: Array<keyframeAnimation.KeyframeAnimationInfo>;
|
||||
|
||||
private _expression: string;
|
||||
private _declarations: cssParser.Declaration[];
|
||||
private _attrExpression: string;
|
||||
@ -22,7 +26,7 @@ export class CssSelector {
|
||||
let leftSquareBracketIndex = expression.indexOf(LSBRACKET);
|
||||
if (leftSquareBracketIndex > 0) {
|
||||
// extracts what is inside square brackets ([target = 'test'] will extract "target = 'test'")
|
||||
var paramsRegex = /\[\s*(.*)\s*\]/;
|
||||
let paramsRegex = /\[\s*(.*)\s*\]/;
|
||||
let attrParams = paramsRegex.exec(expression);
|
||||
if (attrParams && attrParams.length > 1) {
|
||||
this._attrExpression = attrParams[1].trim();
|
||||
@ -34,6 +38,7 @@ export class CssSelector {
|
||||
}
|
||||
}
|
||||
this._declarations = declarations;
|
||||
this.animations = cssAnimationParser.CssAnimationParser.keyframeAnimationsFromCSSDeclarations(declarations);
|
||||
}
|
||||
|
||||
get expression(): string {
|
||||
@ -52,11 +57,16 @@ export class CssSelector {
|
||||
throw "Specificity property is abstract";
|
||||
}
|
||||
|
||||
protected get valueSourceModifier(): number {
|
||||
return observable.ValueSource.Css;
|
||||
}
|
||||
|
||||
public matches(view: view.View): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
public apply(view: view.View) {
|
||||
public apply(view: view.View, valueSourceModifier: number) {
|
||||
let modifier = valueSourceModifier || this.valueSourceModifier;
|
||||
this.eachSetter((property, value) => {
|
||||
if (types.isString(property)) {
|
||||
let attrHandled = false;
|
||||
@ -72,12 +82,20 @@ export class CssSelector {
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
view.style._setValue(property, value, observable.ValueSource.Css);
|
||||
view.style._setValue(property, value, modifier);
|
||||
} catch (ex) {
|
||||
trace.write("Error setting property: " + property.name + " view: " + view + " value: " + value + " " + ex, trace.categories.Style, trace.messageType.error);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (this.animations && view.isLoaded) {
|
||||
for (let animationInfo of this.animations) {
|
||||
let realAnimation = keyframeAnimation.KeyframeAnimation.keyframeAnimationFromInfo(animationInfo, modifier);
|
||||
if (realAnimation) {
|
||||
realAnimation.play(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public eachSetter(callback: (property, resolvedValue: any) => void) {
|
||||
@ -93,7 +111,7 @@ export class CssSelector {
|
||||
callback(property, resolvedValue);
|
||||
}
|
||||
else {
|
||||
var pairs = styleProperty.getShorthandPairs(name, resolvedValue);
|
||||
let pairs = styleProperty.getShorthandPairs(name, resolvedValue);
|
||||
if (pairs) {
|
||||
for (let j = 0; j < pairs.length; j++) {
|
||||
let pair = pairs[j];
|
||||
@ -178,7 +196,7 @@ class CssClassSelector extends CssSelector {
|
||||
return CLASS_SPECIFICITY;
|
||||
}
|
||||
public matches(view: view.View): boolean {
|
||||
var expectedClass = this.expression;
|
||||
let expectedClass = this.expression;
|
||||
let result = view._cssClasses.some((cssClass, i, arr) => { return cssClass === expectedClass });
|
||||
if (result && this.attrExpression) {
|
||||
return matchesAttr(this.attrExpression, view);
|
||||
@ -357,10 +375,14 @@ export class CssVisualStateSelector extends CssSelector {
|
||||
return this._state;
|
||||
}
|
||||
|
||||
protected get valueSourceModifier(): number {
|
||||
return observable.ValueSource.VisualState;
|
||||
}
|
||||
|
||||
constructor(expression: string, declarations: cssParser.Declaration[]) {
|
||||
super(expression, declarations);
|
||||
|
||||
var args = expression.split(COLON);
|
||||
let args = expression.split(COLON);
|
||||
this._key = args[0];
|
||||
this._state = args[1];
|
||||
|
||||
@ -381,13 +403,13 @@ export class CssVisualStateSelector extends CssSelector {
|
||||
}
|
||||
|
||||
public matches(view: view.View): boolean {
|
||||
var matches = true;
|
||||
let matches = true;
|
||||
if (this._isById) {
|
||||
matches = this._match === view.id;
|
||||
}
|
||||
|
||||
if (this._isByClass) {
|
||||
var expectedClass = this._match;
|
||||
let expectedClass = this._match;
|
||||
matches = view._cssClasses.some((cssClass, i, arr) => { return cssClass === expectedClass });
|
||||
}
|
||||
|
||||
@ -407,18 +429,18 @@ export class CssVisualStateSelector extends CssSelector {
|
||||
}
|
||||
}
|
||||
|
||||
var HASH = "#";
|
||||
var DOT = ".";
|
||||
var COLON = ":";
|
||||
var SPACE = " ";
|
||||
var GTHAN = ">";
|
||||
var LSBRACKET = "[";
|
||||
var RSBRACKET = "]";
|
||||
var EQUAL = "=";
|
||||
let HASH = "#";
|
||||
let DOT = ".";
|
||||
let COLON = ":";
|
||||
let SPACE = " ";
|
||||
let GTHAN = ">";
|
||||
let LSBRACKET = "[";
|
||||
let RSBRACKET = "]";
|
||||
let EQUAL = "=";
|
||||
|
||||
export function createSelector(expression: string, declarations: cssParser.Declaration[]): CssSelector {
|
||||
let goodExpr = expression.replace(/>/g, " > ").replace(/\s\s+/g, " ");
|
||||
var spaceIndex = goodExpr.indexOf(SPACE);
|
||||
let spaceIndex = goodExpr.indexOf(SPACE);
|
||||
if (spaceIndex >= 0) {
|
||||
return new CssCompositeSelector(goodExpr, declarations);
|
||||
}
|
||||
@ -462,6 +484,6 @@ class InlineStyleSelector extends CssSelector {
|
||||
}
|
||||
|
||||
export function applyInlineSyle(view: view.View, declarations: cssParser.Declaration[]) {
|
||||
var localStyleSelector = new InlineStyleSelector(declarations);
|
||||
let localStyleSelector = new InlineStyleSelector(declarations);
|
||||
localStyleSelector.apply(view);
|
||||
}
|
||||
|
4
ui/styling/style-scope.d.ts
vendored
4
ui/styling/style-scope.d.ts
vendored
@ -3,6 +3,7 @@ declare module "ui/styling/style-scope" {
|
||||
import view = require("ui/core/view");
|
||||
import cssSelector = require("ui/styling/css-selector");
|
||||
import cssParser = require("css");
|
||||
import keyframeAnimation = require("ui/animation/keyframe-animation");
|
||||
|
||||
export class StyleScope {
|
||||
public css: string;
|
||||
@ -14,6 +15,9 @@ declare module "ui/styling/style-scope" {
|
||||
|
||||
public applySelectors(view: view.View): void
|
||||
public getVisualStates(view: view.View): Object;
|
||||
|
||||
public removeSelectors(selectorExpression: string);
|
||||
public getKeyframeAnimationWithName(animationName: string): keyframeAnimation.KeyframeAnimationInfo;
|
||||
}
|
||||
|
||||
export function applyInlineSyle(view: view.View, style: string): void;
|
||||
|
@ -7,6 +7,9 @@ import * as typesModule from "utils/types";
|
||||
import * as utilsModule from "utils/utils";
|
||||
import * as fileSystemModule from "file-system";
|
||||
import * as visualStateModule from "./visual-state";
|
||||
import keyframeAnimation = require("ui/animation/keyframe-animation");
|
||||
import cssAnimationParser = require("./css-animation-parser");
|
||||
import observable = require("ui/core/dependency-observable");
|
||||
|
||||
var types: typeof typesModule;
|
||||
function ensureTypes() {
|
||||
@ -50,10 +53,12 @@ export class StyleScope {
|
||||
private _localCssSelectorVersion: number = 0;
|
||||
private _localCssSelectorsAppliedVersion: number = 0;
|
||||
private _applicationCssSelectorsAppliedVersion: number = 0;
|
||||
private _keyframes = {};
|
||||
|
||||
get css(): string {
|
||||
return this._css;
|
||||
}
|
||||
|
||||
set css(value: string) {
|
||||
this._cssFileName = undefined;
|
||||
this.setCss(value);
|
||||
@ -71,7 +76,7 @@ export class StyleScope {
|
||||
|
||||
this._reset();
|
||||
|
||||
const parsedSelectors = StyleScope.createSelectorsFromCss(cssString, cssFileName);
|
||||
const parsedSelectors = StyleScope.createSelectorsFromCss(cssString, cssFileName, this._keyframes);
|
||||
if (append) {
|
||||
this._localCssSelectors.push.apply(this._localCssSelectors, parsedSelectors);
|
||||
} else {
|
||||
@ -82,35 +87,51 @@ export class StyleScope {
|
||||
this.ensureSelectors();
|
||||
}
|
||||
|
||||
public static createSelectorsFromCss(css: string, cssFileName: string): cssSelector.CssSelector[] {
|
||||
try {
|
||||
var pageCssSyntaxTree = css ? cssParser.parse(css, { source: cssFileName }) : null;
|
||||
|
||||
var pageCssSelectors = new Array<cssSelector.CssSelector>();
|
||||
|
||||
if (pageCssSyntaxTree) {
|
||||
pageCssSelectors = StyleScope._joinCssSelectorsArrays([pageCssSelectors, StyleScope.createSelectorsFromImports(pageCssSyntaxTree)]);
|
||||
pageCssSelectors = StyleScope._joinCssSelectorsArrays([pageCssSelectors, StyleScope.createSelectorsFromSyntaxTree(pageCssSyntaxTree)]);
|
||||
public removeSelectors(selectorExpression: string) {
|
||||
for (let i = this._mergedCssSelectors.length - 1; i >= 0; i--) {
|
||||
let selector = this._mergedCssSelectors[i];
|
||||
if (selector.expression === selectorExpression) {
|
||||
this._mergedCssSelectors.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public getKeyframeAnimationWithName(animationName: string): keyframeAnimation.KeyframeAnimationInfo {
|
||||
let keyframes = this._keyframes[animationName];
|
||||
if (keyframes !== undefined) {
|
||||
let animation = new keyframeAnimation.KeyframeAnimationInfo();
|
||||
animation.keyframes = cssAnimationParser.CssAnimationParser.keyframesArrayFromCSS(keyframes);
|
||||
return animation;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public static createSelectorsFromCss(css: string, cssFileName: string, keyframes: Object): cssSelector.CssSelector[] {
|
||||
try {
|
||||
let pageCssSyntaxTree = css ? cssParser.parse(css, { source: cssFileName }) : null;
|
||||
let pageCssSelectors = new Array<cssSelector.CssSelector>();
|
||||
if (pageCssSyntaxTree) {
|
||||
pageCssSelectors = StyleScope._joinCssSelectorsArrays([pageCssSelectors, StyleScope.createSelectorsFromImports(pageCssSyntaxTree, keyframes)]);
|
||||
pageCssSelectors = StyleScope._joinCssSelectorsArrays([pageCssSelectors, StyleScope.createSelectorsFromSyntaxTree(pageCssSyntaxTree, keyframes)]);
|
||||
}
|
||||
return pageCssSelectors;
|
||||
} catch (e) {
|
||||
trace.write("Css styling failed: " + e, trace.categories.Error, trace.messageType.error);
|
||||
}
|
||||
}
|
||||
|
||||
public static createSelectorsFromImports(tree: cssParser.SyntaxTree): cssSelector.CssSelector[] {
|
||||
var selectors = new Array<cssSelector.CssSelector>();
|
||||
public static createSelectorsFromImports(tree: cssParser.SyntaxTree, keyframes: Object): cssSelector.CssSelector[] {
|
||||
let selectors = new Array<cssSelector.CssSelector>();
|
||||
ensureTypes();
|
||||
|
||||
if (!types.isNullOrUndefined(tree)) {
|
||||
var imports = tree["stylesheet"]["rules"].filter(r=> r.type === "import");
|
||||
let imports = tree["stylesheet"]["rules"].filter(r=> r.type === "import");
|
||||
|
||||
for (var i = 0; i < imports.length; i++) {
|
||||
var importItem = imports[i]["import"];
|
||||
for (let i = 0; i < imports.length; i++) {
|
||||
let importItem = imports[i]["import"];
|
||||
|
||||
var match = importItem && (<string>importItem).match(pattern);
|
||||
var url = match && match[2];
|
||||
let match = importItem && (<string>importItem).match(pattern);
|
||||
let url = match && match[2];
|
||||
|
||||
if (!types.isNullOrUndefined(url)) {
|
||||
ensureUtils();
|
||||
@ -118,16 +139,16 @@ export class StyleScope {
|
||||
if (utils.isFileOrResourcePath(url)) {
|
||||
ensureFS();
|
||||
|
||||
var fileName = types.isString(url) ? url.trim() : "";
|
||||
let fileName = types.isString(url) ? url.trim() : "";
|
||||
if (fileName.indexOf("~/") === 0) {
|
||||
fileName = fs.path.join(fs.knownFolders.currentApp().path, fileName.replace("~/", ""));
|
||||
}
|
||||
|
||||
if (fs.File.exists(fileName)) {
|
||||
var file = fs.File.fromPath(fileName);
|
||||
var text = file.readTextSync();
|
||||
let file = fs.File.fromPath(fileName);
|
||||
let text = file.readTextSync();
|
||||
if (text) {
|
||||
selectors = StyleScope._joinCssSelectorsArrays([selectors, StyleScope.createSelectorsFromCss(text, fileName)]);
|
||||
selectors = StyleScope._joinCssSelectorsArrays([selectors, StyleScope.createSelectorsFromCss(text, fileName, keyframes)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -152,6 +173,7 @@ export class StyleScope {
|
||||
|
||||
if (toMerge.length > 0) {
|
||||
this._mergedCssSelectors = StyleScope._joinCssSelectorsArrays(toMerge);
|
||||
this._applyKeyframesOnSelectors();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@ -159,8 +181,8 @@ export class StyleScope {
|
||||
}
|
||||
|
||||
private static _joinCssSelectorsArrays(arrays: Array<Array<cssSelector.CssSelector>>): Array<cssSelector.CssSelector> {
|
||||
var mergedResult = [];
|
||||
var i;
|
||||
let mergedResult = [];
|
||||
let i;
|
||||
for (i = 0; i < arrays.length; i++) {
|
||||
if (arrays[i]) {
|
||||
mergedResult.push.apply(mergedResult, arrays[i]);
|
||||
@ -175,8 +197,7 @@ export class StyleScope {
|
||||
this.ensureSelectors();
|
||||
|
||||
view.style._beginUpdate();
|
||||
|
||||
var i,
|
||||
let i,
|
||||
selector: cssSelector.CssSelector,
|
||||
matchedStateSelectors = new Array<cssSelector.CssVisualStateSelector>()
|
||||
|
||||
@ -187,14 +208,14 @@ export class StyleScope {
|
||||
if (selector instanceof cssSelector.CssVisualStateSelector) {
|
||||
matchedStateSelectors.push(<cssSelector.CssVisualStateSelector>selector);
|
||||
} else {
|
||||
selector.apply(view);
|
||||
selector.apply(view, observable.ValueSource.Css);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (matchedStateSelectors.length > 0) {
|
||||
// Create a key for all matched selectors for this element
|
||||
var key: string = "";
|
||||
let key: string = "";
|
||||
matchedStateSelectors.forEach((s) => key += s.key + "|");
|
||||
|
||||
// Associate the view to the created key
|
||||
@ -210,7 +231,7 @@ export class StyleScope {
|
||||
}
|
||||
|
||||
public getVisualStates(view: view.View): Object {
|
||||
var key = this._viewIdToKey[view._domId];
|
||||
let key = this._viewIdToKey[view._domId];
|
||||
if (key === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
@ -219,7 +240,7 @@ export class StyleScope {
|
||||
}
|
||||
|
||||
private _createVisualsStatesForSelectors(key: string, matchedStateSelectors: Array<cssSelector.CssVisualStateSelector>) {
|
||||
var i,
|
||||
let i,
|
||||
allStates = {},
|
||||
stateSelector: cssSelector.CssVisualStateSelector;
|
||||
|
||||
@ -229,25 +250,30 @@ export class StyleScope {
|
||||
for (i = 0; i < matchedStateSelectors.length; i++) {
|
||||
stateSelector = matchedStateSelectors[i];
|
||||
|
||||
var visualState = allStates[stateSelector.state];
|
||||
let visualState = allStates[stateSelector.state];
|
||||
if (!visualState) {
|
||||
visualState = new vs.VisualState();
|
||||
allStates[stateSelector.state] = visualState;
|
||||
}
|
||||
|
||||
// add all stateSelectors instead of adding setters
|
||||
if (stateSelector.animations && stateSelector.animations.length > 0) {
|
||||
visualState.animatedSelectors.push(stateSelector);
|
||||
}
|
||||
else {
|
||||
stateSelector.eachSetter((property, value) => {
|
||||
visualState.setters[property.name] = value;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static createSelectorsFromSyntaxTree(ast: cssParser.SyntaxTree): Array<cssSelector.CssSelector> {
|
||||
var result: Array<cssSelector.CssSelector> = [];
|
||||
|
||||
var rules = ast.stylesheet.rules;
|
||||
var rule: cssParser.Rule;
|
||||
var i;
|
||||
var j;
|
||||
private static createSelectorsFromSyntaxTree(ast: cssParser.SyntaxTree, keyframes: Object): Array<cssSelector.CssSelector> {
|
||||
let result: Array<cssSelector.CssSelector> = [];
|
||||
let rules = ast.stylesheet.rules;
|
||||
let rule: cssParser.Rule;
|
||||
let i;
|
||||
let j;
|
||||
|
||||
// Create selectors form AST
|
||||
for (i = 0; i < rules.length; i++) {
|
||||
@ -256,10 +282,10 @@ export class StyleScope {
|
||||
if (rule.type === "rule") {
|
||||
|
||||
// Filter comment nodes.
|
||||
var filteredDeclarations = [];
|
||||
let filteredDeclarations = [];
|
||||
if (rule.declarations) {
|
||||
for (j = 0; j < rule.declarations.length; j++) {
|
||||
var declaration = rule.declarations[j];
|
||||
let declaration = rule.declarations[j];
|
||||
if (declaration.type === "declaration") {
|
||||
filteredDeclarations.push({
|
||||
property: declaration.property.toLowerCase(),
|
||||
@ -268,11 +294,12 @@ export class StyleScope {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < rule.selectors.length; j++) {
|
||||
result.push(cssSelector.createSelector(rule.selectors[j], filteredDeclarations));
|
||||
}
|
||||
//}
|
||||
}
|
||||
else if (rule.type === "keyframes") {
|
||||
keyframes[(<any>rule).name] = rule;
|
||||
}
|
||||
}
|
||||
|
||||
@ -283,12 +310,26 @@ export class StyleScope {
|
||||
this._statesByKey = {};
|
||||
this._viewIdToKey = {};
|
||||
}
|
||||
|
||||
private _applyKeyframesOnSelectors() {
|
||||
for (let i = this._mergedCssSelectors.length - 1; i >= 0; i--) {
|
||||
let selector = this._mergedCssSelectors[i];
|
||||
if (selector.animations !== undefined) {
|
||||
for (let animation of selector.animations) {
|
||||
let keyframe = this._keyframes[animation.name];
|
||||
if (keyframe !== undefined) {
|
||||
animation.keyframes = cssAnimationParser.CssAnimationParser.keyframesArrayFromCSS(keyframe);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function applyInlineSyle(view: view.View, style: string) {
|
||||
try {
|
||||
var syntaxTree = cssParser.parse("local { " + style + " }", undefined);
|
||||
var filteredDeclarations = syntaxTree.stylesheet.rules[0].declarations.filter((val, i, arr) => { return val.type === "declaration" });
|
||||
let syntaxTree = cssParser.parse("local { " + style + " }", undefined);
|
||||
let filteredDeclarations = syntaxTree.stylesheet.rules[0].declarations.filter((val, i, arr) => { return val.type === "declaration" });
|
||||
cssSelector.applyInlineSyle(view, filteredDeclarations);
|
||||
} catch (ex) {
|
||||
trace.write("Applying local style failed: " + ex, trace.categories.Error, trace.messageType.error);
|
||||
|
11
ui/styling/style.d.ts
vendored
11
ui/styling/style.d.ts
vendored
@ -35,6 +35,11 @@ declare module "ui/styling/style" {
|
||||
}
|
||||
|
||||
export class Style extends DependencyObservable implements styling.Style {
|
||||
public rotate: number;
|
||||
public translateX: number;
|
||||
public translateY: number;
|
||||
public scaleX: number;
|
||||
public scaleY: number;
|
||||
public color: Color;
|
||||
public backgroundColor: Color;
|
||||
public backgroundImage: string;
|
||||
@ -89,6 +94,12 @@ declare module "ui/styling/style" {
|
||||
export function registerNoStylingClass(className);
|
||||
export function getHandler(property: Property, view: View): StylePropertyChangedHandler;
|
||||
// Property registration
|
||||
|
||||
export var rotateProperty: styleProperty.Property;
|
||||
export var translateXProperty: styleProperty.Property;
|
||||
export var translateYProperty: styleProperty.Property;
|
||||
export var scaleXProperty: styleProperty.Property;
|
||||
export var scaleYProperty: styleProperty.Property;
|
||||
export var colorProperty: styleProperty.Property;
|
||||
export var backgroundImageProperty: styleProperty.Property;
|
||||
export var backgroundColorProperty: styleProperty.Property;
|
||||
|
@ -470,6 +470,46 @@ export class Style extends DependencyObservable implements styling.Style {
|
||||
private _updateCounter = 0;
|
||||
private _nativeSetters = new Map<Property, any>();
|
||||
|
||||
get rotate(): number {
|
||||
return this._getValue(rotateProperty);
|
||||
}
|
||||
|
||||
set rotate(value: number) {
|
||||
this._setValue(rotateProperty, value);
|
||||
}
|
||||
|
||||
get scaleX(): number {
|
||||
return this._getValue(scaleXProperty);
|
||||
}
|
||||
|
||||
set scaleX(value: number) {
|
||||
this._setValue(scaleXProperty, value);
|
||||
}
|
||||
|
||||
get scaleY(): number {
|
||||
return this._getValue(scaleYProperty);
|
||||
}
|
||||
|
||||
set scaleY(value: number) {
|
||||
this._setValue(scaleYProperty, value);
|
||||
}
|
||||
|
||||
get translateX(): number {
|
||||
return this._getValue(translateXProperty);
|
||||
}
|
||||
|
||||
set translateX(value: number) {
|
||||
this._setValue(translateXProperty, value);
|
||||
}
|
||||
|
||||
get translateY(): number {
|
||||
return this._getValue(translateYProperty);
|
||||
}
|
||||
|
||||
set translateY(value: number) {
|
||||
this._setValue(translateYProperty, value);
|
||||
}
|
||||
|
||||
get color(): Color {
|
||||
return this._getValue(colorProperty);
|
||||
}
|
||||
@ -934,6 +974,22 @@ export function getHandler(property: Property, view: View): definition.StyleProp
|
||||
}
|
||||
|
||||
// Property registration
|
||||
|
||||
export var rotateProperty = new styleProperty.Property("rotate", "rotate",
|
||||
new PropertyMetadata(undefined, PropertyMetadataSettings.None, null));
|
||||
|
||||
export var scaleXProperty = new styleProperty.Property("scaleX", "scaleX",
|
||||
new PropertyMetadata(undefined, PropertyMetadataSettings.None, null));
|
||||
|
||||
export var scaleYProperty = new styleProperty.Property("scaleY", "scaleY",
|
||||
new PropertyMetadata(undefined, PropertyMetadataSettings.None, null));
|
||||
|
||||
export var translateXProperty = new styleProperty.Property("translateX", "translateX",
|
||||
new PropertyMetadata(undefined, PropertyMetadataSettings.None, null));
|
||||
|
||||
export var translateYProperty = new styleProperty.Property("translateY", "translateY",
|
||||
new PropertyMetadata(undefined, PropertyMetadataSettings.None, null));
|
||||
|
||||
export var colorProperty = new styleProperty.Property("color", "color",
|
||||
new PropertyMetadata(undefined, PropertyMetadataSettings.Inheritable, undefined, Color.isValid, Color.equals),
|
||||
converters.colorConverter);
|
||||
@ -1132,10 +1188,65 @@ function onFontChanged(value: any): Array<styleProperty.KeyValuePair<styleProper
|
||||
return array;
|
||||
}
|
||||
|
||||
function onTransformChanged(value: any): Array<styleProperty.KeyValuePair<styleProperty.Property, any>> {
|
||||
var newTransform = converters.transformConverter(value);
|
||||
var array = new Array<styleProperty.KeyValuePair<styleProperty.Property, any>>();
|
||||
var values = undefined;
|
||||
for (var transform in newTransform) {
|
||||
switch (transform) {
|
||||
case "scaleX":
|
||||
array.push({ property: scaleXProperty, value: parseFloat(newTransform[transform]) });
|
||||
break;
|
||||
case "scaleY":
|
||||
array.push({ property: scaleYProperty, value: parseFloat(newTransform[transform]) });
|
||||
break;
|
||||
case "scale":
|
||||
case "scale3d":
|
||||
values = newTransform[transform].split(",");
|
||||
if (values.length === 2 || values.length === 3) {
|
||||
array.push({ property: scaleXProperty, value: parseFloat(values[0]) });
|
||||
array.push({ property: scaleYProperty, value: parseFloat(values[1]) });
|
||||
}
|
||||
break;
|
||||
case "translateX":
|
||||
array.push({ property: translateXProperty, value: parseFloat(newTransform[transform]) });
|
||||
break;
|
||||
case "translateY":
|
||||
array.push({ property: translateYProperty, value: parseFloat(newTransform[transform]) });
|
||||
break;
|
||||
case "translate":
|
||||
case "translate3d":
|
||||
values = newTransform[transform].split(",");
|
||||
if (values.length === 2 || values.length === 3) {
|
||||
array.push({ property: translateXProperty, value: parseFloat(values[0]) });
|
||||
array.push({ property: translateYProperty, value: parseFloat(values[1]) });
|
||||
}
|
||||
break;
|
||||
case "rotate":
|
||||
let text = newTransform[transform];
|
||||
let val = parseFloat(text);
|
||||
if (text.slice(-3) === "rad") {
|
||||
val = val * (180.0 / Math.PI);
|
||||
}
|
||||
array.push({ property: rotateProperty, value: val });
|
||||
break;
|
||||
case "none":
|
||||
array.push({ property: scaleXProperty, value: 1 });
|
||||
array.push({ property: scaleYProperty, value: 1 });
|
||||
array.push({ property: translateXProperty, value: 0 });
|
||||
array.push({ property: translateYProperty, value: 0 });
|
||||
array.push({ property: rotateProperty, value: 0 });
|
||||
break;
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
// register default shorthand callbacks.
|
||||
styleProperty.registerShorthandCallback("font", onFontChanged);
|
||||
styleProperty.registerShorthandCallback("margin", onMarginChanged);
|
||||
styleProperty.registerShorthandCallback("padding", onPaddingChanged);
|
||||
styleProperty.registerShorthandCallback("transform", onTransformChanged);
|
||||
|
||||
var _defaultNativeValuesCache = {};
|
||||
|
||||
|
@ -1,31 +1,38 @@
|
||||
import viewModule = require("ui/core/view");
|
||||
import observable = require("ui/core/dependency-observable");
|
||||
import styleProperty = require("ui/styling/style-property");
|
||||
import cssSelector = require("ui/styling/css-selector");
|
||||
import * as visualStateConstants from "ui/styling/visual-state-constants";
|
||||
|
||||
export class VisualState {
|
||||
private _setters: {};
|
||||
private _setters: {}; // use css selector instead
|
||||
private _animatedSelectors: cssSelector.CssVisualStateSelector[];
|
||||
|
||||
constructor() {
|
||||
this._setters = {};
|
||||
this._animatedSelectors = [];
|
||||
}
|
||||
|
||||
get setters(): {} {
|
||||
return this._setters;
|
||||
}
|
||||
|
||||
get animatedSelectors(): cssSelector.CssVisualStateSelector[] {
|
||||
return this._animatedSelectors;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export function goToState(view: viewModule.View, state: string): string {
|
||||
var root = <any>view.page;
|
||||
let root = <any>view.page;
|
||||
if (!root) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
//TODO: this of optimization
|
||||
var allStates = root._getStyleScope().getVisualStates(view);
|
||||
let allStates = root._getStyleScope().getVisualStates(view);
|
||||
if (!allStates) {
|
||||
return undefined;
|
||||
}
|
||||
@ -44,8 +51,8 @@ export function goToState(view: viewModule.View, state: string): string {
|
||||
|
||||
// Step 2
|
||||
if (state !== view.visualState) {
|
||||
var newState: VisualState = allStates[state];
|
||||
var oldState: VisualState = allStates[view.visualState];
|
||||
let newState: VisualState = allStates[state];
|
||||
let oldState: VisualState = allStates[view.visualState];
|
||||
|
||||
// Step 3
|
||||
resetProperties(view, oldState, newState);
|
||||
@ -62,9 +69,9 @@ function resetProperties(view: viewModule.View, oldState: VisualState, newState:
|
||||
return;
|
||||
}
|
||||
|
||||
var property: styleProperty.Property;
|
||||
let property: styleProperty.Property;
|
||||
|
||||
for (var name in oldState.setters) {
|
||||
for (let name in oldState.setters) {
|
||||
if (newState && (name in newState.setters)) {
|
||||
// Property will be altered by the new state, no need to reset it.
|
||||
continue;
|
||||
@ -75,6 +82,19 @@ function resetProperties(view: viewModule.View, oldState: VisualState, newState:
|
||||
view.style._resetValue(property, observable.ValueSource.VisualState);
|
||||
}
|
||||
}
|
||||
|
||||
for (let selector of oldState.animatedSelectors) {
|
||||
for (let animationInfo of selector.animations) {
|
||||
for (let keyframe of animationInfo.keyframes) {
|
||||
for (let declaration of keyframe.declarations) {
|
||||
property = styleProperty.getPropertyByName(declaration.property);
|
||||
if (property) {
|
||||
view.style._resetValue(property, observable.ValueSource.VisualState);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function applyProperties(view: viewModule.View, state: VisualState) {
|
||||
@ -82,12 +102,16 @@ function applyProperties(view: viewModule.View, state: VisualState) {
|
||||
return;
|
||||
}
|
||||
|
||||
var property: styleProperty.Property;
|
||||
let property: styleProperty.Property;
|
||||
|
||||
for (var name in state.setters) {
|
||||
for (let name in state.setters) {
|
||||
property = styleProperty.getPropertyByName(name);
|
||||
if (property) {
|
||||
view.style._setValue(property, state.setters[name], observable.ValueSource.VisualState);
|
||||
}
|
||||
}
|
||||
|
||||
for (let selector of state.animatedSelectors) {
|
||||
selector.apply(view, observable.ValueSource.VisualState);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user