React Native 101 從打造一個線上商城開始
  • 環境安裝
  • 專案架構介紹與開始前的準備
  • 個人資訊-PersonalInfo
    • Layout 概念
    • View, Image, TextInput 元件介紹
    • 使用react-native-fetch-blob 來上傳照片
  • 商品管理介面-MerchandiseList
    • 元件介紹
  • 新建商品頁面-NewMerchandise
  • 商城頁面-MerchandiseGrid
  • 查看商品詳細頁面-MerchandiseDetail
  • 購物車頁面-ShoppingCart
  • 設定畫面Navigation
  • 使用Appetize.io
  • 修改專案名稱
  • 使用Expo
Powered by GitBook
On this page
  • Animations
  • 常見錯誤:
  • 參考資料:
  • 使用Linking
  • 使用Webview
  • 加入商品詳細
  • 加入ImageGallery
  • 加入商品回覆紀錄

Was this helpful?

查看商品詳細頁面-MerchandiseDetail

Animations

將要做動畫的元件用Animated.View 進行包裝

  •    Animated,
       Easing
     } from 'react-native';
    
     ....
      <Animated.View style={[scaleAnimation]}>
         <Icon size={56} color="#777" name="arrow-right" />
      </Animated.View>

設定Animated.Value並給予一個動畫尚未開始前的初始值

  •  state = {
         scaleAnimatedValue: new Animated.Value(1)
     };

動畫的描述模型

  • timing

    • 以duration 與 toValue, easing 型態 描述

  • decay

    • 以velocity 與 acceleration 描述

  • const toBig = Animated.timing(this.state.scaleAnimatedValue, {
          duration: DURATION,
          easing: Easing.linear,
          toValue: 1.3
    });

對動畫進行行為疊加 (Parallel, Sequence, Loop) 並執行動畫

  •  const breathe = Animated.loop(Animated.sequence([toBig, toSmall]));
     breathe.start();

將Animated.Value 綁定至要進行動畫的Layout / Transform Props

  const { scaleAnimatedValue } = this.state
  const scaleAnimation = {
     transform: [ { scale: scaleAnimatedValue } ]
  };

  return (
     <Animated.View style={[scaleAnimation]}>
         <Icon size={56} color="#777" name="arrow-left" />
     </Animated.View>
  )
 }

常見錯誤:

Attempted to assign readonly property animation

 const yourTransformStyle = {
      transform: [{ scale: 1 }]
  };
  • transform 拼錯,系統找不到此property

參考資料:

目前useNativeDriver: true 只support Transform Props 與 opacity

使用Linking

  • 開啟外部連結, mailto://, tel://, http://

  • 開啟電話應用

    goPhoneURL = (phone) => {
      Linking.openURL(`tel://${phone}`);
    };

使用Webview

  • 使用baseUrl: ''避免亂碼 (hack) react-native ^0.53 解決

  • 監聽Webview 的url 變更 onNavigationStateChange

  • 點擊連結時開啟系統browser,使用Linking.openURL

  • 必須寫定Webview 的 height 造成layout 的 不方便,無法根據內容做高的動態調整

    * 當html 獲得後,計算高度,並動態決定webview 的 height

         <WebView
              scrollEnabled={false}
              style={styles.description}
              source={{ html: productDetail.description, baseUrl: '' }}
              ref={(ref) => {
                this.webview = ref;
              }}
              onNavigationStateChange={(event) => {
                if (event.url.indexOf('about:blank') < 0) {
                  this.webview.stopLoading();
                  Linking.openURL(event.url);
                }
              }}
            />

加入商品詳細

  • 使用 RatingBar 進行收藏

  • yarn add react-native-star-rating

import RatingBar from 'react-native-star-rating';
...


  <RatingBar
   selectedStar={() => {
     this.setState({
       rating: rating === 0 ? 1 : 0
     });

   }}
   maxStars={1}
   rating={rating}
   iconSet="FontAwesome" 
 />
  • 加入放大動畫

 componentDidMount() {
    const scaleUp = Animated.timing(this.state.scaledValue, {
      duration: 200,
      toValue: 1.3
    });
    const scaleDown = Animated.timing(this.state.scaledValue, {
      duration: 200,
      toValue: 1
    });
    this.bounceAnimation = Animated.sequence([scaleUp, scaleDown]);
}

render(){
  return (
      <Animated.View
          style={[styles.rating, { transform: [{ scale: this.state.scaledValue }] }]}
      >
        <RatingBar
          selectedStar={() => {
            this.setState({
              rating: rating === 0 ? 1 : 0
            });
            this.bounceAnimation.start(() => {
              this.bounceAnimation.reset();
            });
          }}
          maxStars={1}
          rating={rating}
          iconSet="FontAwesome"
        />
      </Animated.View>
  )
}

加入ImageGallery

  • 疊加兩個Icon箭頭在圖片上方

  • 實作按下箭頭後,圖片會進行更換

  • 加入箭頭的呼吸動畫

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
  StyleSheet,
  View,
  ViewPropTypes,
  Text,
  Image,
  Animated,
  TouchableOpacity,
  Easing
} from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';

const styles = StyleSheet.create({
  container: {
    flex: 1
  },
  image: {
    height: '100%',
    width: '100%',
    position: 'absolute'
  },
  iconLeft: {
    left: 10,
    position: 'absolute'
  },
  iconRight: {
    right: 10,
    position: 'absolute'
  },
  iconContainer: {
    justifyContent: 'center'
  },
  animatedIcon: {
    height: 50,
    width: 50
  },
  center: {
    justifyContent: 'center',
    alignItems: 'center'
  }
});
export default class ImageGallery extends Component {
  static propTypes = {
    images: PropTypes.array.isRequired,
    contentContainerStyle: ViewPropTypes.style.isRequired
  };

  state = {
    currentIdx: 0,
    scaleAnimatedValue: new Animated.Value(1)
  };

  componentDidMount = () => {
    const DURATION = 1000;
    const toBig = Animated.timing(this.state.scaleAnimatedValue, {
      duration: DURATION,
      easing: Easing.linear,
      toValue: 1.3
    });
    const toSmall = Animated.timing(this.state.scaleAnimatedValue, {
      duration: DURATION,
      easing: Easing.linear,
      toValue: 1
    });

    const breathe = Animated.loop(Animated.sequence([toBig, toSmall]));
    breathe.start();
  };
  next = (nextInc) => {
    const { currentIdx } = this.state;
    const next =
      currentIdx + nextInc < 0 || currentIdx + nextInc >= this.props.images.length
        ? 0
        : currentIdx + nextInc;
    return next;
  };

  toRight = () => {
    this.setState({
      currentIdx: this.next(1)
    });
  };
  toLeft = () => {
    this.setState({
      currentIdx: this.next(-1)
    });
  };
  render() {
    const { currentIdx, scaleAnimatedValue } = this.state;
    const currentImageUri = this.props.images[currentIdx];
    const scaleAnimation = {
      transform: [{ scale: scaleAnimatedValue }]
    };

    return (
      <View style={[this.props.contentContainerStyle]}>
        <Image style={styles.image} source={{ uri: currentImageUri }} />
        <View style={[styles.container, styles.iconContainer]}>
          <TouchableOpacity
            activeOpacity={0.7}
            style={[styles.iconRight, styles.center, styles.animatedIcon]}
            onPress={this.toRight}
          >
            <Animated.View style={[scaleAnimation]}>
              <Icon size={28} color="#ccc" name="arrow-right" />
            </Animated.View>
          </TouchableOpacity>
          <TouchableOpacity
            activeOpacity={0.7}
            style={[styles.iconLeft, styles.center, styles.animatedIcon]}
            onPress={this.toLeft}
          >
            <Animated.View style={[scaleAnimation]}>
              <Icon size={28} color="#ccc" name="arrow-left" />
            </Animated.View>
          </TouchableOpacity>
        </View>
      </View>
    );
  }
}

加入商品回覆紀錄

  • Layout 練習

Previous商城頁面-MerchandiseGridNext購物車頁面-ShoppingCart

Last updated 5 years ago

Was this helpful?

*

*

https://facebook.github.io/react-native/docs/transforms.html#transform
https://www.jianshu.com/p/b3cfc6b0c33f
http://easings.net/zh-cn
https://github.com/jsdf/react-native-htmlview
https://gist.github.com/epeli/10c77c1710dd137a1335