# 查看商品詳細頁面-MerchandiseDetail

## Animations

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

* ```javascript
     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 描述
* ```javascript
  const toBig = Animated.timing(this.state.scaleAnimatedValue, {
        duration: DURATION,
        easing: Easing.linear,
        toValue: 1.3
  });
  ```

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

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

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

```javascript
  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`

### 參考資料：

[https://facebook.github.io/react-native/docs/transforms.html#transform](https://legacy.gitbook.com/book/zhang699/react-native/edit)

[https://www.jianshu.com/p/b3cfc6b0c33f](https://legacy.gitbook.com/book/zhang699/react-native/edit)

[http://easings.net/zh-cn](http://easings.net/zh-cn)

> 目前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

    \* [https://github.com/jsdf/react-native-htmlview](https://github.com/jsdf/react-native-htmlview)

    \* [https://gist.github.com/epeli/10c77c1710dd137a1335](https://gist.github.com/epeli/10c77c1710dd137a1335)

```javascript
         <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箭頭在圖片上方
* 實作按下箭頭後，圖片會進行更換
* 加入箭頭的呼吸動畫

```javascript
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 練習
