Skip to content

Commit

Permalink
feat: Add 3D rotation to view - takeover of PR# 5950 (#8136)
Browse files Browse the repository at this point in the history
* feat: add 3d rotation

* chore: fix build errors

* chore: fix tslint errors

* chore: add @types/chai dev dep

* chore: unused import cleanup

* chore: update tests for x,y rotation

* chore: rebase upstream/master

* fix: iOS Affine Transform test verification

* feat(css): Added optional css-tree parser (#8076)

* feat(css): Added optional css-tree parser

* test: css-tree parser compat tests

* test: more css-tree compat tests

* feat(dialogs): Setting the size of popup dialog thru dialog options (#8041)

* Added iOS specific height and width attributes to ShowModalOptions

* Set the height and width of the popup dialog to the presenting controller

* dialog options ios attributes presentationStyle, height & width are made optional

* Updated NativeScript.api.md for public API changes

* Update with git properties

* Public API

* CLA update

* fix: use iOS native-helper for 3d-rotate

* test: Fix tests using _getTransformMismatchError

* fix: view.__hasTransfrom not set updating properly

* test: fix css-animations test page

Co-authored-by: Alexander Vakrilov <alexander.vakrilov@gmail.com>
Co-authored-by: Darin Dimitrov <darin.dimitrov@gmail.com>
Co-authored-by: Shailesh Lolam <slolam@live.com>
Co-authored-by: Dimitar Topuzov <dtopuzov@gmail.com>
  • Loading branch information
5 people authored and Alexander Vakrilov committed Jan 10, 2020
1 parent 8550c32 commit e8f5ac8
Show file tree
Hide file tree
Showing 31 changed files with 708 additions and 191 deletions.
12 changes: 11 additions & 1 deletion api-reports/NativeScript.api.md
Expand Up @@ -243,7 +243,8 @@ export interface AnimationDefinition {

opacity?: number;

rotate?: number;
// Warning: (ae-forgotten-export) The symbol "Point3D" needs to be exported by the entry point index.d.ts
rotate?: number | Point3D;

scale?: Pair;

Expand Down Expand Up @@ -2086,6 +2087,8 @@ export class Style extends Observable {
// (undocumented)
public paddingTop: Length;
// (undocumented)
public perspective: number;
// (undocumented)
public placeholderColor: Color;
// Warning: (ae-forgotten-export) The symbol "PropertyBagClass" needs to be exported by the entry point index.d.ts
public readonly PropertyBag: PropertyBagClass;
Expand All @@ -2094,6 +2097,10 @@ export class Style extends Observable {
// (undocumented)
public rotate: number;
// (undocumented)
public rotateX: number;
// (undocumented)
public rotateY: number;
// (undocumented)
public scaleX: number;
// (undocumented)
public scaleY: number;
Expand Down Expand Up @@ -2702,12 +2709,15 @@ export abstract class View extends ViewBase {
opacity: number;
originX: number;
originY: number;
perspective: number;
// (undocumented)
_redrawNativeBackground(value: any): void;
// (undocumented)
_removeAnimation(animation: Animation): boolean;
public static resolveSizeAndState(size: number, specSize: number, specMode: number, childMeasuredState: number): number;
rotate: number;
rotateX: number;
rotateY: number;
scaleX: number;
scaleY: number;
_setCurrentLayoutBounds(left: number, top: number, right: number, bottom: number): { boundsChanged: boolean, sizeChanged: boolean };
Expand Down
40 changes: 40 additions & 0 deletions e2e/animation/app/3d-rotate/page.ts
@@ -0,0 +1,40 @@
import { EventData, Page } from "tns-core-modules/ui/page";
import { View } from "tns-core-modules/ui/core/view";
import { Point3D } from "tns-core-modules/ui/animation/animation";

let view: View;

export function pageLoaded(args: EventData) {
const page = <Page>args.object;
view = page.getViewById<View>("view");
}

export function onAnimateX(args: EventData) {
rotate({ x: 360, y: 0, z: 0 });
}

export function onAnimateY(args: EventData) {
rotate({ x: 0, y: 360, z: 0 });
}

export function onAnimateZ(args: EventData) {
rotate({ x: 0, y: 0, z: 360 });
}

export function onAnimateXYZ(args: EventData) {
rotate({ x: 360, y: 360, z: 360 });
}

async function rotate(rotate: Point3D) {
await view.animate({
rotate,
duration: 1000
});
reset();
}

function reset() {
view.rotate = 0;
view.rotateX = 0;
view.rotateY = 0;
}
24 changes: 24 additions & 0 deletions e2e/animation/app/3d-rotate/page.xml
@@ -0,0 +1,24 @@
<Page xmlns="http://schemas.nativescript.org/tns.xsd" loaded="pageLoaded">
<ActionBar title="Rotate" />

<GridLayout rows="auto auto auto auto *" columns="* * *">
<Image src="~/res/icon_100x100.png" width="30" height="30" col="0" row="0" rotateX="60"/>
<Image src="~/res/icon_100x100.png" width="30" height="30" col="1" row="0" rotateY="60"/>
<Image src="~/res/icon_100x100.png" width="30" height="30" col="2" row="0" rotate="60"/>

<Button text="X" tap="onAnimateX" col="0" row="1"/>
<Button text="Y" tap="onAnimateY" col="1" row="1"/>
<Button text="Z" tap="onAnimateZ" col="2" row="1"/>

<Image src="~/res/icon_100x100.png" width="60" height="60" horizontalAlignment="center"
colSpan="3" row="2" rotate="60" rotateX="60" rotateY="60"/>

<Button text="XYZ" tap="onAnimateXYZ" row="3" colSpan="3"/>

<AbsoluteLayout width="300" height="300" clipToBounds="true" backgroundColor="LightGray" row="4" colSpan="3">
<Image id="view" src="~/res/icon_100x100.png"
width="100" height="100"
left="100" top="100"/>
</AbsoluteLayout>
</GridLayout>
</Page>
75 changes: 75 additions & 0 deletions e2e/animation/app/css-animations/3d-rotate/page.css
@@ -0,0 +1,75 @@
.rotate-x {
rotateX: 60;
}

.rotate-y {
rotateY: 60;
}

.rotate-z {
rotate: 60;
}

.original {
transform: none;
}

.animate-x {
animation-name: rotateX;
animation-duration: 2s;
animation-fill-mode: forwards;
}

.animate-y {
animation-name: rotateY;
animation-duration: 2s;
animation-fill-mode: forwards;
}

.animate-z {
animation-name: rotateZ;
animation-duration: 2s;
animation-fill-mode: forwards;
}

.animate-xyz-3d {
animation-name: rotateXYZ3D;
animation-duration: 2s;
animation-fill-mode: forwards;
}

.animate-xyz {
animation-name: rotateXYZ;
animation-duration: 2s;
animation-fill-mode: forwards;
}

@keyframes rotateX {
from { transform: none; }
50% { transform: rotateX(60) }
to { transform: none; }
}

@keyframes rotateY {
from { transform: none; }
50% { transform: rotateY(60) }
to { transform: none; }
}

@keyframes rotateZ {
from { transform: none; }
50% { transform: rotate(60) }
to { transform: none; }
}

@keyframes rotateXYZ3D {
from { transform: none; }
50% { transform: rotate3d(60, 60, 60) }
to { transform: none; }
}

@keyframes rotateXYZ {
from { transform: none; }
50% { transform: rotateX(60) rotateY(60) rotate(60) }
to { transform: none; }
}
35 changes: 35 additions & 0 deletions e2e/animation/app/css-animations/3d-rotate/page.ts
@@ -0,0 +1,35 @@
import { EventData, Page } from "tns-core-modules/ui/page";
import { View } from "tns-core-modules/ui/core/view";
import { Point3D } from "tns-core-modules/ui/animation/animation";

let view: View;

export function pageLoaded(args: EventData) {
const page = <Page>args.object;
view = page.getViewById<View>("view");
}

export function onAnimateX(args: EventData) {
view.className = "original";
view.className = "animate-x";
}

export function onAnimateY(args: EventData) {
view.className = "original";
view.className = "animate-y";
}

export function onAnimateZ(args: EventData) {
view.className = "original";
view.className = "animate-z";
}

export function onAnimateXYZ3D(args: EventData) {
view.className = "original";
view.className = "animate-xyz-3d";
}

export function onAnimateXYZ(args: EventData) {
view.className = "original";
view.className = "animate-xyz";
}
22 changes: 22 additions & 0 deletions e2e/animation/app/css-animations/3d-rotate/page.xml
@@ -0,0 +1,22 @@
<Page xmlns="http://schemas.nativescript.org/tns.xsd" loaded="pageLoaded">
<ActionBar title="Rotate" />

<GridLayout rows="auto auto auto *" columns="* * *">
<Image src="~/res/icon_100x100.png" width="30" height="30" col="0" row="0" class="rotate-x"/>
<Image src="~/res/icon_100x100.png" width="30" height="30" col="1" row="0" class="rotate-y"/>
<Image src="~/res/icon_100x100.png" width="30" height="30" col="2" row="0" class="rotate-z"/>

<Button text="X" tap="onAnimateX" col="0" row="1"/>
<Button text="Y" tap="onAnimateY" col="1" row="1"/>
<Button text="Z" tap="onAnimateZ" col="2" row="1"/>

<Button text="XYZ" tap="onAnimateXYZ" row="2" col="0"/>
<Button text="XYZ-3D" tap="onAnimateXYZ3D" row="2" col="1"/>

<AbsoluteLayout width="300" height="300" clipToBounds="true" backgroundColor="LightGray" row="3" colSpan="3">
<Image id="view" src="~/res/icon_100x100.png"
width="100" height="100"
left="100" top="100" />
</AbsoluteLayout>
</GridLayout>
</Page>
2 changes: 1 addition & 1 deletion e2e/animation/app/css-animations/page.ts
Expand Up @@ -11,6 +11,6 @@ export function pageLoaded(args: EventData) {
export function onButtonTap(args: EventData) {
const clickedButton = <Button>args.object;

const destination = clickedButton.text + "/page";
const destination = "css-animations/" + clickedButton.text + "/page";
currentFrame.navigate(destination);
}
1 change: 1 addition & 0 deletions e2e/animation/app/css-animations/page.xml
Expand Up @@ -13,6 +13,7 @@
<Button text="settings" tap="onButtonTap"/>
<Button text="visual-states" tap="onButtonTap"/>
<Button text="initial-animation" tap="onButtonTap"/>
<Button text="3d-rotate" tap="onButtonTap"/>
</StackLayout>
</ScrollView>
</Page>
3 changes: 2 additions & 1 deletion e2e/animation/app/home/home-page.xml
Expand Up @@ -18,7 +18,8 @@
<Button text="slide-in-effect" tap="onButtonTap" />
<Button text="infinite" tap="onButtonTap" />
<Button text="animation-curves" tap="onButtonTap" />
<Button text="css-animations" tap="onButtonTap" />
<Button text="css-animations" tap="onButtonTap" />
<Button text="3d-rotate" tap="onButtonTap" />
</StackLayout>
</ScrollView>

Expand Down
16 changes: 10 additions & 6 deletions nativescript-core/matrix/matrix.ts
Expand Up @@ -16,12 +16,15 @@ const TRANSFORM_MATRIXES = {
0, 1, y,
0, 0, 1,
],
"rotate": angleInDeg => {
const angleInRad = degreesToRadians(angleInDeg);
"rotate": ({ x, y, z }) => {
// TODO: Handle rotations over X and Y axis
const radZ = degreesToRadians(z);
const cosZ = Math.cos(radZ);
const sinZ = Math.sin(radZ);

return [
Math.cos(angleInRad), -Math.sin(angleInRad), 0,
Math.sin(angleInRad), Math.cos(angleInRad), 0,
cosZ, -sinZ, 0,
sinZ, cosZ, 0,
0, 0, 1,
];
},
Expand All @@ -43,6 +46,7 @@ export function multiplyAffine2d(m1: number[], m2: number[]): number[] {
];
}

// TODO: Decompose rotations over X and Y axis
export function decompose2DTransformMatrix(matrix: number[])
: TransformFunctionsInfo {

Expand All @@ -52,7 +56,7 @@ export function decompose2DTransformMatrix(matrix: number[])
const determinant = A * D - B * C;
const translate = { x: E || 0, y: F || 0 };

// rewrite with obj desctructuring using the identity matrix
// rewrite with obj destructuring using the identity matrix
let rotate = 0;
let scale = { x: 1, y: 1 };
if (A || B) {
Expand All @@ -67,7 +71,7 @@ export function decompose2DTransformMatrix(matrix: number[])

rotate = radiansToDegrees(rotate);

return { translate, rotate, scale };
return { translate, rotate: { x: 0, y: 0, z: rotate }, scale };
}

function verifyTransformMatrix(matrix: number[]) {
Expand Down
3 changes: 2 additions & 1 deletion nativescript-core/package.json
Expand Up @@ -26,6 +26,7 @@
"tslib": "1.10.0"
},
"devDependencies": {
"@types/chai": "~4.2.5",
"@types/node": "~10.12.18",
"tns-platform-declarations": "next"
},
Expand Down Expand Up @@ -58,4 +59,4 @@
}
}
}
}
}

0 comments on commit e8f5ac8

Please sign in to comment.