предисловие
Недавно босс нашел меня и сказал, что я хочу сделать несколько причудливых спецэффектов, поэтому я перечислил все сумасшедшие спецэффекты.
Позже, в моем углубленном анализе (рыбалка), я выбрал несколько более простой спецэффект: так называемая ослепляющая вода на самом деле представляет собой эффект ряби.
найти идеи
После того, как спецэффекты были установлены, босс дал мне визуализацию:
Молодец, хотя у меня в коде много функциональной логики, я не могу расстаться с этим модным умением.Я написал этот спецэффект на основе реакции (также есть версия 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>
)
}
Запустите проект и нажмите на страницу, вы увидите следующий эффект:
Чтобы слишком много 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
Я видел это, тебе не нравится?