Help us understand the problem. What is going on with this article?

React Nativeでライブラリを使わずに枠線だけの吹き出しを作る

Fringe81アドベントカレンダー1日目の記事です。

弊社の提供しているUniposというサービスではWeb版のほか、Android/iOS向けにReact Nativeを使ってアプリを開発しています。
今回はそのアプリの新機能開発において、「枠線だけの吹き出し」を作る必要が出たためやってみた、という軽めの記事でございます。

「枠線だけの吹き出し」というのはこんなやつです。

スクリーンショット 2019-11-28 9.38.48.png

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つ用意し、外側の線部分と、内側の塗りの部分をずらして重ねています。(balloonTriangleOuterballoonTriangleInnerがそれ)


また、くちばしの部分は絶対位置指定(position: 'absolute')で配置しているため、このままだと上に配置されたコンポーネントがくちばしの上に重なってしまうのを防止するため、 <View style={{justifyContent: 'flex-end', paddingTop: BALLOON_TRIANGLE_HEIGHT}}> というコンポーネントでラップすることでCommentBalloonコンポーネント全体で見たときにくちばしを含んだ高さとなるように調整しています。

Simulator Screen Shot - iPhone 11 Pro Max - 2019-12-01 at 11.41.34.png

所感

React NativeのstyleがWebのCSSと同じ挙動になるように実装されているおかげもあり、こうやってWebでよく用いられるテクニックを転用して、さほど悩むことなく画面を作れるのは幸せなことだと改めて思いました。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした