反应原生无限重复图像动画不同步

时间:2019-01-20 22:44:11

标签: react-native expo

我正在尝试使用一组图像在React Native中创建无限视差背景动画。我已经成功创建了动画。但是,动画播放的时间越长,它们看起来越不同步。

总体而言,我编写了按以下顺序创建三个动画的代码:

  1. 将图像分量的y偏移量从其初始位置移到0。
  2. 将图像分量的y偏移量从0移动到-image.height。
  3. 立即将图像分量的y偏移量移至所有图像分量的原始和。
  4. 再次将图像分量的y偏移量移至0。
  5. 再次将图像分量y偏移量移至-image.height。

我将动画序列3-5循环放置,以便它们无限重复。

我也有同样的问题,而没有使用Expo。我还考虑过使视图位置不是绝对的,因此视图将被迫相互接触。但是,使用这种方法,当我要切换组件顺序时,我将不得不重新渲染。

我创建了this Expo项目来演示正在发生的事情。

这是症状的屏幕截图: enter image description here

这是我当前的代码:

App.js

import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';
import { Constants } from 'expo';

// You can import from local files
import ScrollingBackground from './components/AssetExample';

// or any pure javascript modules available in npm
import { Card } from 'react-native-paper';

export default class App extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <ScrollingBackground style={styles.scrollingBackground} images={[require('./assets/chess.png'),require('./assets/chess.png'),require('./assets/chess.png')]}/>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    backgroundColor: '#ecf0f1',
  },
  scrollingBackground: {
    width: '100%',
    height: '100%',
    backgroundColor: 'blue',
  },
});

AssetExample.js

import React, { Component } from "react";
import {
  StyleSheet,
  View,
  Animated,
  Image,
  Dimensions,
  Easing
} from "react-native";

export default class ScrollingBackground extends Component {
  constructor(props) {
    super(props);
  }

  componentWillMount() {
    let imageComponents = [];
    let lastImageYOffset = 0;
    let counter = 0;
    let deviceWidth = Dimensions.get("window").width;
    this.props.images.forEach(image => {
      const { width, height } = Image.resolveAssetSource(image);

      let localElement = {};
      let currentKey = "image" + counter.toString();
      localElement.width = width;
      localElement.height = (height * deviceWidth) / width;
      localElement.initialOffset = lastImageYOffset;
      localElement.topPositionAnimated = new Animated.Value(lastImageYOffset);
      localElement.image = image;
      localElement.currentKey = currentKey;
      imageComponents.push(localElement);
      lastImageYOffset = lastImageYOffset + localElement.height;
      counter++;
    });
    lastImageYOffset = lastImageYOffset - imageComponents[imageComponents.length-1].height
    this.setState({
      imageComponents: imageComponents,
      lastImageYOffset: lastImageYOffset
    });
  }

  componentDidMount() {
    let animations = [];
    let arrayLength = this.state.imageComponents.length;
    for (let i = 0; i < arrayLength; i++) {
      // let height  = -1 * this.state.imageComponents[i].height
      // this.state.imageComponents[i].topPositionAnimated.addListener(({value}) => value == height ? console.log(this.state) : "");
      animations.push(
        Animated.sequence([
          Animated.timing(this.state.imageComponents[i].topPositionAnimated, {
            toValue: 0,
            duration:
              10 *
              (this.state.imageComponents[i].initialOffset),
            delay: 0,
            easing: Easing.linear,
            useNativeDriver: true
          }),
          Animated.timing(this.state.imageComponents[i].topPositionAnimated, {
            toValue: -1 * this.state.imageComponents[i].height,
            duration:
              10 *
              (this.state.imageComponents[i].height),
            delay: 0,
            easing: Easing.linear,
            useNativeDriver: true
          }),
          Animated.loop(
            Animated.sequence([
              Animated.timing(this.state.imageComponents[i].topPositionAnimated, {
                toValue: this.state.lastImageYOffset,
                duration: 0,
                delay: 0,
                useNativeDriver: true
              }),
              Animated.timing(this.state.imageComponents[i].topPositionAnimated, {
                toValue: 0,
                duration:
                  10 *
                  (this.state.lastImageYOffset),
                delay: 0,
                easing: Easing.linear,
                useNativeDriver: true
              }),
              Animated.timing(this.state.imageComponents[i].topPositionAnimated, {
                toValue: -1 * this.state.imageComponents[i].height,
                duration:
                  10 *
                  (this.state.imageComponents[i].height),
                delay: 0,
                easing: Easing.linear,
                useNativeDriver: true
              }),
            ])
          )
        ])
      );
    }


    Animated.parallel(animations).start();
  }

  render() {
    let elements = [];
    for (imageComponent of this.state.imageComponents) {
      elements.push(
        <Animated.Image
          key={imageComponent.currentKey}
          source={imageComponent.image}
          style={{
            position: "absolute",
            width: "100%",
            height: imageComponent.height,
            transform: [
              {
                translateY: imageComponent.topPositionAnimated
              }
            ],
            backgroundColor: "white"
          }}
        />
      );
    }
      return (
        <View
          style={[
            styles.container,
            { backgroundColor: this.props.style.backgroundColor }
          ]}
        >
          {elements}
        </View>
      );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    width: "100%",
    height: "100%"
  }
});

1 个答案:

答案 0 :(得分:0)

由于对多个视图进行动画处理存在延迟问题,因此对包含所有单个图像视图的容器视图进行动画处理被认为是更好的选择。我最终为此创建了一个反应本机库:https://www.npmjs.com/package/react-native-scrolling-images