0

我有一个组件级,它有一个 Animated.value “this.progress”,它采用 [1, 3] 中的值。

Parent 渲染一个 Component Child并将进度作为 props 传递给它:

// File Parent.js
import React from "react";
import { Animated } from "react-native";
import Child from "./Child.js"

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.progress = new Animated.Value(1)
  }

  render() {
    return (
      <Child
        progress = {this.progress}
      />
    );
  }
}

Child 位置取决于进度:

  • 如果progress = 1,则子位置为[1, 3]
  • 如果progress = 2,则子位置为[2, 2]
  • 如果progress = 3,则子位置为[1, 1]

如果动画改变了progress的值,Child必须相应地移动(例如,如果progress是从1到2的动画,用户必须看到Child从[1, 3]平滑地移动到[2, 2] )。

为此,我使用了两个插值:

// In the constructor of Child.js
this.interpolateX = this.props.progress.interpolate({
  inputRange: [1, 2, 3],
  outputRange: [1, 2, 1],
});

this.interpolateY = this.props.progress.interpolate({
  inputRange: [1, 2, 3],
  outputRange: [3, 2, 1],
});

我用它们使用“变换”来固定孩子的位置:

// Render method of Child.js
render() {
return (
  <Animated.View
    style={[
      {
        transform: [
          {
            translateX: this.interpolateX,
          },
          {
            translateY: this.interpolateY,
          },
        ],
      },
      { position: 'absolute' },
      { left: 0 },
      { top: 0 },
    ]}
  />
);
}

它运作良好,但现在我还想使用用户手指使 Child 可移动。为此,我定义了一个 panResponder。问题是 panResponder 也使用了转换。我现在有 2 个转换:

  1. 用于从 this.props.progress 插入 Child 的位置;
  2. panResponder 用来用用户手指移动 Child 的一种。

我不知道如何结合这两个变换。这是我尝试过的:

// File Child.js
import React from "react";
import { Animated, PanResponder } from "react-native";

class Child extends React.Component {
  constructor(props) {
    super(props);

    this.interpolateX = this.props.progress.interpolate({
      inputRange: [1, 2, 3],
      outputRange: [1, 2, 1],
    });

    this.interpolateY = this.props.progress.interpolate({
      inputRange: [1, 2, 3],
      outputRange: [3, 2, 1],
    });

    this.offset = new Animated.ValueXY({ x: 0, y: 0 });

    this._val = { x: 0, y: 0 };

    this.offset.addListener((value) => (this._val = value));

    // Initiate the panResponder
    this.panResponder = PanResponder.create({
      // Ask to be the responder
      onStartShouldSetPanResponder: () => true,

      // Called when the gesture starts
      onPanResponderGrant: () => {
        this.offset.setOffset({
          x: this._val.x,
          y: this._val.y,
        });

        this.offset.setValue({ x: 0, y: 0 });
      },

      // Called when a move is made
      onPanResponderMove: Animated.event([
        null,
        { dx: this.offset.x, dy: this.offset.y },
      ]),

      onPanResponderRelease: (evt, gesturestate) => {
        console.log(
          "released with offsetX: " +
            this.offset.x._value +
            "/" +
            this.offset.y._value
        );
      },
    });
  }

  render() {
    const panStyle = {
      transform: this.offset.getTranslateTransform(),
    };

    return (
      <Animated.View
        {...this.panResponder.panHandlers}
        style={[
          panStyle,
          {
            transform: [
              {
                translateX: this.interpolateX,
              },
              {
                translateY: this.interpolateY,
              },
            ],
          },
          { position: "absolute" },
          { left: 0 },
          { top: 0 },
        ]}
      />
    );
  }
}

export default Child;

这没用。panStyle 似乎被忽略了。我想你不能同时进行两个变换。

我还尝试在转换中“添加”这两个表达式,但没有成功(也许可行)。

我的最后一个想法是在用户移动 Child 时让布尔值“isMoving”等于 true。如果 isMoving 为假,我使用第一个转换,否则,我使用第二个。这似乎很有希望,但我没有成功地让它发挥作用。

你能告诉我我该怎么做吗?

4

1 回答 1

1

一个布尔变量 isMoving 指示使用哪个变换是个好主意。诀窍是:

  • 将 isMoving 置于状态;
  • 将我的偏移变量的偏移量设置为插值的当前值。

结果如下:

// File Child.js
import React from "react";
import { Animated, PanResponder } from "react-native";

class Child extends React.Component {
  constructor(props) {
    super(props);

    this.interpolateX = this.props.progress.interpolate({
      inputRange: [1, 2, 3],
      outputRange: [1, 2, 1],
    });

    this.interpolateY = this.props.progress.interpolate({
      inputRange: [1, 2, 3],
      outputRange: [3, 2, 1],
    });

    this.offset = new Animated.ValueXY({ x: 0, y: 0 });

    this.state = {
      isMoving: false,
    };

    // Initiate the panResponder
    this.panResponder = PanResponder.create({
      // Ask to be the responder
      onStartShouldSetPanResponder: () => true,

      // Called when the gesture starts
      onPanResponderGrant: () => {
        this.setState({
          isMoving: true,
        });

        this.offset.setOffset({
          x: this.interpolateX.__getValue(),
          y: this.interpolateY.__getValue(),
        });

        this.offset.setValue({ x: 0, y: 0 });
      },

      // Called when a move is made
      onPanResponderMove: Animated.event([
        null,
        { dx: this.offset.x, dy: this.offset.y },
      ]),

      onPanResponderRelease: (evt, gesturestate) => {
        this.setState({
          isMoving: false,
        });
      },
    });
  }

  render() {
    const panStyle = {
      transform: this.offset.getTranslateTransform(),
    };

    if (this.state.isMoving)
      return (
        <Animated.View
          {...this.panResponder.panHandlers}
          style={[panStyle, { position: "absolute" }, { left: 0 }, { top: 0 }]}
        />
      );
    else
      return (
        <Animated.View
          {...this.panResponder.panHandlers}
          style={[
            {
              transform: [
                {
                  translateX: this.interpolateX,
                },
                {
                  translateY: this.interpolateY,
                },
              ],
            },
            { position: "absolute" },
            { left: 0 },
            { top: 0 },
          ]}
        />
      );
  }
}

export default Child;
于 2020-06-04T14:47:05.070 回答