Skip to content

Instantly share code, notes, and snippets.

@thisiscarlosgomes
Created November 14, 2022 21:47
Show Gist options
  • Save thisiscarlosgomes/11745164a088542f0af32486e3b3e3cc to your computer and use it in GitHub Desktop.
Save thisiscarlosgomes/11745164a088542f0af32486e3b3e3cc to your computer and use it in GitHub Desktop.
/* eslint-disable jsx-a11y/anchor-is-valid */
import React, { useEffect, useRef, useState } from 'react';
import useInterval from '@use-it/interval';
// Constants
const VALID_CHARS = `proliferating creative collection forefront dao tokenized community digital vibe!&@#~~@#(*:"?>)`;
const STREAM_MUTATION_ODDS = 2;
const MIN_STREAM_SIZE = 5;
const MAX_STREAM_SIZE = 40;
const MIN_INTERVAL_DELAY = 100;
const MAX_INTERVAL_DELAY = 200;
const MIN_DELAY_BETWEEN_STREAMS = 0;
const MAX_DELAY_BETWEEN_STREAMS = 12000;
const getRandInRange = (min, max) =>
Math.floor(Math.random() * (max - min)) + min;
const getRandChar = () =>
VALID_CHARS.charAt(Math.floor(Math.random() * VALID_CHARS.length));
const getRandStream = () =>
new Array(getRandInRange(MIN_STREAM_SIZE, MAX_STREAM_SIZE))
.fill()
.map(_ => getRandChar());
const getMutatedStream = stream => {
const newStream = [];
for (let i = 1; i < stream.length; i++) {
if (Math.random() < STREAM_MUTATION_ODDS) {
newStream.push(getRandChar());
} else {
newStream.push(stream[i]);
}
}
newStream.push(getRandChar());
return newStream;
};
const RainStream = props => {
const [stream, setStream] = useState(getRandStream());
const [topPadding, setTopPadding] = useState(stream.length * -20);
const [leftPadding, setLeftPadding] = useState(stream.length * -10);
const [intervalDelay, setIntervalDelay] = useState(null);
// Initialize intervalDelay
useEffect(() => {
setTimeout(() => {
setIntervalDelay(getRandInRange(MIN_INTERVAL_DELAY, MAX_INTERVAL_DELAY));
}, getRandInRange(MIN_DELAY_BETWEEN_STREAMS, MAX_DELAY_BETWEEN_STREAMS));
}, []);
useInterval(() => {
if (!props.height) return;
if (!intervalDelay) return;
// If stream is off the screen, reset it after timeout
if (topPadding > props.height) {
setStream([]);
const newStream = getRandStream();
setStream(newStream);
setTopPadding(newStream.length * -44);
setIntervalDelay(null);
setTimeout(
() =>
setIntervalDelay(
getRandInRange(MIN_INTERVAL_DELAY, MAX_INTERVAL_DELAY),
),
getRandInRange(MIN_DELAY_BETWEEN_STREAMS, MAX_DELAY_BETWEEN_STREAMS),
);
} else {
setTopPadding(topPadding + 32);
}
// setStream(stream => [...stream.slice(1, stream.length), getRandChar()]);
setStream(getMutatedStream);
}, intervalDelay);
return (
<div
style={{
fontFamily: 'matrixFont',
color: '#000',
writingMode: 'vertical-rl',
textOrientation: 'upright',
userSelect: 'none',
whiteSpace: 'nowrap',
marginLeft: leftPadding,
marginLeft: -13,
marginRight: -12,
fontSize: 12,
}}>
{stream.map((char, index) => (
<a
style={{
marginTop: -2,
// Reduce opacity for last charslet ourSubstring = "Example";stream.includes("c")
opacity: index < 6 ? 0.1 + index * 0.2 : 1,
color: index === stream.length - 1 ? '#151BFF' : undefined,
}}>
{char}
</a>
))}
</div>
);
};
const MatrixRain = props => {
const containerRef = useRef(null);
const [containerSize, setContainerSize] = useState(null); // ?{width, height}
useEffect(() => {
const boundingClientRect = containerRef.current.getBoundingClientRect();
setContainerSize({
width: boundingClientRect.width,
height: boundingClientRect.height,
});
}, []);
const streamCount = containerSize ? Math.floor(containerSize.width / 12) : 0;
return (
<div
style={{
background: '#f5f5f5',
position: 'fixed',
top: 0,
left: 0,
display: 'flex',
flexDirection: 'row',
justifyContent: 'center',
inset:'1',
width:'500px',
height:'auto',
}}
ref={containerRef}>
{new Array(streamCount).fill().map(_ => (
<RainStream height={containerSize?.height} />
))}
</div>
);
};
export default MatrixRain;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment