Skip to content

Commit e8f5ac8

Browse files
rynopvakrilovdarindslolamdtopuzov
authored andcommitted
feat: Add 3D rotation to view - takeover of PR# 5950 (NativeScript#8136)
* 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 (NativeScript#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 (NativeScript#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>
1 parent 8550c32 commit e8f5ac8

File tree

31 files changed

+708
-191
lines changed

31 files changed

+708
-191
lines changed

api-reports/NativeScript.api.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,8 @@ export interface AnimationDefinition {
243243

244244
opacity?: number;
245245

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

248249
scale?: Pair;
249250

@@ -2086,6 +2087,8 @@ export class Style extends Observable {
20862087
// (undocumented)
20872088
public paddingTop: Length;
20882089
// (undocumented)
2090+
public perspective: number;
2091+
// (undocumented)
20892092
public placeholderColor: Color;
20902093
// Warning: (ae-forgotten-export) The symbol "PropertyBagClass" needs to be exported by the entry point index.d.ts
20912094
public readonly PropertyBag: PropertyBagClass;
@@ -2094,6 +2097,10 @@ export class Style extends Observable {
20942097
// (undocumented)
20952098
public rotate: number;
20962099
// (undocumented)
2100+
public rotateX: number;
2101+
// (undocumented)
2102+
public rotateY: number;
2103+
// (undocumented)
20972104
public scaleX: number;
20982105
// (undocumented)
20992106
public scaleY: number;
@@ -2702,12 +2709,15 @@ export abstract class View extends ViewBase {
27022709
opacity: number;
27032710
originX: number;
27042711
originY: number;
2712+
perspective: number;
27052713
// (undocumented)
27062714
_redrawNativeBackground(value: any): void;
27072715
// (undocumented)
27082716
_removeAnimation(animation: Animation): boolean;
27092717
public static resolveSizeAndState(size: number, specSize: number, specMode: number, childMeasuredState: number): number;
27102718
rotate: number;
2719+
rotateX: number;
2720+
rotateY: number;
27112721
scaleX: number;
27122722
scaleY: number;
27132723
_setCurrentLayoutBounds(left: number, top: number, right: number, bottom: number): { boundsChanged: boolean, sizeChanged: boolean };
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { EventData, Page } from "tns-core-modules/ui/page";
2+
import { View } from "tns-core-modules/ui/core/view";
3+
import { Point3D } from "tns-core-modules/ui/animation/animation";
4+
5+
let view: View;
6+
7+
export function pageLoaded(args: EventData) {
8+
const page = <Page>args.object;
9+
view = page.getViewById<View>("view");
10+
}
11+
12+
export function onAnimateX(args: EventData) {
13+
rotate({ x: 360, y: 0, z: 0 });
14+
}
15+
16+
export function onAnimateY(args: EventData) {
17+
rotate({ x: 0, y: 360, z: 0 });
18+
}
19+
20+
export function onAnimateZ(args: EventData) {
21+
rotate({ x: 0, y: 0, z: 360 });
22+
}
23+
24+
export function onAnimateXYZ(args: EventData) {
25+
rotate({ x: 360, y: 360, z: 360 });
26+
}
27+
28+
async function rotate(rotate: Point3D) {
29+
await view.animate({
30+
rotate,
31+
duration: 1000
32+
});
33+
reset();
34+
}
35+
36+
function reset() {
37+
view.rotate = 0;
38+
view.rotateX = 0;
39+
view.rotateY = 0;
40+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<Page xmlns="http://schemas.nativescript.org/tns.xsd" loaded="pageLoaded">
2+
<ActionBar title="Rotate" />
3+
4+
<GridLayout rows="auto auto auto auto *" columns="* * *">
5+
<Image src="~/res/icon_100x100.png" width="30" height="30" col="0" row="0" rotateX="60"/>
6+
<Image src="~/res/icon_100x100.png" width="30" height="30" col="1" row="0" rotateY="60"/>
7+
<Image src="~/res/icon_100x100.png" width="30" height="30" col="2" row="0" rotate="60"/>
8+
9+
<Button text="X" tap="onAnimateX" col="0" row="1"/>
10+
<Button text="Y" tap="onAnimateY" col="1" row="1"/>
11+
<Button text="Z" tap="onAnimateZ" col="2" row="1"/>
12+
13+
<Image src="~/res/icon_100x100.png" width="60" height="60" horizontalAlignment="center"
14+
colSpan="3" row="2" rotate="60" rotateX="60" rotateY="60"/>
15+
16+
<Button text="XYZ" tap="onAnimateXYZ" row="3" colSpan="3"/>
17+
18+
<AbsoluteLayout width="300" height="300" clipToBounds="true" backgroundColor="LightGray" row="4" colSpan="3">
19+
<Image id="view" src="~/res/icon_100x100.png"
20+
width="100" height="100"
21+
left="100" top="100"/>
22+
</AbsoluteLayout>
23+
</GridLayout>
24+
</Page>
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
.rotate-x {
2+
rotateX: 60;
3+
}
4+
5+
.rotate-y {
6+
rotateY: 60;
7+
}
8+
9+
.rotate-z {
10+
rotate: 60;
11+
}
12+
13+
.original {
14+
transform: none;
15+
}
16+
17+
.animate-x {
18+
animation-name: rotateX;
19+
animation-duration: 2s;
20+
animation-fill-mode: forwards;
21+
}
22+
23+
.animate-y {
24+
animation-name: rotateY;
25+
animation-duration: 2s;
26+
animation-fill-mode: forwards;
27+
}
28+
29+
.animate-z {
30+
animation-name: rotateZ;
31+
animation-duration: 2s;
32+
animation-fill-mode: forwards;
33+
}
34+
35+
.animate-xyz-3d {
36+
animation-name: rotateXYZ3D;
37+
animation-duration: 2s;
38+
animation-fill-mode: forwards;
39+
}
40+
41+
.animate-xyz {
42+
animation-name: rotateXYZ;
43+
animation-duration: 2s;
44+
animation-fill-mode: forwards;
45+
}
46+
47+
@keyframes rotateX {
48+
from { transform: none; }
49+
50% { transform: rotateX(60) }
50+
to { transform: none; }
51+
}
52+
53+
@keyframes rotateY {
54+
from { transform: none; }
55+
50% { transform: rotateY(60) }
56+
to { transform: none; }
57+
}
58+
59+
@keyframes rotateZ {
60+
from { transform: none; }
61+
50% { transform: rotate(60) }
62+
to { transform: none; }
63+
}
64+
65+
@keyframes rotateXYZ3D {
66+
from { transform: none; }
67+
50% { transform: rotate3d(60, 60, 60) }
68+
to { transform: none; }
69+
}
70+
71+
@keyframes rotateXYZ {
72+
from { transform: none; }
73+
50% { transform: rotateX(60) rotateY(60) rotate(60) }
74+
to { transform: none; }
75+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { EventData, Page } from "tns-core-modules/ui/page";
2+
import { View } from "tns-core-modules/ui/core/view";
3+
import { Point3D } from "tns-core-modules/ui/animation/animation";
4+
5+
let view: View;
6+
7+
export function pageLoaded(args: EventData) {
8+
const page = <Page>args.object;
9+
view = page.getViewById<View>("view");
10+
}
11+
12+
export function onAnimateX(args: EventData) {
13+
view.className = "original";
14+
view.className = "animate-x";
15+
}
16+
17+
export function onAnimateY(args: EventData) {
18+
view.className = "original";
19+
view.className = "animate-y";
20+
}
21+
22+
export function onAnimateZ(args: EventData) {
23+
view.className = "original";
24+
view.className = "animate-z";
25+
}
26+
27+
export function onAnimateXYZ3D(args: EventData) {
28+
view.className = "original";
29+
view.className = "animate-xyz-3d";
30+
}
31+
32+
export function onAnimateXYZ(args: EventData) {
33+
view.className = "original";
34+
view.className = "animate-xyz";
35+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<Page xmlns="http://schemas.nativescript.org/tns.xsd" loaded="pageLoaded">
2+
<ActionBar title="Rotate" />
3+
4+
<GridLayout rows="auto auto auto *" columns="* * *">
5+
<Image src="~/res/icon_100x100.png" width="30" height="30" col="0" row="0" class="rotate-x"/>
6+
<Image src="~/res/icon_100x100.png" width="30" height="30" col="1" row="0" class="rotate-y"/>
7+
<Image src="~/res/icon_100x100.png" width="30" height="30" col="2" row="0" class="rotate-z"/>
8+
9+
<Button text="X" tap="onAnimateX" col="0" row="1"/>
10+
<Button text="Y" tap="onAnimateY" col="1" row="1"/>
11+
<Button text="Z" tap="onAnimateZ" col="2" row="1"/>
12+
13+
<Button text="XYZ" tap="onAnimateXYZ" row="2" col="0"/>
14+
<Button text="XYZ-3D" tap="onAnimateXYZ3D" row="2" col="1"/>
15+
16+
<AbsoluteLayout width="300" height="300" clipToBounds="true" backgroundColor="LightGray" row="3" colSpan="3">
17+
<Image id="view" src="~/res/icon_100x100.png"
18+
width="100" height="100"
19+
left="100" top="100" />
20+
</AbsoluteLayout>
21+
</GridLayout>
22+
</Page>

e2e/animation/app/css-animations/page.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ export function pageLoaded(args: EventData) {
1111
export function onButtonTap(args: EventData) {
1212
const clickedButton = <Button>args.object;
1313

14-
const destination = clickedButton.text + "/page";
14+
const destination = "css-animations/" + clickedButton.text + "/page";
1515
currentFrame.navigate(destination);
1616
}

e2e/animation/app/css-animations/page.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<Button text="settings" tap="onButtonTap"/>
1414
<Button text="visual-states" tap="onButtonTap"/>
1515
<Button text="initial-animation" tap="onButtonTap"/>
16+
<Button text="3d-rotate" tap="onButtonTap"/>
1617
</StackLayout>
1718
</ScrollView>
1819
</Page>

e2e/animation/app/home/home-page.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
<Button text="slide-in-effect" tap="onButtonTap" />
1919
<Button text="infinite" tap="onButtonTap" />
2020
<Button text="animation-curves" tap="onButtonTap" />
21-
<Button text="css-animations" tap="onButtonTap" />
21+
<Button text="css-animations" tap="onButtonTap" />
22+
<Button text="3d-rotate" tap="onButtonTap" />
2223
</StackLayout>
2324
</ScrollView>
2425

nativescript-core/matrix/matrix.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@ const TRANSFORM_MATRIXES = {
1616
0, 1, y,
1717
0, 0, 1,
1818
],
19-
"rotate": angleInDeg => {
20-
const angleInRad = degreesToRadians(angleInDeg);
19+
"rotate": ({ x, y, z }) => {
20+
// TODO: Handle rotations over X and Y axis
21+
const radZ = degreesToRadians(z);
22+
const cosZ = Math.cos(radZ);
23+
const sinZ = Math.sin(radZ);
2124

2225
return [
23-
Math.cos(angleInRad), -Math.sin(angleInRad), 0,
24-
Math.sin(angleInRad), Math.cos(angleInRad), 0,
26+
cosZ, -sinZ, 0,
27+
sinZ, cosZ, 0,
2528
0, 0, 1,
2629
];
2730
},
@@ -43,6 +46,7 @@ export function multiplyAffine2d(m1: number[], m2: number[]): number[] {
4346
];
4447
}
4548

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

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

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

6872
rotate = radiansToDegrees(rotate);
6973

70-
return { translate, rotate, scale };
74+
return { translate, rotate: { x: 0, y: 0, z: rotate }, scale };
7175
}
7276

7377
function verifyTransformMatrix(matrix: number[]) {

0 commit comments

Comments
 (0)