React Native Animated - 展开以新闻为中心的圆圈
React Native Animated - Expand circle centered on press
我正在尝试在 React Native 上使用 Animated 创建一个组件,其中一个圆圈从按下点开始扩展以包含整个屏幕。
这个想法是基本上有一个颜色轮播,比如说:
const colors = ['white', 'black', 'green', 'blue', 'red', 'pink'];
您从颜色 0 开始作为背景,然后,当您按下屏幕时,一个颜色 1 的圆圈从按下点开始(从无到有)扩展,并占据整个屏幕。到那时,随着动画的完成,colorIndex
递增,动画圆圈同时被擦除,因此,我们无缝地移动到下一个颜色,现在是背景。 (例如,我们现在使用 'black' 作为背景色)。
然后,当您再次按下时,重复该过程,扩大一圈 "green" 以填满屏幕,然后将绿色更新为 BG 颜色,等等
(为了阐明我在动画中寻找的内容,请检查 this GIF(减去图标等))。
我正在使用 Animated 进行尝试,代码如下,在按下主背景组件(TouchableHighlight)时调用:
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
函数中,我将 absolute
ly 定位的圆组件的 left
和 top
设置为等于位置,然后我将 width
和 height
设置为等于 diameter
,以及 borderRadius
(diameter / 2.0
将不起作用,因为 diameter
是一个动画对象,而不是一个号码)。
这里有两个问题我似乎无法弄清楚(都在这个问题底部的 GIF 中可见):
- 每个动画前都有黑色闪光。它涵盖了整个 TouchableHighlight 组件。而且它总是黑色的。不管我穿什么颜色。组件中没有硬编码的黑色(!)。
- 位置决定了圆的边缘(或者,更准确地说,它被转录在其中的正方形),而不是中心。我希望它确定圆的中心,以便圆从我的压力机向外扩展。
我假设如果没有所有代码,#1 真的很难推理。所以我将在下面粘贴完整的组件代码。我希望第二个 conceptual/easy 可以在不玩它的情况下弄清楚。
谁能想出断言圆 center 位置的好方法?我担心它需要根据圆的直径 (left: location.x - diameter._value / 2
) 不断更新圆的 left/top 位置,这将消除 Animated 的所有本机性能优势,据我所知。有更好的想法吗?
这是完整的组件代码:
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;
这是当前的功能(在 iPhone X 模拟器中):
如果您希望您的圈子从点击点扩大,我建议使用 transform: scale()
而不是动画 width/height 属性。这样你只需要动画一个 属性.
您仍然需要将您的圆圈居中。假设您将 width/height 设置为 100px。现在您将 transform: scale(0)
设置为您的初始大小,如果您愿意,您可以将您的圆圈扩大到 1 以上,这样它将占据全屏。您可以通过数学来获得 timing/feel 的正确结果。
使用这种方法,您仍然会遇到圆圈从左上角扩大的问题。那是因为你把它放在那里了。 top/left 是相对于父容器的。您现在需要将圆圈相对于自身居中。这意味着您将需要添加另一个 transform
属性。 transform: translateX(-50%)translateY(-50%)
。这将始终使您的圆相对于它的中心位置居中。
开始转换:transform: translateX(-50%)translateY(-50%)scale(0)
放大变换:transform: translateX(-50%)translateY(-50%)scale(x)
其中 x
是您希望圆圈变得多大。
我正在尝试在 React Native 上使用 Animated 创建一个组件,其中一个圆圈从按下点开始扩展以包含整个屏幕。
这个想法是基本上有一个颜色轮播,比如说:
const colors = ['white', 'black', 'green', 'blue', 'red', 'pink'];
您从颜色 0 开始作为背景,然后,当您按下屏幕时,一个颜色 1 的圆圈从按下点开始(从无到有)扩展,并占据整个屏幕。到那时,随着动画的完成,colorIndex
递增,动画圆圈同时被擦除,因此,我们无缝地移动到下一个颜色,现在是背景。 (例如,我们现在使用 'black' 作为背景色)。
然后,当您再次按下时,重复该过程,扩大一圈 "green" 以填满屏幕,然后将绿色更新为 BG 颜色,等等
(为了阐明我在动画中寻找的内容,请检查 this GIF(减去图标等))。
我正在使用 Animated 进行尝试,代码如下,在按下主背景组件(TouchableHighlight)时调用:
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
函数中,我将 absolute
ly 定位的圆组件的 left
和 top
设置为等于位置,然后我将 width
和 height
设置为等于 diameter
,以及 borderRadius
(diameter / 2.0
将不起作用,因为 diameter
是一个动画对象,而不是一个号码)。
这里有两个问题我似乎无法弄清楚(都在这个问题底部的 GIF 中可见):
- 每个动画前都有黑色闪光。它涵盖了整个 TouchableHighlight 组件。而且它总是黑色的。不管我穿什么颜色。组件中没有硬编码的黑色(!)。
- 位置决定了圆的边缘(或者,更准确地说,它被转录在其中的正方形),而不是中心。我希望它确定圆的中心,以便圆从我的压力机向外扩展。
我假设如果没有所有代码,#1 真的很难推理。所以我将在下面粘贴完整的组件代码。我希望第二个 conceptual/easy 可以在不玩它的情况下弄清楚。
谁能想出断言圆 center 位置的好方法?我担心它需要根据圆的直径 (left: location.x - diameter._value / 2
) 不断更新圆的 left/top 位置,这将消除 Animated 的所有本机性能优势,据我所知。有更好的想法吗?
这是完整的组件代码:
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;
这是当前的功能(在 iPhone X 模拟器中):
如果您希望您的圈子从点击点扩大,我建议使用 transform: scale()
而不是动画 width/height 属性。这样你只需要动画一个 属性.
您仍然需要将您的圆圈居中。假设您将 width/height 设置为 100px。现在您将 transform: scale(0)
设置为您的初始大小,如果您愿意,您可以将您的圆圈扩大到 1 以上,这样它将占据全屏。您可以通过数学来获得 timing/feel 的正确结果。
使用这种方法,您仍然会遇到圆圈从左上角扩大的问题。那是因为你把它放在那里了。 top/left 是相对于父容器的。您现在需要将圆圈相对于自身居中。这意味着您将需要添加另一个 transform
属性。 transform: translateX(-50%)translateY(-50%)
。这将始终使您的圆相对于它的中心位置居中。
开始转换:transform: translateX(-50%)translateY(-50%)scale(0)
放大变换:transform: translateX(-50%)translateY(-50%)scale(x)
其中 x
是您希望圆圈变得多大。