前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >老板:你给我来个蜻蜓点水的特效

老板:你给我来个蜻蜓点水的特效

作者头像
饼干_
发布2022-09-19 15:16:14
5190
发布2022-09-19 15:16:14
举报

theme: channing-cyan

前言

最近,老板找到我说想搞点花哨的特效,于是乎,列举了各大让人抓狂的特效。

之后在我的深入评估(摸鱼)中,选取了一个稍微简单的特效,所谓蜻蜓点水实际就是波纹特效。

寻找思路

定好特效后,老板拿了张效果图给我:

5c6a9822720e0cf345a6a7dd0d46f21fbe09aa78.jpg
5c6a9822720e0cf345a6a7dd0d46f21fbe09aa78.jpg

好家伙,虽然我码的功能性的逻辑比较多,但是这种花哨的技能我也是不能落下的,我是基于react来编写该特效(也有vue版本的,后面会放上,有兴趣自行查看),接下来该理理思路,好为接下来的工作(摸鱼)做好准备。

首先,我们知道波纹会从中间扩散开来,且会有多个波纹叠加在一起,之后扩散开来的大小可以随机生成来拟真。

所以我们需要定义纹波圈层数以及波纹的最小最大尺寸,以及波纹的颜色等数据。

先定义一下波纹的配置:

代码语言:javascript
复制
import React from "react";
class App extends React.Component {
    get waveArr() {
            const wavesConfig = { ...this.state.wavesConfig };
            let total = [];
            for (let i = 1; i <= wavesConfig.total; i++) {
                    total.push(i);
            }
            return total;
    }
    constructor(props) {
        super(props);
        this.state = {
                waves: [], // 存放波纹的数组
                wavesConfig: {
                        maxSize: 200, // px,波纹最大尺寸
                        minSize: 100, // px,波纹最小尺寸
                        zIndexCount: 999, // 波纹父元素其实z-index数值
                        waveColor: "#40b6f0", //波纹基础颜色
                        total: 5, //波纹圈层数
                },			
                clickedCount: 0, //统计点击次数(这个后面说)
        };
    }

}

有了基本配置后,我们需要一个创建波纹的方法,那这个方法该如何实现呢?

理一下思路,首先新生成的波纹当然是要在之前波纹的上层产生叠加效果,之后给定随机范围内的波纹大小让其生成,并且往波纹数据里push一个新的波纹配置对象。

代码语言:javascript
复制
createWave = (e) => {
        const wavesConfig = { ...this.state.wavesConfig };
        const { waves } = this.state;
        // 让新生成的波纹始终在之前波纹的上层产生叠加效果
        if (wavesConfig.zIndexCount > 99999) {
                wavesConfig.zIndexCount = 999;
        } else {
                wavesConfig.zIndexCount++;
        }
        // 在一定范围内随机生成波纹的大小
        const waveSize = parseInt(
                Math.random() * (wavesConfig.maxSize - wavesConfig.minSize) +
                        wavesConfig.minSize
        );
        //添加新的波纹数据
        waves.push({
                left: `${e.clientX - waveSize / 2}px`,
                top: `${e.clientY - waveSize / 2}px`,
                zIndex: wavesConfig.zIndexCount,
                width: `${waveSize}px`,
                height: `${waveSize}px`,
        });
        this.setState({
                waves,
                wavesConfig,
        });
};

有了创建波纹的方法后,当用户点击时候,将调用这个方法创建一个波纹。

代码语言:javascript
复制
document.getElementById("root").onclick = (e) => {
    this.createWave(e);
}

那么有了波纹的基本配置,接下来就是波纹的特效,利用css动画和遍历来实现:

代码语言:javascript
复制
<div>
    <div className="main-container">
        <div className="waves">
        {waves.map((w, i) => {
            return (
            <div className="wave" style={{ ...w }} key={`w_${i}`}>
                {this.waveArr.map((n) => {
                    return (
                        <div
                        className="wave-item"
                        key={`wi_${n}`}
                        style={{
                                transform: `scale(${0.1 * Math.sqrt(n - 1)})`,
                                opacity: 0.3 * (1 / n),
                                animationDelay: `${(n - 1) * 0.12}s`,
                                animationDuration: `${0.6 + n * 0.3}s`,
                                backgroundColor: wavesConfig.waveColor,
                        }}
                        />
                    );
                })}
            </div>
            );
        })}
        </div>
    </div>
</div>
代码语言:javascript
复制
.waves {
  .wave {
    position: fixed;
    pointer-events: none; // 点击事件穿透,使得鼠标点击可以穿透波纹,兼容ie11及以上
    @keyframes wave {
      to {
        //波纹逐渐扩散变大变透明
        transform: scale(1);
        opacity: 0;
      }
    }
    .wave-item {
      width: 100%;
      height: 100%;
      position: absolute;
      border-radius: 100%;
      animation: wave forwards ease-out;
    }
  }
}

将上述代码码完后,随便往一个地方引入该组件(此处略N行代码):

代码语言:javascript
复制
import Wave from './Wave/app.jsx'
render() {
    return (
        <div>
            <Wave/>
        </div>
    )
}

跑起项目并点击页面你会看到如下效果:

20210906_113826.gif
20210906_113826.gif

为了防止过多dom积累占用内存,需要定时清理波纹内的数据:

代码语言:javascript
复制
componentDidMount() {
        const { clickedCount, waves } = this.state;
        let num = clickedCount;
        document.getElementById("root").onclick = (e) => {
                num++; // 统计点击次数
                this.setState({
                        clickedCount: num,
                });
                this.createWave(e);
        };
        let lastCount = 0;
        // 2秒内无点击清空waves,防止过多的dom累积占用内存
        setInterval(() => {
                if (lastCount === clickedCount) {
                        console.log("hi");
                        console.log(clickedCount);
                        this.setState({
                                waves: [],
                        });
                }
                lastCount = clickedCount;
        }, 2000);
}

完整代码

app.jsx

代码语言:javascript
复制
import React from "react";
class App extends React.Component {
    get waveArr() {
            const wavesConfig = { ...this.state.wavesConfig };
            let total = [];
            for (let i = 1; i <= wavesConfig.total; i++) {
                    total.push(i);
            }
            return total;
    }
    constructor(props) {
        super(props);
        this.state = {
                waves: [], // 存放波纹的数组
                wavesConfig: {
                        maxSize: 200, // px,波纹最大尺寸
                        minSize: 100, // px,波纹最小尺寸
                        zIndexCount: 999, // 波纹父元素其实z-index数值
                        waveColor: "#40b6f0", //波纹基础颜色
                        total: 5, //波纹圈层数
                },			
                clickedCount: 0, //统计点击次数(这个后面说)
        };
    }
    componentDidMount() {
        const { clickedCount, waves } = this.state;
        let num = clickedCount;
        document.getElementById("root").onclick = (e) => {
                num++; // 统计点击次数
                this.setState({
                        clickedCount: num,
                });
                this.createWave(e);
        };
        let lastCount = 0;
        // 2秒内无点击清空waves,防止过多的dom累积占用内存
        setInterval(() => {
                if (lastCount === clickedCount) {
                        console.log("hi");
                        console.log(clickedCount);
                        this.setState({
                                waves: [],
                        });
                }
                lastCount = clickedCount;
        }, 2000);
    }
    createWave = (e) => {
        const wavesConfig = { ...this.state.wavesConfig };
        const { waves } = this.state;
        // 让新生成的波纹始终在之前波纹的上层产生叠加效果
        if (wavesConfig.zIndexCount > 99999) {
                wavesConfig.zIndexCount = 999;
        } else {
                wavesConfig.zIndexCount++;
        }
        // 在一定范围内随机生成波纹的大小
        const waveSize = parseInt(
                Math.random() * (wavesConfig.maxSize - wavesConfig.minSize) +
                        wavesConfig.minSize
        );
        //添加新的波纹数据
        waves.push({
                left: `${e.clientX - waveSize / 2}px`,
                top: `${e.clientY - waveSize / 2}px`,
                zIndex: wavesConfig.zIndexCount,
                width: `${waveSize}px`,
                height: `${waveSize}px`,
        });
        this.setState({
                waves,
                wavesConfig,
        });
    };
    render() {
        const { waves, wavesConfig } = this.state;
        return (
            <div>
                <div className="main-container">
                    <div className="waves">
                        {waves.map((w, i) => {
                            return (
                            <div className="wave" style={{ ...w }} key={`w_${i}`}>
                                {this.waveArr.map((n) => {
                                    return (
                                        <div
                                        className="wave-item"
                                        key={`wi_${n}`}
                                        style={{
                                                transform: `scale(${0.1 * Math.sqrt(n - 1)})`,
                                                opacity: 0.3 * (1 / n),
                                                animationDelay: `${(n - 1) * 0.12}s`,
                                                animationDuration: `${0.6 + n * 0.3}s`,
                                                backgroundColor: wavesConfig.waveColor,
                                        }}
                                        />
                                    );
                                })}
                            </div>
                            );
                        })}
                    </div>
                </div>
            </div>
        )
    }

}

app.less

代码语言:javascript
复制
.waves {
  .wave {
    position: fixed;
    pointer-events: none; // 点击事件穿透,使得鼠标点击可以穿透波纹,兼容ie11及以上
    @keyframes wave {
      to {
        //波纹逐渐扩散变大变透明
        transform: scale(1);
        opacity: 0;
      }
    }
    .wave-item {
      width: 100%;
      height: 100%;
      position: absolute;
      border-radius: 100%;
      animation: wave forwards ease-out;
    }
  }
}

最后

以上故事纯属虚构,出自作者开源项目的组件,也许不是最佳写法,欢迎提出建议。

react版源码:react-dark-photo

vue版源码:vue-dark-photo

都看到这了,不给的赞吗?

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-09-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • theme: channing-cyan
  • 前言
  • 寻找思路
  • 完整代码
    • app.jsx
      • app.less
      • 最后
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档