Javascript 反应本机动画-展开以按下键为中心的圆圈

Javascript 反应本机动画-展开以按下键为中心的圆圈,javascript,css,react-native,animation,react-animated,Javascript,Css,React Native,Animation,React Animated,我正在尝试使用React Native上的动画创建一个组件,其中一个圆从压力机的点扩展到整个屏幕 这个想法是,基本上有一个旋转木马的颜色,说: const colors = ['white', 'black', 'green', 'blue', 'red', 'pink']; 首先以颜色0作为背景,然后,当按下屏幕时,颜色1的圆圈从按下点开始展开(从无到有),并占据整个屏幕。此时,动画完成后,colorIndex将递增,动画圆圈同时被擦除,因此,我们无缝地移动到下一种颜色,即现在的背景。(例如

我正在尝试使用React Native上的动画创建一个组件,其中一个圆从压力机的点扩展到整个屏幕

这个想法是,基本上有一个旋转木马的颜色,说:

const colors = ['white', 'black', 'green', 'blue', 'red', 'pink'];
首先以颜色0作为背景,然后,当按下屏幕时,颜色1的圆圈从按下点开始展开(从无到有),并占据整个屏幕。此时,动画完成后,
colorIndex
将递增,动画圆圈同时被擦除,因此,我们无缝地移动到下一种颜色,即现在的背景。(例如,我们现在使用“黑色”作为背景色)

然后,当您再次按下时,该过程会重复,扩展一个“绿色”圆圈以填充屏幕,然后将绿色更新为背景色,等等

(要澄清我在动画中寻找的内容,请查看(减去图标等))

我正在用动画和如下代码来尝试这一点,在按下主背景组件(一个可触摸的突出显示)时调用:

然后,在
渲染
函数中,我将
绝对
设置为与位置相等,并将
宽度
高度
设置为等于
直径
,以及
边界半径
diameter/2.0
不起作用,因为
diameter
是动画对象,而不是数字)

这里有两个问题我似乎无法理解(这两个问题都可以在问题底部的GIF中看到):

  • 在每个动画之前都有一个黑色闪光灯。它覆盖了整个TouchableHighlight组件。它总是黑色的。无论我使用什么颜色。组件中没有硬编码的黑色(!)
  • 位置决定圆的边缘(或者更准确地说,是它所转录的正方形),而不是圆心。我希望它决定圆的圆心,这样圆就可以从我按下的按钮向外扩展
  • 我假设没有所有的代码#1真的很难推理。因此我将在下面粘贴完整的组件代码。我希望第二个代码更概念化/更容易理解,而无需使用它

    有人能想出一个好方法来确定圆心的位置吗?我担心这需要不断更新圆的左/顶部位置,作为其直径的函数(
    left:location.x-diameter.\u value/2
    ),这将消除动画的所有固有性能优势,据我所知。有更好的主意吗

    以下是完整的组件代码:

    import React from 'react';
    import { TouchableHighlight, Animated } from 'react-native';
    import { Dimensions } from 'react-native';
    
    const colors = ['white', 'black', 'green', 'blue', 'red', 'pink'];
    
    const color = index => colors[index % colors.length];
    
    class ColorScape extends React.Component {
      constructor(props) {
        super(props);
    
        this.state = {
          colorIndex: 0,
          location: { x: 0, y: 0 },
          diameter: new Animated.Value(0)
        };
      }
    
      triggerSwipe(event) {
        this.setState({ location: { x: event.nativeEvent.locationX, y: event.nativeEvent.locationY } });
    
        Animated.timing(
          this.state.diameter,
          { toValue: Dimensions.get('window').height * 2, duration: 500 },
        ).start(() => {
          this.state.diameter.setValue(0);
          this.setState({ colorIndex: this.state.colorIndex + 1 });
        });
      }
    
      render() {
        const { colorIndex, diameter, location } = this.state;
    
        const circleStyles = {
          backgroundColor: color(colorIndex + 1),
          width: diameter,
          height: diameter,
          borderRadius: diameter,
          position: 'absolute',
          zIndex: 2,
          left: location.x,
          top: location.y
        };
    
        return (
          <TouchableHighlight
            style={ {
              flex: 1,
              width: '100%',
              height: '100%',
              zIndex: 1,
              backgroundColor: color(colorIndex)
            } }
            onPress={ (event) => this.triggerSwipe(event) }
          >
            <Animated.View
              style={ circleStyles }
            />
          </TouchableHighlight>
        );
      }
    }
    
    export default ColorScape;
    
    从“React”导入React;
    从“react native”导入{TouchableHighlight,动画};
    从“react native”导入{Dimensions};
    常量颜色=[“白色”、“黑色”、“绿色”、“蓝色”、“红色”、“粉色”];
    const color=index=>colors[index%colors.length];
    类ColorScape扩展了React.Component{
    建造师(道具){
    超级(道具);
    此.state={
    颜色索引:0,
    位置:{x:0,y:0},
    直径:新动画。值(0)
    };
    }
    触发滑动(事件){
    this.setState({location:{x:event.nativeEvent.locationX,y:event.nativeEvent.locationY}});
    时间(
    这个,州,直径,
    {toValue:Dimensions.get('window')。高度*2,持续时间:500},
    ).start(()=>{
    此.state.diameter.setValue(0);
    this.setState({colorIndex:this.state.colorIndex+1});
    });
    }
    render(){
    const{colorIndex,diameter,location}=this.state;
    常数圈样式={
    背景颜色:颜色(颜色索引+1),
    宽度:直径,
    高度:直径,
    边界半径:直径,
    位置:'绝对',
    zIndex:2,
    左:location.x,
    上图:地点
    };
    返回(
    this.triggerSwipe(事件)}
    >
    

    如果您希望圆从单击点开始展开,我建议使用
    transform:scale()
    而不是设置宽度/高度属性的动画。这样您只需设置一个属性的动画

    您仍然需要将圆居中。假设您将宽度/高度设置为100px。现在您将
    transform:scale(0)
    设置为初始大小,如果您愿意,您可以将圆缩放到1以上,因此需要全屏显示。您可以玩数学游戏以获得正确的计时/感觉

    使用这种方法,您仍然会遇到从左上角开始放大圆的问题。这是因为您已将其定位在那里。左上角相对于父容器。您现在需要将圆相对于自身居中。这意味着您需要添加另一个
    transform
    属性。
    transform:translateeX(-50%)translateY(-50%)
    。这将始终使圆相对于其中心位置居中

    开始转换:
    transform:translateX(-50%)translateY(-50%)比例(0)

    放大的变换:
    transform:translateX(-50%)translateY(-50%)scale(x)
    其中
    x
    是您希望圆得到的大小。

    如果您希望圆从单击点开始展开,我建议使用
    transform:scale()
    而不是设置宽度/高度属性的动画。这样,您只需设置一个属性的动画

    您仍然需要将圆居中。假设您将宽度/高度设置为100px。现在您将
    transform:scale(0)
    设置为初始大小,如果您愿意,您可以将圆缩放到1以上,因此需要全屏显示。您可以玩数学游戏以获得正确的计时/感觉

    使用这种方法,您仍然会遇到从左上角开始放大圆的问题。这是因为您已将其定位在那里。左上角相对于父容器。您现在需要将圆相对于自身居中。这意味着您将
    import React from 'react';
    import { TouchableHighlight, Animated } from 'react-native';
    import { Dimensions } from 'react-native';
    
    const colors = ['white', 'black', 'green', 'blue', 'red', 'pink'];
    
    const color = index => colors[index % colors.length];
    
    class ColorScape extends React.Component {
      constructor(props) {
        super(props);
    
        this.state = {
          colorIndex: 0,
          location: { x: 0, y: 0 },
          diameter: new Animated.Value(0)
        };
      }
    
      triggerSwipe(event) {
        this.setState({ location: { x: event.nativeEvent.locationX, y: event.nativeEvent.locationY } });
    
        Animated.timing(
          this.state.diameter,
          { toValue: Dimensions.get('window').height * 2, duration: 500 },
        ).start(() => {
          this.state.diameter.setValue(0);
          this.setState({ colorIndex: this.state.colorIndex + 1 });
        });
      }
    
      render() {
        const { colorIndex, diameter, location } = this.state;
    
        const circleStyles = {
          backgroundColor: color(colorIndex + 1),
          width: diameter,
          height: diameter,
          borderRadius: diameter,
          position: 'absolute',
          zIndex: 2,
          left: location.x,
          top: location.y
        };
    
        return (
          <TouchableHighlight
            style={ {
              flex: 1,
              width: '100%',
              height: '100%',
              zIndex: 1,
              backgroundColor: color(colorIndex)
            } }
            onPress={ (event) => this.triggerSwipe(event) }
          >
            <Animated.View
              style={ circleStyles }
            />
          </TouchableHighlight>
        );
      }
    }
    
    export default ColorScape;