背景
アリペイ会員カードのページでは、周りの折り畳み式携帯電話、ジェスチャーを移動させる効果を持つ光があります。
私たちは、この効果を達成するために持っているが、我々はRNページのカードであり、その後、RNは、この機能を実現することができますか?
調査
少し見えるようになったREACT-ネイティブセンサーを、おそらく次のように書かれて
subscription = attitude.subscribe(({ x, y, z }) =>
{
let newTranslateX = y * screenWidth * 0.5 + screenWidth/2 - imgWidth/2;
this.setState({
translateX: newTranslateX
});
}
);
复制代码
これは最終的なものであり、ネイティブJSの間、ページをリフレッシュする--setState伝統的な方法であるブリッジを介して非同期通信であるため、最終的な結果は、その意志カトンです。
どのようにないブリッジを介して、直接の答えが何であるかのネイティブの更新されたビューへの私たちすることができます- アニメーションのために使用したネイティブドライバー!!!
アニメーションのためのネイティブドライバを使用
アニメーションとは何ですか
アニメーションAPIはまた、アニメーションを更新表示するAnimated.Value操作を結合、Animated.Value小道具やスタイル、そしてAnimated.timing()メソッドなどで、アニメーションがスムーズに実行することができます。アニメーションAPIの詳細を見ることができるここに。
アニメーションデフォルトでは、図を駆動JSドライバを使用することで、次のように動作します:
このページの更新プロセスでは:
[JS]はアニメーションドライバが使用
requestAnimationFrame
更新するAnimated.Value
[JS]補間計算[JS]更新Animated.View
小道具
[JS→N]直列化ビュー更新イベント
[N]UIView
またはandroid.View
更新されます。
Animated.event
あなたは、1つのビューイベントに関連するAnimated.event Animated.Valueを使用することができます。
<ScrollView
scrollEventThrottle={16}
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: this.state.animatedValue } } }]
)}
>
{content}
</ScrollView>
复制代码
useNativeDriver
RNドキュメント useNativeDriverの説明については、次のように:
Animated
APIは直列化可能になるように設計されています。使用してネイティブドライバを、私たちは、ネイティブコードはすべてのフレームに橋を経由することなく、UIスレッド上でアニメーションを実行することができ、アニメーションを開始する前にネイティブにアニメーションについてのすべてを送ります。アニメーションが開始されると、JSのスレッドがアニメーションに影響を与えずにブロックすることができます。
ネイティブUIスレッドで達成することができ、レンダリングuseNativeDriver使用し、使用後onScrollはこれです:
<Animated.ScrollView // <-- Use the Animated ScrollView wrapper
scrollEventThrottle={1} // <-- Use 1 here to make sure no events are ever missed
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: this.state.animatedValue } } }],
{ useNativeDriver: true } // <-- Add this
)}
>
{content}
</Animated.ScrollView>
复制代码
useNativeDriverを使用した後、ページの更新がJSのない参加できなくなります
[N]天然使用
CADisplayLink
またはandroid.view.Choreographer
更新するためにAnimated.Value
、[N]が補間計算
[N]更新Animated.View
小道具
[N]UIView
またはandroid.View
更新されます。
今、私たちはonScrollイベントマッピングの似たScrollViewはどのように確認するために、今、最も適切な外にある場合には、リアルタイムのデータフリップ角センサの実際のニーズを達成したいです。
実現
まず、JSの終わりを見て、アニメーションAPIは、この機能を使用して実装されている方法のcreateAnimatedComponent、アニメーション内のAPIを持っています
const Animated = {
View: AnimatedImplementation.createAnimatedComponent(View),
Text: AnimatedImplementation.createAnimatedComponent(Text),
Image: AnimatedImplementation.createAnimatedComponent(Image),
...
}
复制代码
その後ネイティブ見て、RCTScrollViewはonScrollを達成する方法であります
RCTScrollEvent *scrollEvent = [[RCTScrollEvent alloc] initWithEventName:eventName
reactTag:self.reactTag
scrollView:scrollView
userData:userData
coalescingKey:_coalescingKey];
[_eventDispatcher sendEvent:scrollEvent];
复制代码
ここでRCTScrollEventが、実際には、RCTEventのサブクラスであるパッケージがあり、その後、この方法でそれを使用してください?それはないですか?だから、本来の呼び出しを使用して、それを試してみました:
if (self.onMotionChange) {
self.onMotionChange(data);
}
复制代码
私たちは、ああ、驚くことではないが、動作しない、ことがわかりました。その後、我々は、ネイティブの呼び出しでonScroll最終試運転でそれを見て:
だから、最終的に私は、[RCTEventDispatcherのSendEvent:]を呼び出す必要があったアップデートのネイティブUIをトリガーするので、このインタフェースを使用する必要があります。その後、我々はRCTMotionEvent、本体機能のコードが何であるかを達成するためにRCTScrollEventに従います。
- (NSDictionary *)body
{
NSDictionary *body = @{
@"attitude":@{
@"pitch":@(_motion.attitude.pitch),
@"roll":@(_motion.attitude.roll),
@"yaw":@(_motion.attitude.yaw),
},
@"rotationRate":@{
@"x":@(_motion.rotationRate.x),
@"y":@(_motion.rotationRate.y),
@"z":@(_motion.rotationRate.z)
},
@"gravity":@{
@"x":@(_motion.gravity.x),
@"y":@(_motion.gravity.y),
@"z":@(_motion.gravity.z)
},
@"userAcceleration":@{
@"x":@(_motion.userAcceleration.x),
@"y":@(_motion.userAcceleration.y),
@"z":@(_motion.userAcceleration.z)
},
@"magneticField":@{
@"field":@{
@"x":@(_motion.magneticField.field.x),
@"y":@(_motion.magneticField.field.y),
@"z":@(_motion.magneticField.field.z)
},
@"accuracy":@(_motion.magneticField.accuracy)
}
};
return body;
}
复制代码
最後に、最終用途コードでJS
var interpolatedValue = this.state.roll.interpolate(...)
<AnimatedDeviceMotionView
onDeviceMotionChange={
Animated.event([{
nativeEvent: {
attitude: {
roll: this.state.roll,
}
},
}],
{useNativeDriver: true},
)
}
/>
<Animated.Image style={{height: imgHeight, width: imgWidth, transform: [{translateX:interpolatedValue}]}} source={require('./image.png')} />
复制代码
最終的な効果:
最適化するために、続行
上記の実装では、接続がAnimated.eventとのAnimated.Valueを実現して、無用でAnimatedMotionViewを記述する必要があり、良いことではありません。そして、私たちのコンポーネントを使用して、同じRNのモジュールとして、この不要なビューを取り除く方法はありませんか?
Animated.eventんが、それを達成するために正確にどのように、その後、イベントを関連付け、Animated.Valueのですか?
まず、私たちが見に呼び出しを実装、この機能は、ネイティブの呼び出し:node_modules/react-native/Libraries/Animated/src/AnimatedImplementation.js
createAnimatedComponent
attachNativeEvent
NativeAnimatedAPI.addAnimatedEventToView(viewTag, eventName, mapping);
复制代码
私たちは、ネイティブコードを見て、この機能を実現する方法です。
- (void)addAnimatedEventToView:(nonnull NSNumber *)viewTag
eventName:(nonnull NSString *)eventName
eventMapping:(NSDictionary<NSString *, id> *)eventMapping
{
NSNumber *nodeTag = [RCTConvert NSNumber:eventMapping[@"animatedValueTag"]];
RCTAnimatedNode *node = _animationNodes[nodeTag];
......
NSArray<NSString *> *eventPath = [RCTConvert NSStringArray:eventMapping[@"nativeEventPath"]];
RCTEventAnimation *driver =
[[RCTEventAnimation alloc] initWithEventPath:eventPath valueNode:(RCTValueAnimatedNode *)node];
NSString *key = [NSString stringWithFormat:@"%@%@", viewTag, eventName];
if (_eventDrivers[key] != nil) {
[_eventDrivers[key] addObject:driver];
} else {
NSMutableArray<RCTEventAnimation *> *drivers = [NSMutableArray new];
[drivers addObject:driver];
_eventDrivers[key] = drivers;
}
}
复制代码
最終構築物eventDriverの情報をEventMapping、我々はRCTEventの天然構造に呼び出されます最終的にはドライバがのSendEventを呼び出します。
- (void)handleAnimatedEvent:(id<RCTEvent>)event
{
if (_eventDrivers.count == 0) {
return;
}
NSString *key = [NSString stringWithFormat:@"%@%@", event.viewTag, event.eventName];
NSMutableArray<RCTEventAnimation *> *driversForKey = _eventDrivers[key];
if (driversForKey) {
for (RCTEventAnimation *driver in driversForKey) {
[driver updateWithEvent:event];
}
[self updateAnimations];
}
}
复制代码
など、その後viewTagとeventNameのの役割は、キーに接続されていますか?何?
これはviewTagの観点からRNは最終的にちょうどのみ、そして私たちは、ただ一つのviewTagができていることができない、このビューを必要としない一意の文字列になって識別しますか?
道に沿って、私たちはこの世代だけviewTagを見てください。私たちは、コードを見てJSのUIViewの(RNバージョン0.45.1)をロード
mountComponent: function(
transaction,
hostParent,
hostContainerInfo,
context,
) {
var tag = ReactNativeTagHandles.allocateTag();
this._rootNodeID = tag;
this._hostParent = hostParent;
this._hostContainerInfo = hostContainerInfo;
...
UIManager.createView(
tag,
this.viewConfig.uiViewClassName,
nativeTopRootTag,
updatePayload,
);
...
return tag;
}
复制代码
AllocateTag我々はReactNativeTagHandles viewTagを生成するには、このメソッドを使用することができます。
2019年2月25日更新:なし露出allocateTag()メソッドのでRN0.58.5、多数の問題を回避タグとして割り当てることができるように
これまでのところ、我々はAnimated.eventとAnimated.Valueを接続するためにattachNativeEvent方法のAnimatedImplementationを使用することができ、およびビューを追加するために、無駄な時間をレンダリングする必要はありません。
詳細会場Githubにしてください:github.com/rrd-fe/reac ...、私にスターを付けて良い感じ:)
最後に、我々は我々スター歓迎大型フロントエンドチームのブログを貸すすべてが、すべての記事がために同時に更新されますほとんどの列を知っているとナゲッツのアカウントを、我々はいくつかの高品質な大型のフロントエンド技術記事を共有し、週を。
参照
facebook.github.io/react-nativ...
facebook.github.io/react-nativ...
ます。https://juejin.im/post/5cfdc0995188252ed3172d34で再現