Fringe81アドベントカレンダー1日目の記事です。
弊社の提供しているUniposというサービスではWeb版のほか、Android/iOS向けにReact Nativeを使ってアプリを開発しています。
今回はそのアプリの新機能開発において、「枠線だけの吹き出し」を作る必要が出たためやってみた、という軽めの記事でございます。
「枠線だけの吹き出し」というのはこんなやつです。
react-native-svgなどを使って作る方法もありそうですが、今回は依存ライブラリなしでReact Nativeの標準コンポーネントだけを使って作ってみました。
環境
- React Native 0.59.9
- TypeScript 3.7.2
今回使用した環境は上記ですが、おそらく他の環境でも問題なく使用できるはずです。
結論
仕組みはどうあれ、先に出来上がったコードを貼ります。
import * as React from 'react';
import {View, Text, StyleSheet} from 'react-native';
const BALLOON_TRIANGLE_HEIGHT = 14;
export const CommentBalloon = ({text}: {text: string}) => {
return (
<View style={{justifyContent: 'flex-end', paddingTop: BALLOON_TRIANGLE_HEIGHT}}>
<View style={styles.balloonContainer}>
<View style={[styles.balloonTriangleBase, styles.balloonTriangleOuter]} />
<View style={[styles.balloonTriangleBase, styles.balloonTriangleInner]} />
<View style={styles.textContainer}>
<Text style={styles.text}>{text}</Text>
</View>
</View>
</View>
);
};
const styles = StyleSheet.create({
balloonContainer: {
width: '100%',
backgroundColor: '#FFFFFF',
borderWidth: 1,
borderColor: '#E1E6E6',
borderRadius: 2
},
balloonTriangleBase: {
width: 0,
height: 0,
position: 'absolute',
bottom: '100%',
borderTopColor: 'transparent',
borderLeftColor: 'transparent',
borderRightColor: 'transparent'
},
balloonTriangleOuter: {
left: 32,
borderWidth: BALLOON_TRIANGLE_HEIGHT,
borderBottomColor: '#E1E6E6'
},
balloonTriangleInner: {
left: 33,
borderWidth: BALLOON_TRIANGLE_HEIGHT - 1, // border分だけ引く
borderBottomColor: '#FFFFFF'
},
textContainer: {
padding: 15
},
text: {
fontWeight: '300',
fontSize: 14,
lineHeight: 20,
color: '#4A4A4A',
maxWidth: '100%',
letterSpacing: 0
}
});
仕組み
触ったことがある方はすでにご存知の通り、React NativeではCSSと似た形式のプロパティを持ったオブジェクトを用いて画面のスタイルを定義します。
そのため、CSSでできることは大抵できるのですが、いくつかできないことがあります。その中の一つに、:before
や:after
といった擬似要素が挙げられます。
CSSを使って吹き出しを作る方法として有名なのは、:before
や:after
擬似要素を用いて、大きさのない要素に対してborder
を当てることによって、吹き出しの三角形の部分(余談ですがくちばしって呼ぶらしいですね)を作る方法があります。
【コピペ改変OK】CSSで作れる吹き出しデザイン8選 | creive【クリーブ】
Webでの方法はこちらの記事がとても分かり易かったです。
React Nativeでは擬似要素が使用できないため、代わりに中身のないView
コンポーネントを配置して、そのコンポーネントに対してスタイルを当てています。
また、枠線のみの吹き出しにするため、大きさの違うくちばしを2つ用意し、外側の線部分と、内側の塗りの部分をずらして重ねています。(balloonTriangleOuter
とballoonTriangleInner
がそれ)
また、くちばしの部分は絶対位置指定(position: 'absolute'
)で配置しているため、このままだと上に配置されたコンポーネントがくちばしの上に重なってしまうのを防止するため、 <View style={{justifyContent: 'flex-end', paddingTop: BALLOON_TRIANGLE_HEIGHT}}>
というコンポーネントでラップすることでCommentBalloonコンポーネント全体で見たときにくちばしを含んだ高さとなるように調整しています。
所感
React NativeのstyleがWebのCSSと同じ挙動になるように実装されているおかげもあり、こうやってWebでよく用いられるテクニックを転用して、さほど悩むことなく画面を作れるのは幸せなことだと改めて思いました。