React Native twitter收藏动画

参考文章: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,即图形左上角的坐标。

谜之蹩脚的数学插入:

QQ20161021-2@2x.png

假设左上角坐标为(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'
    })}
/>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容