查看商品詳細頁面-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

參考資料:

https://facebook.github.io/react-native/docs/transforms.html#transform

https://www.jianshu.com/p/b3cfc6b0c33f

http://easings.net/zh-cn

目前useNativeDriver: true 只support Transform Props 與 opacity

使用Linking

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

  • 開啟電話應用

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

使用Webview

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

Last updated