随机颜色飞溅代码JS无库



我想创建一个彩色的飞溅罩,就像画家扔在纸上一样。当你看代码笔的例子时,你可以看到效果(点击左上角的飞溅(。正如你所看到的,这不是很好,因为我们看到了粒子的运动。我想让飞溅的形状更随机(有贝塞尔曲线?(,当鼠标点击时间更长时,可能会获得更大的飞溅。

我开始将代码重新构建到React TS应用程序中。我在接口中声明了我的类型。为了与React组件中的DOM元素交互,我使用refhooks API,因此ref.current保存对canvas DOM节点的引用。

然后我添加了useEffect回调,以便在DOM中直接与画布交互。

import React from "react";
import { useRef } from "react";
import { useEffect } from "react";
export interface Props {
canvas: HTMLCanvasElement;
context: CanvasRenderingContext2D;
particles: [];
x: number[];
y: number[];
}
function App(props: Props) {
//instead of getElement, I use useEffect
//props.canvas = document.getElementById("canvas");
//var ctx = props.canvas.getContext("2d");
//Adjust the scaling of canvas depending on pixel density
const getPixelRatio = (context) => {
var backingStore =
context.backingStorePixelRatio ||
context.webkitBackingStorePixelRatio ||
context.mozBackingStorePixelRatio ||
context.msBackingStorePixelRatio ||
context.oBackingStorePixelRatio ||
context.backingStorePixelRatio ||
1;
return (window.devicePixelRatio || 1) / backingStore;
};
//ref to hold a reference to the canvas DOM node
let ref = useRef();
useEffect(() => {
let canvas = ref.current;
const size = props.canvas.getBoundingClientRect();
const dpr = window.devicePixelRatio || 1;
props.canvas.height = size.height * dpr;
props.canvas.width = size.width * dpr;
let context = props.canvas.getContext("2d");
props.context.scale(dpr, dpr);
let ratio = getPixelRatio(context);
var p = props.particles[i];
props.context.beginPath();
props.context.arc(p.x, p.y, p.size, 0, Math.PI * 2, false);
props.context.fill();
});
context.fillStyle = "black";
props.particles = [];
props.canvas.onmousedown = function (e: any) {
for (var i = 0; i < 36 * 2; i++) {
props.particles.push({
x: e.clientX,
y: e.clientY,
angle: i * 5,
size: 5 + Math.random() * 3,
life: 200 + Math.random() * 50,
});
}
};
props.canvas.onmouseup = function () {
//ctx.clearRect(0, 0, 600, 600);
};
var delta = 0;
var last = Date.now();
const animate = () => {
delta = Date.now() - last;
last = Date.now();
for (var i = 0; i < props.particles.length; i++) {
var p = props.particles[i];
p.x += Math.cos(p.angle) * 4 + Math.random() * 2 - Math.random() * 2;
p.y += Math.sin(p.angle) * 4 + Math.random() * 2 - Math.random() * 2;
p.life -= delta;
p.size -= delta / 50;
if (p.size <= 0) {
p.life = 0;
}
if (p.life <= 0) {
props.particles.splice(i--, 1);
continue;
}
}
};
//randomize the color
const randomColorSet = () => {
const colorRanges = [
["#FE7E9C", "#ff80df"],
["#93f", "#ff80df"],
["#d8dadb", "#53ceef"],
["#FE7E9C", "#F7BD8D"],
];
return colorRanges[
Math.floor(Math.random() * 16777215 * colorRanges.length)
];
};
//set random color gradient
const createHex = () => {
var hexCode1 = "";
var hexValues1 = "0123456789abcdef";
for (var i = 0; i < 6; i++) {
hexCode1 += hexValues1.charAt(
Math.floor(Math.random() * hexValues1.length)
);
}
return hexCode1;
};
const generate = () => {
var deg = Math.floor(Math.random() * 360);
var gradient =
"linear-gradient(" +
deg +
"deg, " +
"#" +
createHex() +
", " +
"#" +
createHex() +
")";
document.getElementById("output").innerHTML = gradient;
document.getElementById("bg").style.background = gradient;
console.log(hexCode1, hexCode2);
};
document.onload = generate();

const render = () => {
context.fillStyle = randomColorSet;
for (var i = 0; i < props.particles.length; i++) {
if (Math.random() < 0.1) {
continue;
}
//this part already happens in my useffect
var p = props.particles[i];
context.beginPath();
context.arc(p.x, p.y, p.size, 0, Math.PI * 2, false);
context.fill();
}
};
const animloop = () => {
window.requestAnimationFrame(animloop);
animate();
render();
};
return (
<canvas
ref={ref}
id="canvas"
style={{ width: "100px", height: "100px" }}
></canvas>
);
}
export default App;

现在,即使我在任何地方添加任何as类型来测试功能,我也无法运行它?

所以,不幸的是,您的返工没有多大意义。

这里有一个基于它的版本,它到处都有严格的类型,并且有效。它将粒子逻辑实现为自定义挂钩useSplash,因此用户只需向画布提供一个ref,它就可以完成其余的操作。useIntervaluse-interval库提供,但当然也可以使用其他选项。

你可以在CodeSandbox上找到现场演示。

import React from "react";
import useInterval from "use-interval";
interface Particle {
x: number;
y: number;
size: number;
color: string;
life: number;
angle: number;
}
const colors = [
"#FE7E9C",
"#ff80df",
"#93f",
"#ff80df",
"#d8dadb",
"#53ceef",
"#FE7E9C",
"#F7BD8D"
];
function simulate(particles: readonly Particle[], delta: number): Particle[] {
const newParticles: Particle[] = [];
for (var i = 0; i < particles.length; i++) {
var p: Particle = { ...particles[i] };
p.x += Math.cos(p.angle) * 4 + Math.random() * 2 - Math.random() * 2;
p.y += Math.sin(p.angle) * 4 + Math.random() * 2 - Math.random() * 2;
p.life -= delta;
p.size -= delta / 50;
if (p.size <= 0) {
p.life = 0;
}
if (p.life > 0) {
newParticles.push(p);
}
}
return newParticles;
}
function render(
context: CanvasRenderingContext2D,
particles: readonly Particle[]
) {
for (var i = 0; i < particles.length; i++) {
if (Math.random() < 0.1) {
continue;
}
var p = particles[i];
context.fillStyle = p.color;
context.beginPath();
context.arc(p.x, p.y, p.size, 0, Math.PI * 2, false);
context.fill();
}
}
function useSplash(canvasRef: React.RefObject<HTMLCanvasElement>) {
const particlesRef = React.useRef<Particle[]>([]);
const lastTimeRef = React.useRef<number>();
const step = React.useCallback(
(timestamp: number) => {
if (!canvasRef.current) {
return;
}
const context = canvasRef.current.getContext("2d");
if (!context) {
return;
}
if (!lastTimeRef.current) {
lastTimeRef.current = timestamp;
}
const elapsed = timestamp - lastTimeRef.current;
lastTimeRef.current = timestamp;
if (particlesRef.current.length) {
particlesRef.current = simulate(particlesRef.current, elapsed);
render(context, particlesRef.current);
}
},
[canvasRef]
);
useInterval(() => {
step(Date.now());
}, 1000 / 60);
const createSplash = React.useCallback((x: number, y: number) => {
const newParticles: Particle[] = [];
const color = colors[Math.floor(Math.random() * colors.length)];
for (var i = 0; i < 36 * 2; i++) {
newParticles.push({
x,
y,
angle: i * 5,
size: 5 + Math.random() * 3,
life: 200 + Math.random() * 50,
color
});
}
particlesRef.current = [...particlesRef.current, ...newParticles];
}, []);
return { createSplash };
}
export default function App() {
const canvasRef = React.useRef<HTMLCanvasElement>(null);
const { createSplash } = useSplash(canvasRef);
return (
<div className="App">
<canvas
ref={canvasRef}
width={400}
height={400}
onMouseDown={(e) => createSplash(e.clientX, e.clientY)}
style={{ border: "1px solid orange" }}
/>
</div>
);
}

最新更新