参考文章:react native how to create twitter exploding hearts
看了这个文章后试着写了下,一些参数什么的写的有点随意,效果木有原作的那么好_(:зゝ∠)_,就当记录下学习笔记好了(ง •_•)ง

start~
引入Animated和ART组件,图形有大小位移等的变化,需要让Shape组价可动画化:
...
import {
ART,
Animated,
...
} from 'react-native';
const {
Surface,
Shape,
Group,
Path
} = ART;
const AnimatedShape = Animated.createAnimatedComponent(Shape);
创建Animated.Value,之后样式变化都靠它了_(:зゝ∠)_
constructor (props) {
super(props);
this.state = {
animation: new Animated.Value(0)
};
}
...
animate () {
Animated.timing(this.state.animation, {
toValue: 28, // 可以假设是做了一个28帧的gif
duration: 1000
}).start();
}
心形
- 形状用的SVG,也可以用css来画;
- 有一个大小变化的动画;
- 颜色从灰色变成红色
<TouchableWithoutFeedback
onPress={this.animate}
>
<View style={{height: 100, width: 100, backgroundColor: '#fff'}}>
<Surface
height="100"
width="100">
<AnimatedShape
d="M16,29 C15.9970361,29 11.914011,25.472682 8.03173648,20.4590299 C4.14946196,15.4453778 -2.46221558,7.68242391 3.08862305,2.10644531 C8.63946168,-3.46953329 16.1049427,5.07202148 16.0279365,5.07202148 C16.0279365,4.99789663 23.4116905,-3.48231344 28.9511719,2.10644531 C34.4897069,7.69648208 28.0043918,15.4325977 24.1315817,20.4590299 C20.2587715,25.4841842 15.9970361,29 16,29 Z"
fill={this.state.animation.interpolate({
inputRange: [0, 1],
outputRange: ['rgba(243,243,243)', 'rgba(235,65,61)'],
extrapolate: 'clamp'
})}
scale={this.state.animation.interpolate({
inputRange: [0, .01, 6, 10, 12, 18],
outputRange: [1, 0, .1, 1, 1.2, 1],
extrapolate: 'clamp'
})}
/>
</Surface>
</View>
</TouchableWithoutFeedback>
inputRange并没有写0-28,这里用了extrapolate: 'clamp'
,不让值超出outputRange的范围,不然大小和颜色还会继续变化。
形状没有类似[缩放中心点]的属性,所以大小变化的时候位置也会变(╯‵□′)╯︵┻━┻,不过可以改变的值还有x和y,即图形左上角的坐标。
谜之蹩脚的数学插入:

假设左上角坐标为(x0,y0),中心点坐标为(x1,y1),正常大小为1倍,缩放为原来的n≠0倍后
新x0 = x0 - (n - 1) * ( x1 - x0);
新y0 = y0 - (n - 1) * (y1 - y1);
这里画布是100 * 100,中心点为(50, 50),图形是[约]30 * 30 大小,左上角坐标就是(35, 35),算出来x y 的变化:
<AnimatedShape
...
x={this.state.animation.interpolate({
inputRange: [0, .01, 6, 10, 12, 18],
outputRange: [35, 50, 48.5, 35, 32, 35 ],
extrapolate: 'clamp'
})}
y={this.state.animation.interpolate({
inputRange: [0, .01, 6, 10, 12, 18],
outputRange: [35, 50, 48.5, 35, 32, 35 ],
extrapolate: 'clamp'
})}
/>
那一坨小圆点
位置
如果看成每个位置只有一个圆的话,就是7个圆在一个大圆形的边上面平均分布,假设第一个圆位置和y轴的夹角为0,那剩下6个圆形夹角[弧度]就是 360 / 7 * 1, 360 / 7 * 2 ...
谜之蹩脚的数学再次乱入:

假设大圆形半径为R,中心点坐标是(0,0),小圆从0开始第n个坐标为(Xn,Yn)
1弧度=180 / π * 1角度
弧度 360 / total * i = 360 / 7 * i
Xn = cosθ * R = cos((360 / 7 * i) * (π / 180)) * R = cos(2 * Math.PI * i / 7) * R
Yn = sinθ * R = sin((360 / 7 * i) * (π / 180)) * R = sin(2 * Math.PI * i / 7) * R
so:
const CENTER_X = 50,
CENTER_Y = 50; // 画布中心刚才说的是(50, 50)
...
componentWillMount () {
// init circle position
this.setState({
circlePosition: CIRCLE_COUNT.map((item, index) => {
return getPosition(index, 30);
})
});
function getPosition (index, radius) {
const a = 2 * Math.PI * index / 7;
return {
x: Math.cos(a) * radius + CENTER_X,
y: Math.sin(a) * radius + CENTER_Y
}
}
}
位移变化,颜色,旋转
...
renderCircle () {
let moveUp = this.state.animation.interpolate({
inputRange: [0, 5.99, 14],
outputRange: [0, 0, -1]
});
let moveDown = this.state.animation.interpolate({
inputRange: [0, 5.99, 14],
outputRange: [0, 0, 1]
});
var PARTICLE_COLORS = [
'rgb(158, 202, 250)',
'rgb(161, 235, 206)',
'rgb(208, 148, 246)',
'rgb(244, 141, 166)',
'rgb(234, 171, 104)',
'rgb(170, 163, 186)',
'rgb(208, 148, 246)'
];
return this.state.circlePosition.map((item, index) =>
<Group
x={item.x}
y={item.y}
key={index}
rotation={20 * index} // 角度额写的有点随意了。。
>
<Circle
x={moveUp}
y={moveUp}
scale={this.state.animation.interpolate({
inputRange: [0, 20, 28],
outputRange: [0, 1, 0]
})}
fill={PARTICLE_COLORS[index]}
stroke={PARTICLE_COLORS[index]}
radius={2}
/>
<Circle
x={moveDown}
y={moveDown}
scale={this.state.animation.interpolate({
inputRange: [0, 20, 28],
outputRange: [0, 1, 0]
})}
fill={PARTICLE_COLORS[6 - index]}
stroke={PARTICLE_COLORS[6 - index]}
radius={1}
/>
</Group>
)
}
这里单独把小圆拉出来写
class Circle extends Component {
render () {
let radius = this.props.radius;
let circle = Path().moveTo(0,-radius)
.arc(0,2 * radius, radius)
.arc(0,-2 * radius, radius)
.close();
return (
<AnimatedShape d={circle} {...this.props} />
)
}
}
到这基本完了,还有一个从小到大的原型在星星的后面,变化基本是从小到大,然后中间空白[要写下描边],然后消失
<Circle
radius={30}
stroke={this.state.animation.interpolate({
inputRange: [0, 5],
outputRange: ["#ce3586", "#c885ef"],
extrapolate: 'clamp'
})}
fill={this.state.animation.interpolate({
inputRange: [0, 5, 6],
outputRange: ["#ce3586", "#c885ef", "#fff"],
extrapolate: 'clamp'
})}
x={50}
y={50}
scale={this.state.animation.interpolate({
inputRange: [0, 1, 4],
outputRange: [0, .3, 1],
extrapolate: 'clamp'
})}
strokeWidth={this.state.animation.interpolate({
inputRange: [0, 5.99, 6, 7, 10],
outputRange: [0, 0, 3, 2, 0],
extrapolate: 'clamp'
})}
/>
网友评论