Босс: Вы даете мне особый эффект с прикосновением воды

внешний интерфейс JavaScript
Босс: Вы даете мне особый эффект с прикосновением воды

предисловие

Недавно босс нашел меня и сказал, что я хочу сделать несколько причудливых спецэффектов, поэтому я перечислил все сумасшедшие спецэффекты.

Позже, в моем углубленном анализе (рыбалка), я выбрал несколько более простой спецэффект: так называемая ослепляющая вода на самом деле представляет собой эффект ряби.

найти идеи

После того, как спецэффекты были установлены, босс дал мне визуализацию:

5c6a9822720e0cf345a6a7dd0d46f21fbe09aa78.jpg

Молодец, хотя у меня в коде много функциональной логики, я не могу расстаться с этим модным умением.Я написал этот спецэффект на основе реакции (также есть версия vue, которая будет добавлена ​​позже, если вы интересно посмотреть самому) , Далее следует рационализировать свои мысли, чтобы можно было подготовиться к следующей работе (рыбалке).

Прежде всего, мы знаем, что рябь будет распространяться от середины, и будет несколько рябей, накладывающихся друг на друга, а затем размер распространения может быть сгенерирован случайным образом для моделирования.

Итак, нам нужно определить количество слоев кругов ряби, минимальный и максимальный размер ряби, а также цвет ряби и другие данные.

Сначала определите конфигурацию пульсации:

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, //统计点击次数(这个后面说)
        };
    }

}

После того, как у нас есть базовая конфигурация, нам нужен метод создания ряби, как реализовать этот метод?

Чтобы подумать об этом, во-первых, вновь сгенерированная пульсация, конечно, накладывается на верхний слой предыдущей пульсации, а затем для ее генерации задается размер пульсации в случайном диапазоне, и вставляется новый объект конфигурации пульсации. пульсирующие данные.

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,
        });
};

С методом создания ряби, когда пользователь нажимает, этот метод будет вызываться для создания ряби.

document.getElementById("root").onclick = (e) => {
    this.createWave(e);
}

Затем с базовой конфигурацией ряби следующим шагом является специальный эффект ряби, который достигается с помощью CSS-анимации и обхода:

<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>
.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 строк кода):

import Wave from './Wave/app.jsx'
render() {
    return (
        <div>
            <Wave/>
        </div>
    )
}

Запустите проект и нажмите на страницу, вы увидите следующий эффект:

20210906_113826.gif

Чтобы слишком много dom не накапливалось и не занимало память, необходимо периодически очищать данные в ripple:

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

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

.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 Version:react-dark-photo

Исходный код версии Vue:vue-dark-photo

Я видел это, тебе не нравится?