UNPKG

12.9 kBJavaScriptView Raw
1import { isNumber } from '../../utils/types';
2import { Transition } from '.';
3import { getRectFromProps, SharedTransition, SharedTransitionAnimationType } from './shared-transition';
4import { ImageSource } from '../../image-source';
5import { ContentView } from '../content-view';
6import { GridLayout } from '../layouts/grid-layout';
7import { Screen } from '../../platform';
8import { android as AndroidUtils } from '../../utils/native-helper';
9var SnapshotViewGroup = /** @class */ (function (_super) {
10 __extends(SnapshotViewGroup, _super);
11 function SnapshotViewGroup(context) {
12 var _this = _super.call(this, context) || this;
13 return global.__native(_this);
14 }
15 SnapshotViewGroup.prototype.onMeasure = function () {
16 this.setMeasuredDimension(0, 0);
17 };
18 SnapshotViewGroup.prototype.onLayout = function () {
19 //
20 };
21 return SnapshotViewGroup;
22}(android.view.ViewGroup));
23class ContentViewSnapshot extends ContentView {
24 createNativeView() {
25 return new SnapshotViewGroup(this._context);
26 }
27}
28var CustomSpringInterpolator = /** @class */ (function (_super) {
29 __extends(CustomSpringInterpolator, _super);
30 function CustomSpringInterpolator() {
31 return _super !== null && _super.apply(this, arguments) || this;
32 }
33 CustomSpringInterpolator.prototype.getInterpolation = function (input) {
34 // Note: we speed up the interpolation by 10% to fix the issue with the transition not being finished
35 // and the views shifting from their intended final position...
36 // this is really just a workaround and should be fixed properly once we
37 // can figure out the root cause of the issue.
38 var res = _super.prototype.getInterpolation.call(this, input) * 1.1;
39 if (res > 1) {
40 return float(1);
41 }
42 return float(res);
43 };
44 return CustomSpringInterpolator;
45}(android.view.animation.AnticipateOvershootInterpolator));
46var CustomLinearInterpolator = /** @class */ (function (_super) {
47 __extends(CustomLinearInterpolator, _super);
48 function CustomLinearInterpolator() {
49 return _super !== null && _super.apply(this, arguments) || this;
50 }
51 CustomLinearInterpolator.prototype.getInterpolation = function (input) {
52 // Note: we speed up the interpolation by 10% to fix the issue with the transition not being finished
53 // and the views shifting from their intended final position...
54 // this is really just a workaround and should be fixed properly once we
55 // can figure out the root cause of the issue.
56 var res = _super.prototype.getInterpolation.call(this, input) * 1.1;
57 if (res > 1) {
58 return float(1);
59 }
60 return float(res);
61 };
62 return CustomLinearInterpolator;
63}(android.view.animation.LinearInterpolator));
64function setTransitionName(view) {
65 if (!view?.sharedTransitionTag) {
66 return;
67 }
68 try {
69 androidx.core.view.ViewCompat.setTransitionName(view.nativeView, view.sharedTransitionTag);
70 }
71 catch (err) {
72 // ignore
73 }
74}
75export class PageTransition extends Transition {
76 constructor(duration, curve, pageLoadedTimeout = 0) {
77 // disable custom curves until we can fix the issue with the animation not completing
78 if (curve) {
79 console.warn('PageTransition does not support custom curves at the moment. The passed in curve will be ignored.');
80 }
81 if (typeof duration !== 'number') {
82 duration = 500;
83 }
84 super(duration);
85 this.pageLoadedTimeout = pageLoadedTimeout;
86 }
87 createAndroidAnimator(transitionType) {
88 const state = SharedTransition.getState(this.id);
89 const pageStart = state.pageStart;
90 const startFrame = getRectFromProps(pageStart, {
91 x: 0,
92 y: 0,
93 width: Screen.mainScreen.widthPixels,
94 height: Screen.mainScreen.heightPixels,
95 });
96 const pageEnd = state.pageEnd;
97 const endFrame = getRectFromProps(pageEnd);
98 const pageReturn = state.pageReturn;
99 const returnFrame = getRectFromProps(pageReturn);
100 let customDuration = -1;
101 if (state.activeType === SharedTransitionAnimationType.present && isNumber(pageEnd?.duration)) {
102 customDuration = pageEnd.duration;
103 }
104 else if (isNumber(state.pageReturn?.duration)) {
105 customDuration = state.pageReturn.duration;
106 }
107 const animationSet = new android.animation.AnimatorSet();
108 animationSet.setDuration(customDuration > -1 ? customDuration : this.getDuration());
109 const alphaValues = Array.create('float', 2);
110 const translationXValues = Array.create('float', 2);
111 const translationYValues = Array.create('float', 2);
112 switch (transitionType) {
113 case Transition.AndroidTransitionType.enter:
114 // incoming page (to)
115 alphaValues[0] = isNumber(pageStart?.opacity) ? pageStart?.opacity : 0;
116 alphaValues[1] = isNumber(pageEnd?.opacity) ? pageEnd?.opacity : 1;
117 translationYValues[0] = startFrame.y;
118 translationYValues[1] = endFrame.y;
119 translationXValues[0] = startFrame.x;
120 translationXValues[1] = endFrame.x;
121 break;
122 case Transition.AndroidTransitionType.exit:
123 // current page (from)
124 alphaValues[0] = 1;
125 alphaValues[1] = 0;
126 translationYValues[0] = 0;
127 translationYValues[1] = 0;
128 break;
129 case Transition.AndroidTransitionType.popEnter:
130 // current page (returning to)
131 alphaValues[0] = 0;
132 alphaValues[1] = 1;
133 break;
134 case Transition.AndroidTransitionType.popExit:
135 // removing page (to)
136 alphaValues[0] = isNumber(pageEnd?.opacity) ? pageEnd?.opacity : 1;
137 alphaValues[1] = isNumber(pageStart?.opacity) ? pageStart?.opacity : 0;
138 translationYValues[0] = endFrame.y;
139 translationYValues[1] = startFrame.y;
140 translationXValues[0] = endFrame.x;
141 translationXValues[1] = startFrame.x;
142 break;
143 }
144 const properties = {
145 alpha: alphaValues,
146 translationX: translationXValues,
147 translationY: translationYValues,
148 };
149 const animations = new java.util.HashSet();
150 for (const prop in properties) {
151 // console.log(prop, ' ', properties[prop][1]);
152 const animator = android.animation.ObjectAnimator.ofFloat(null, prop, properties[prop]);
153 if (customDuration) {
154 // duration always overrides default spring
155 animator.setInterpolator(new CustomLinearInterpolator());
156 }
157 else {
158 animator.setInterpolator(new CustomSpringInterpolator());
159 }
160 animations.add(animator);
161 }
162 animationSet.playTogether(animations);
163 return animationSet;
164 }
165 androidFragmentTransactionCallback(fragmentTransaction, currentEntry, newEntry) {
166 const fromPage = currentEntry.resolvedPage;
167 const toPage = newEntry.resolvedPage;
168 const newFragment = newEntry.fragment;
169 const state = SharedTransition.getState(this.id);
170 if (!state) {
171 // when navigating transition is set on the currentEntry but never cleaned up
172 // which means that on a next navigation forward (without transition) and back
173 // we will go here with an empty state!
174 currentEntry.transition = null;
175 return;
176 }
177 const pageEnd = state.pageEnd;
178 //we can't look for presented right now as the toPage might not be loaded
179 // and thus some views like ListView/Pager... might not have loaded their "children"
180 // presented will be handled in loaded event of toPage
181 const { presenting } = SharedTransition.getSharedElements(fromPage, toPage);
182 // Note: we can enhance android more over time with element targeting across different screens
183 // const pageStart = state.pageStart;
184 // const pageEndIndependentTags = Object.keys(pageEnd?.sharedTransitionTags || {});
185 // console.log('pageEndIndependentTags:', pageEndIndependentTags);
186 // for (const tag of pageEndIndependentTags) {
187 // // only consider start when there's a matching end
188 // const pageStartIndependentProps = pageStart?.sharedTransitionTags[tag];
189 // if (pageStartIndependentProps) {
190 // console.log('pageStartIndependentProps:', tag, pageStartIndependentProps);
191 // }
192 // const pageEndIndependentProps = pageEnd?.sharedTransitionTags[tag];
193 // let independentView = presenting.find((v) => v.sharedTransitionTag === tag);
194 // let isPresented = false;
195 // if (!independentView) {
196 // independentView = presented.find((v) => v.sharedTransitionTag === tag);
197 // if (!independentView) {
198 // break;
199 // }
200 // isPresented = true;
201 // }
202 // if (independentView) {
203 // console.log('independentView:', independentView);
204 // const imageSource = renderToImageSource(independentView);
205 // const image = new Image();
206 // image.src = imageSource;
207 // const { hostView } = loadViewInBackground(image);
208 // (<any>fromPage).addChild(hostView);
209 // independentView.opacity = 0;
210 // }
211 // }
212 const onPageLoaded = () => {
213 // add a timeout so that Views like ListView / CollectionView can have their children instantiated
214 setTimeout(() => {
215 const { presented } = SharedTransition.getSharedElements(fromPage, toPage);
216 // const sharedElementTags = sharedElements.map((v) => v.sharedTransitionTag);
217 presented.forEach(setTransitionName);
218 newFragment.startPostponedEnterTransition();
219 }, this.pageLoadedTimeout);
220 };
221 fragmentTransaction.setReorderingAllowed(true);
222 let customDuration = -1;
223 if (state.activeType === SharedTransitionAnimationType.present && isNumber(pageEnd?.duration)) {
224 customDuration = pageEnd.duration;
225 }
226 else if (isNumber(state.pageReturn?.duration)) {
227 customDuration = state.pageReturn.duration;
228 }
229 const transitionSet = new androidx.transition.TransitionSet();
230 transitionSet.setDuration(customDuration > -1 ? customDuration : this.getDuration());
231 transitionSet.addTransition(new androidx.transition.ChangeBounds());
232 transitionSet.addTransition(new androidx.transition.ChangeTransform());
233 transitionSet.setOrdering(androidx.transition.TransitionSet.ORDERING_TOGETHER);
234 if (customDuration) {
235 // duration always overrides default spring
236 transitionSet.setInterpolator(new CustomLinearInterpolator());
237 }
238 else {
239 transitionSet.setInterpolator(new CustomSpringInterpolator());
240 }
241 // postpone enter until we call "loaded" on the new page
242 newFragment.postponeEnterTransition();
243 newFragment.setSharedElementEnterTransition(transitionSet);
244 newFragment.setSharedElementReturnTransition(transitionSet);
245 presenting.forEach((v) => {
246 setTransitionName(v);
247 fragmentTransaction.addSharedElement(v.nativeView, v.sharedTransitionTag);
248 });
249 if (toPage.isLoaded) {
250 onPageLoaded();
251 }
252 else {
253 toPage.once('loaded', onPageLoaded);
254 }
255 }
256}
257function renderToImageSource(hostView) {
258 const bitmap = android.graphics.Bitmap.createBitmap(hostView.android.getWidth(), hostView.android.getHeight(), android.graphics.Bitmap.Config.ARGB_8888);
259 const canvas = new android.graphics.Canvas(bitmap);
260 // ensure we start with a blank transparent canvas
261 canvas.drawARGB(0, 0, 0, 0);
262 hostView.android.draw(canvas);
263 return new ImageSource(bitmap);
264}
265function loadViewInBackground(view) {
266 const hiddenHost = new ContentViewSnapshot();
267 const hostView = new GridLayout(); // use a host view to ensure margins are respected
268 hiddenHost.content = hostView;
269 hiddenHost.visibility = 'collapse';
270 hostView.addChild(view);
271 hiddenHost._setupAsRootView(AndroidUtils.getApplicationContext());
272 hiddenHost.callLoaded();
273 AndroidUtils.getCurrentActivity().addContentView(hiddenHost.android, new android.view.ViewGroup.LayoutParams(0, 0));
274 return {
275 hiddenHost,
276 hostView,
277 };
278}
279//# sourceMappingURL=page-transition.android.js.map
\No newline at end of file