@nativescript/core
Version:
A JavaScript library providing an easy to use api for interacting with iOS and Android platform APIs.
279 lines • 12.9 kB
JavaScript
import { isNumber } from '../../utils/types';
import { Transition } from '.';
import { getRectFromProps, SharedTransition, SharedTransitionAnimationType } from './shared-transition';
import { ImageSource } from '../../image-source';
import { ContentView } from '../content-view';
import { GridLayout } from '../layouts/grid-layout';
import { Screen } from '../../platform';
import { android as AndroidUtils } from '../../utils/native-helper';
var SnapshotViewGroup = /** @class */ (function (_super) {
__extends(SnapshotViewGroup, _super);
function SnapshotViewGroup(context) {
var _this = _super.call(this, context) || this;
return global.__native(_this);
}
SnapshotViewGroup.prototype.onMeasure = function () {
this.setMeasuredDimension(0, 0);
};
SnapshotViewGroup.prototype.onLayout = function () {
//
};
return SnapshotViewGroup;
}(android.view.ViewGroup));
class ContentViewSnapshot extends ContentView {
createNativeView() {
return new SnapshotViewGroup(this._context);
}
}
var CustomSpringInterpolator = /** @class */ (function (_super) {
__extends(CustomSpringInterpolator, _super);
function CustomSpringInterpolator() {
return _super !== null && _super.apply(this, arguments) || this;
}
CustomSpringInterpolator.prototype.getInterpolation = function (input) {
// Note: we speed up the interpolation by 10% to fix the issue with the transition not being finished
// and the views shifting from their intended final position...
// this is really just a workaround and should be fixed properly once we
// can figure out the root cause of the issue.
var res = _super.prototype.getInterpolation.call(this, input) * 1.1;
if (res > 1) {
return float(1);
}
return float(res);
};
return CustomSpringInterpolator;
}(android.view.animation.AnticipateOvershootInterpolator));
var CustomLinearInterpolator = /** @class */ (function (_super) {
__extends(CustomLinearInterpolator, _super);
function CustomLinearInterpolator() {
return _super !== null && _super.apply(this, arguments) || this;
}
CustomLinearInterpolator.prototype.getInterpolation = function (input) {
// Note: we speed up the interpolation by 10% to fix the issue with the transition not being finished
// and the views shifting from their intended final position...
// this is really just a workaround and should be fixed properly once we
// can figure out the root cause of the issue.
var res = _super.prototype.getInterpolation.call(this, input) * 1.1;
if (res > 1) {
return float(1);
}
return float(res);
};
return CustomLinearInterpolator;
}(android.view.animation.LinearInterpolator));
function setTransitionName(view) {
if (!view?.sharedTransitionTag) {
return;
}
try {
androidx.core.view.ViewCompat.setTransitionName(view.nativeView, view.sharedTransitionTag);
}
catch (err) {
// ignore
}
}
export class PageTransition extends Transition {
constructor(duration, curve, pageLoadedTimeout = 0) {
// disable custom curves until we can fix the issue with the animation not completing
if (curve) {
console.warn('PageTransition does not support custom curves at the moment. The passed in curve will be ignored.');
}
if (typeof duration !== 'number') {
duration = 500;
}
super(duration);
this.pageLoadedTimeout = pageLoadedTimeout;
}
createAndroidAnimator(transitionType) {
const state = SharedTransition.getState(this.id);
const pageStart = state.pageStart;
const startFrame = getRectFromProps(pageStart, {
x: 0,
y: 0,
width: Screen.mainScreen.widthPixels,
height: Screen.mainScreen.heightPixels,
});
const pageEnd = state.pageEnd;
const endFrame = getRectFromProps(pageEnd);
const pageReturn = state.pageReturn;
const returnFrame = getRectFromProps(pageReturn);
let customDuration = -1;
if (state.activeType === SharedTransitionAnimationType.present && isNumber(pageEnd?.duration)) {
customDuration = pageEnd.duration;
}
else if (isNumber(state.pageReturn?.duration)) {
customDuration = state.pageReturn.duration;
}
const animationSet = new android.animation.AnimatorSet();
animationSet.setDuration(customDuration > -1 ? customDuration : this.getDuration());
const alphaValues = Array.create('float', 2);
const translationXValues = Array.create('float', 2);
const translationYValues = Array.create('float', 2);
switch (transitionType) {
case Transition.AndroidTransitionType.enter:
// incoming page (to)
alphaValues[0] = isNumber(pageStart?.opacity) ? pageStart?.opacity : 0;
alphaValues[1] = isNumber(pageEnd?.opacity) ? pageEnd?.opacity : 1;
translationYValues[0] = startFrame.y;
translationYValues[1] = endFrame.y;
translationXValues[0] = startFrame.x;
translationXValues[1] = endFrame.x;
break;
case Transition.AndroidTransitionType.exit:
// current page (from)
alphaValues[0] = 1;
alphaValues[1] = 0;
translationYValues[0] = 0;
translationYValues[1] = 0;
break;
case Transition.AndroidTransitionType.popEnter:
// current page (returning to)
alphaValues[0] = 0;
alphaValues[1] = 1;
break;
case Transition.AndroidTransitionType.popExit:
// removing page (to)
alphaValues[0] = isNumber(pageEnd?.opacity) ? pageEnd?.opacity : 1;
alphaValues[1] = isNumber(pageStart?.opacity) ? pageStart?.opacity : 0;
translationYValues[0] = endFrame.y;
translationYValues[1] = startFrame.y;
translationXValues[0] = endFrame.x;
translationXValues[1] = startFrame.x;
break;
}
const properties = {
alpha: alphaValues,
translationX: translationXValues,
translationY: translationYValues,
};
const animations = new java.util.HashSet();
for (const prop in properties) {
// console.log(prop, ' ', properties[prop][1]);
const animator = android.animation.ObjectAnimator.ofFloat(null, prop, properties[prop]);
if (customDuration) {
// duration always overrides default spring
animator.setInterpolator(new CustomLinearInterpolator());
}
else {
animator.setInterpolator(new CustomSpringInterpolator());
}
animations.add(animator);
}
animationSet.playTogether(animations);
return animationSet;
}
androidFragmentTransactionCallback(fragmentTransaction, currentEntry, newEntry) {
const fromPage = currentEntry.resolvedPage;
const toPage = newEntry.resolvedPage;
const newFragment = newEntry.fragment;
const state = SharedTransition.getState(this.id);
if (!state) {
// when navigating transition is set on the currentEntry but never cleaned up
// which means that on a next navigation forward (without transition) and back
// we will go here with an empty state!
currentEntry.transition = null;
return;
}
const pageEnd = state.pageEnd;
//we can't look for presented right now as the toPage might not be loaded
// and thus some views like ListView/Pager... might not have loaded their "children"
// presented will be handled in loaded event of toPage
const { presenting } = SharedTransition.getSharedElements(fromPage, toPage);
// Note: we can enhance android more over time with element targeting across different screens
// const pageStart = state.pageStart;
// const pageEndIndependentTags = Object.keys(pageEnd?.sharedTransitionTags || {});
// console.log('pageEndIndependentTags:', pageEndIndependentTags);
// for (const tag of pageEndIndependentTags) {
// // only consider start when there's a matching end
// const pageStartIndependentProps = pageStart?.sharedTransitionTags[tag];
// if (pageStartIndependentProps) {
// console.log('pageStartIndependentProps:', tag, pageStartIndependentProps);
// }
// const pageEndIndependentProps = pageEnd?.sharedTransitionTags[tag];
// let independentView = presenting.find((v) => v.sharedTransitionTag === tag);
// let isPresented = false;
// if (!independentView) {
// independentView = presented.find((v) => v.sharedTransitionTag === tag);
// if (!independentView) {
// break;
// }
// isPresented = true;
// }
// if (independentView) {
// console.log('independentView:', independentView);
// const imageSource = renderToImageSource(independentView);
// const image = new Image();
// image.src = imageSource;
// const { hostView } = loadViewInBackground(image);
// (<any>fromPage).addChild(hostView);
// independentView.opacity = 0;
// }
// }
const onPageLoaded = () => {
// add a timeout so that Views like ListView / CollectionView can have their children instantiated
setTimeout(() => {
const { presented } = SharedTransition.getSharedElements(fromPage, toPage);
// const sharedElementTags = sharedElements.map((v) => v.sharedTransitionTag);
presented.forEach(setTransitionName);
newFragment.startPostponedEnterTransition();
}, this.pageLoadedTimeout);
};
fragmentTransaction.setReorderingAllowed(true);
let customDuration = -1;
if (state.activeType === SharedTransitionAnimationType.present && isNumber(pageEnd?.duration)) {
customDuration = pageEnd.duration;
}
else if (isNumber(state.pageReturn?.duration)) {
customDuration = state.pageReturn.duration;
}
const transitionSet = new androidx.transition.TransitionSet();
transitionSet.setDuration(customDuration > -1 ? customDuration : this.getDuration());
transitionSet.addTransition(new androidx.transition.ChangeBounds());
transitionSet.addTransition(new androidx.transition.ChangeTransform());
transitionSet.setOrdering(androidx.transition.TransitionSet.ORDERING_TOGETHER);
if (customDuration) {
// duration always overrides default spring
transitionSet.setInterpolator(new CustomLinearInterpolator());
}
else {
transitionSet.setInterpolator(new CustomSpringInterpolator());
}
// postpone enter until we call "loaded" on the new page
newFragment.postponeEnterTransition();
newFragment.setSharedElementEnterTransition(transitionSet);
newFragment.setSharedElementReturnTransition(transitionSet);
presenting.forEach((v) => {
setTransitionName(v);
fragmentTransaction.addSharedElement(v.nativeView, v.sharedTransitionTag);
});
if (toPage.isLoaded) {
onPageLoaded();
}
else {
toPage.once('loaded', onPageLoaded);
}
}
}
function renderToImageSource(hostView) {
const bitmap = android.graphics.Bitmap.createBitmap(hostView.android.getWidth(), hostView.android.getHeight(), android.graphics.Bitmap.Config.ARGB_8888);
const canvas = new android.graphics.Canvas(bitmap);
// ensure we start with a blank transparent canvas
canvas.drawARGB(0, 0, 0, 0);
hostView.android.draw(canvas);
return new ImageSource(bitmap);
}
function loadViewInBackground(view) {
const hiddenHost = new ContentViewSnapshot();
const hostView = new GridLayout(); // use a host view to ensure margins are respected
hiddenHost.content = hostView;
hiddenHost.visibility = 'collapse';
hostView.addChild(view);
hiddenHost._setupAsRootView(AndroidUtils.getApplicationContext());
hiddenHost.callLoaded();
AndroidUtils.getCurrentActivity().addContentView(hiddenHost.android, new android.view.ViewGroup.LayoutParams(0, 0));
return {
hiddenHost,
hostView,
};
}
//# sourceMappingURL=page-transition.android.js.map