Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android hardware artifacting fix #38

4 changes: 4 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ module.exports = {
'node_modules/(?!(.*-)?react(.*-)?(native)(-.*)?)',
'node_modules/core-js'
],
setupFiles: [
'<rootDir>/node_modules/react-native/jest/setup.js',
'<rootDir>/jestSetup.js'
],
collectCoverage: true,
coverageReporters: ['lcov', 'text', 'html'],
collectCoverageFrom: [
Expand Down
27 changes: 27 additions & 0 deletions jestSetup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// @flow

jest.mock('react-native', () => {
const ReactNative = jest.requireActual('react-native');
const { Platform } = ReactNative;

jest.spyOn(Platform, 'select');
const MockPlatform = {
...Platform,
OS: 'ios',
};
Platform.select.mockImplementation(specifics => {
const { OS } = MockPlatform
if (OS in specifics) {
return specifics[OS];
} else if ('default' in specifics) {
return specifics.default;
}
return undefined;
})

return Object.setPrototypeOf({
Platform: MockPlatform,
}, ReactNative);
});

jest.mock('react-native/Libraries/Animated/src/NativeAnimatedHelper');
42 changes: 36 additions & 6 deletions src/__tests__/index.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
// @flow

import * as React from 'react';
import { Animated, Platform } from 'react-native';
import renderer from 'react-test-renderer';

import ConfettiCannon, {DEFAULT_EXPLOSION_SPEED, DEFAULT_FALL_SPEED} from '..';

describe('index', () => {
beforeEach(() => {
jest.useFakeTimers();
Platform.OS = 'ios';
});

it('should trigger animations callbacks', () => {
Expand Down Expand Up @@ -64,7 +66,7 @@ describe('index', () => {
expect(handleAnimationEnd).toHaveBeenCalledTimes(1);
});

it('should not start is autoStart is disabled', () => {
it('should not start if autoStart is disabled', () => {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

const handleAnimationStart = jest.fn();

renderer.create(
Expand Down Expand Up @@ -108,7 +110,7 @@ describe('index', () => {
const handleAnimationResume = jest.fn();
const handleAnimationStop = jest.fn();
const handleAnimationEnd = jest.fn();
const ref = jest.fn();
const ref = jest.fn<[ConfettiCannon | null], void>();

renderer.create(
<ConfettiCannon
Expand All @@ -119,28 +121,27 @@ describe('index', () => {
onAnimationResume={handleAnimationResume}
onAnimationStop={handleAnimationStop}
onAnimationEnd={handleAnimationEnd}
// $FlowFixMe this is a mock
ref={ref}
/>
);

const [confettiCannon] = ref.mock.calls[0];

confettiCannon.start();
confettiCannon && confettiCannon.start();

expect(handleAnimationStart).toHaveBeenCalledTimes(1);
expect(handleAnimationResume).toHaveBeenCalledTimes(0);
expect(handleAnimationStop).toHaveBeenCalledTimes(0);
expect(handleAnimationEnd).toHaveBeenCalledTimes(0);

confettiCannon.stop();
confettiCannon && confettiCannon.stop();

expect(handleAnimationStart).toHaveBeenCalledTimes(1);
expect(handleAnimationResume).toHaveBeenCalledTimes(0);
expect(handleAnimationStop).toHaveBeenCalledTimes(1);
expect(handleAnimationEnd).toHaveBeenCalledTimes(0);

confettiCannon.resume();
confettiCannon && confettiCannon.resume();

expect(handleAnimationStart).toHaveBeenCalledTimes(1);
expect(handleAnimationResume).toHaveBeenCalledTimes(1);
Expand Down Expand Up @@ -230,4 +231,33 @@ describe('index', () => {

expect(confettis1).toEqual(confettis2);
});

it('should include the perspective transform on the Android platform', () => {
Platform.OS = 'android';

const origin = {x: -10, y: 0};
const count = 1000;

const component = renderer.create(
<ConfettiCannon count={count} origin={origin} />
);
const confetti = component.root.find(el => el.props.testID === 'confetti-1');

expect(confetti.props.transform).toEqual(expect.arrayContaining([{ perspective: 100 }]));
});

it('should set "renderToHardwareTextureAndroid" prop to true for confetti animated view', () => {
const origin = {x: -10, y: 0};
const count = 1000;

const component = renderer.create(
<ConfettiCannon count={count} origin={origin} />
);

const confettiAnimatedView = component.root
.find(el => el.props.testID === 'confetti-1')
.findByType(Animated.View);

expect(confettiAnimatedView.props.renderToHardwareTextureAndroid).toEqual(true);
});
});
8 changes: 6 additions & 2 deletions src/components/confetti.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ type Interpolations = Array<{
translateY?: Animated.Interpolation,
rotate?: Animated.Interpolation,
rotateX?: Animated.Interpolation,
rotateY?: Animated.Interpolation
rotateY?: Animated.Interpolation,
perspective?: number
}>;

type Props = {|
Expand All @@ -34,7 +35,10 @@ class Confetti extends React.PureComponent<Props> {
const style = { width, height, backgroundColor: color, transform, opacity};

return (
<Animated.View style={[styles.confetti, containerStyle]}>
<Animated.View
pointerEvents="none"
renderToHardwareTextureAndroid={true}
style={[styles.confetti, containerStyle]}>
<Animated.View style={[isRounded && styles.rounded, style]} />
</Animated.View>
);
Expand Down
6 changes: 5 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @flow

import * as React from 'react';
import { Animated, Dimensions, Easing } from 'react-native';
import { Animated, Dimensions, Easing, Platform } from 'react-native';
import type { CompositeAnimation } from 'react-native/Libraries/Animated/src/AnimatedImplementation';
import type { EndResult } from 'react-native/Libraries/Animated/src/animations/Animation';

Expand Down Expand Up @@ -216,6 +216,10 @@ class Explosion extends React.PureComponent<Props, State> {
const containerTransform = [{translateX: left}, {translateY: top}];
const transform = [{rotateX}, {rotateY}, {rotate: rotateZ}, {translateX}];

if (Platform.OS === 'android') {
leezumstein marked this conversation as resolved.
Show resolved Hide resolved
transform.push({ perspective: 100 });
VincentCATILLON marked this conversation as resolved.
Show resolved Hide resolved
}

return (
<Confetti
color={item.color}
Expand Down