Panresponder + interpolate

问题描述 投票:0回答:1

我有一个组件 父母 其中有一个Animated.value "this.progress",它的取值范围是[1, 3]。

父节点渲染一个组件 儿童传来 作为道具。

// 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}
      />
    );
  }
}

孩子的位置取决于进度。

  • 如果进度=1,孩子的位置是[1,3] 。
  • 如果进度=2,则孩子的位置是[2,2] 。
  • 如果进度=3,则孩子的位置是[1,1] 。

如果一个动画改变了进度的值,那么Child必须以accoringly的方式移动(例如,如果进度从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 },
    ]}
  />
);
}

效果很好,但现在我还想用用户的手指让孩子移动。为此,我定义了一个panResponder。问题是panResponder也使用了一个transform。现在我有两个变换。

  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为假,我就使用第一个变换,否则就使用第二个变换。这看起来很有希望,但我没有成功地使它工作。

你能告诉我如何做到这一点吗?

javascript react-native animation interpolation panresponder
1个回答
0
投票

一个布尔变量isMoving表示使用哪种变换是好主意。技巧是。

  • 把isMoving放在状态中,
  • 将我的offset变量的偏移量设置为插值的当前值。

这就是结果。

// 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;
© www.soinside.com 2019 - 2024. All rights reserved.