Skip to content

Commit 1482ce6

Browse files
author
Ryan Nixon Salim
committed
initial commit
1 parent 008e8cf commit 1482ce6

File tree

9 files changed

+424
-53
lines changed

9 files changed

+424
-53
lines changed

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
"version": "0.1.0",
44
"private": true,
55
"dependencies": {
6+
"chance": "^1.0.18",
7+
"html2canvas": "^1.0.0-rc.1",
68
"react": "^16.8.6",
79
"react-dom": "^16.8.6",
10+
"react-pose": "^4.0.8",
811
"react-scripts": "3.0.0"
912
},
1013
"scripts": {

src/App.css

Lines changed: 0 additions & 33 deletions
This file was deleted.

src/App.js

Lines changed: 133 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,140 @@
1-
import React from 'react';
2-
import logo from './logo.svg';
3-
import './App.css';
1+
import React, { useRef, useState, createRef } from 'react';
2+
3+
import spiderman from './asset/purepng.webp';
4+
import html2canvas from 'html2canvas';
5+
import Chance from 'chance';
6+
import posed from 'react-pose';
7+
const chance = new Chance();
8+
9+
const canvasCount = 30;
10+
const imageDataArray = [];
11+
12+
const Dust = posed.canvas({
13+
visible: { opacity: 1, transition: '0.5s', filter: `blur(0px)` },
14+
hidden: {
15+
opacity: 0,
16+
y: (props) => props.y,
17+
x: (props) => props.x,
18+
rotate: (props) => props.rotate,
19+
transition: { duration: 2000 },
20+
filter: `blur(2px)`,
21+
},
22+
});
23+
const CanvasContainer = posed.div({
24+
hidden: { delayChildren: 200, staggerChildren: 35, filter: `blur(1px)` },
25+
});
26+
27+
const OriginalElement = posed.img({
28+
visible: {
29+
opacity: 1,
30+
},
31+
hidden: {
32+
opacity: 0,
33+
transition: { duration: 100 },
34+
filter: `blur(1px)`,
35+
},
36+
});
37+
38+
function createBlankImageData(imageData) {
39+
for (let i = 0; i < canvasCount; i++) {
40+
let arr = new Uint8ClampedArray(imageData.data);
41+
for (let j = 0; j < arr.length; j++) {
42+
arr[j] = 0;
43+
}
44+
imageDataArray.push(arr);
45+
}
46+
}
47+
48+
function weightedRandomDistrib(peak) {
49+
var prob = [],
50+
seq = [];
51+
for (let i = 0; i < canvasCount; i++) {
52+
prob.push(Math.pow(canvasCount - Math.abs(peak - i), 3));
53+
seq.push(i);
54+
}
55+
return chance.weighted(seq, prob);
56+
}
57+
58+
function handleSnap(imgRef, particleRefs, setState, state) {
59+
if (state === 'hidden') {
60+
setState('visible');
61+
return;
62+
}
63+
html2canvas(imgRef.current, { scale: 1 }).then((canvas) => {
64+
const w = canvas.width;
65+
const h = canvas.height;
66+
// targetRef.current.appendChild(canvas);
67+
const ctx = canvas.getContext('2d');
68+
const data = ctx.getImageData(0, 0, w, h);
69+
var pixelArr = data.data;
70+
createBlankImageData(data);
71+
72+
for (let i = 0; i <= pixelArr.length; i += 4) {
73+
//find the highest probability canvas the pixel should be in
74+
let p = Math.floor((i / pixelArr.length) * canvasCount);
75+
let a = imageDataArray[weightedRandomDistrib(p)];
76+
a[i] = pixelArr[i];
77+
a[i + 1] = pixelArr[i + 1];
78+
a[i + 2] = pixelArr[i + 2];
79+
a[i + 3] = pixelArr[i + 3];
80+
}
81+
82+
particleRefs.current.map((ref, idx) => {
83+
const ctx2 = ref.current.getContext('2d');
84+
ref.current.width = w;
85+
ref.current.height = h;
86+
ctx2.putImageData(new ImageData(imageDataArray[idx], w, h), 0, 0);
87+
});
88+
});
89+
setTimeout(() => {
90+
setState('hidden');
91+
}, 1000);
92+
}
93+
94+
function generateBlankCanvas(refs, state, srcRef) {
95+
const canvases = [];
96+
console.log(refs);
97+
98+
for (let a = 0; a < canvasCount; a++) {
99+
const canvasStyle = {
100+
position: 'fixed',
101+
top: 200,
102+
left: 10,
103+
};
104+
105+
canvases.push(
106+
<Dust
107+
ref={refs.current[a]}
108+
key={a}
109+
style={canvasStyle}
110+
pose={state}
111+
x={150}
112+
y={-150}
113+
rotate={chance.integer({ min: -20, max: 20 })}
114+
/>
115+
);
116+
}
117+
return canvases;
118+
}
4119

5120
function App() {
121+
const imgRef = useRef();
122+
const [state, setState] = useState('visible');
123+
const particleRefs = useRef([...Array(canvasCount)].map(() => createRef()));
124+
125+
const canvases = generateBlankCanvas(particleRefs, state, imgRef);
126+
6127
return (
7128
<div className="App">
8-
<header className="App-header">
9-
<img src={logo} className="App-logo" alt="logo" />
10-
<p>
11-
Edit <code>src/App.js</code> and save to reload.
12-
</p>
13-
<a
14-
className="App-link"
15-
href="https://reactjs.org"
16-
target="_blank"
17-
rel="noopener noreferrer"
18-
>
19-
Learn React
20-
</a>
21-
</header>
129+
<OriginalElement
130+
pose={state}
131+
src={spiderman}
132+
width={'250px'}
133+
ref={imgRef}
134+
style={{ position: 'fixed', top: 200, left: 10, zIndex: 1 }}
135+
/>
136+
<button onClick={() => handleSnap(imgRef, particleRefs, setState, state)}> Snap</button>
137+
<CanvasContainer pose={state}>{canvases}</CanvasContainer>
22138
</div>
23139
);
24140
}

src/asset/purepng.webp

779 KB
Loading

src/asset/spiderman.png

1.34 MB
Loading

src/components/Dust.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import React from 'react';
2+
import posed from 'react-pose';
3+
4+
const DustCanvas = posed.canvas({
5+
visible: { opacity: 1, transition: '0.5s', filter: `blur(0px)` },
6+
hidden: {
7+
opacity: 0,
8+
y: (props) => props.y,
9+
x: (props) => props.x,
10+
rotate: (props) => props.rotate,
11+
transition: { duration: 2000 },
12+
filter: `blur(2px)`,
13+
},
14+
});
15+
16+
function Dust(props) {
17+
return <DustCanvas {...props} />;
18+
}
19+
20+
export default Dust;

src/components/InfinityGauntlet.js

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import React, { useRef, useState, createRef } from 'react';
2+
3+
import spiderman from './asset/purepng.webp';
4+
import html2canvas from 'html2canvas';
5+
6+
import posed from 'react-pose';
7+
import { weightedRandomDistrib, createBlankImageData, generateBlankCanvas } from '../helper/Util';
8+
9+
const canvasCount = 30;
10+
let imageDataArray = [];
11+
12+
const CanvasContainer = posed.div({
13+
hidden: { delayChildren: 200, staggerChildren: 35, filter: `blur(1px)` },
14+
});
15+
16+
const OriginalElement = posed.div({
17+
visible: {
18+
opacity: 1,
19+
},
20+
hidden: {
21+
opacity: 0,
22+
transition: { duration: 100 },
23+
filter: `blur(1px)`,
24+
},
25+
});
26+
27+
function handleSnap(imgRef, particleRefs, setState, state) {
28+
if (state === 'hidden') {
29+
setState('visible');
30+
return;
31+
}
32+
html2canvas(imgRef.current, { scale: 1 }).then((canvas) => {
33+
const w = canvas.width;
34+
const h = canvas.height;
35+
// targetRef.current.appendChild(canvas);
36+
const ctx = canvas.getContext('2d');
37+
const data = ctx.getImageData(0, 0, w, h);
38+
var pixelArr = data.data;
39+
imageDataArray = createBlankImageData(data);
40+
41+
for (let i = 0; i <= pixelArr.length; i += 4) {
42+
//find the highest probability canvas the pixel should be in
43+
let p = Math.floor((i / pixelArr.length) * canvasCount);
44+
let a = imageDataArray[weightedRandomDistrib(p, canvasCount)];
45+
a[i] = pixelArr[i];
46+
a[i + 1] = pixelArr[i + 1];
47+
a[i + 2] = pixelArr[i + 2];
48+
a[i + 3] = pixelArr[i + 3];
49+
}
50+
51+
particleRefs.current.map((ref, idx) => {
52+
const ctx2 = ref.current.getContext('2d');
53+
ref.current.width = w;
54+
ref.current.height = h;
55+
ctx2.putImageData(new ImageData(imageDataArray[idx], w, h), 0, 0);
56+
});
57+
});
58+
setTimeout(() => {
59+
setState('hidden');
60+
}, 1000);
61+
}
62+
63+
function InfinityGauntlet(props) {
64+
const wrapperRef = useRef();
65+
const [state, setState] = useState('visible');
66+
const particleRefs = useRef([...Array(canvasCount)].map(() => createRef()));
67+
const canvases = generateBlankCanvas(particleRefs, state, canvasCount);
68+
if (props.snap) {
69+
handleSnap(wrapperRef, particleRefs, setState, state);
70+
}
71+
return (
72+
<div style={{ position: 'relative' }}>
73+
<OriginalElement pose={state} src={spiderman} ref={wrapperRef} style={{ position: 'absolute', zIndex: 1 }}>
74+
{props.children}
75+
</OriginalElement>
76+
<CanvasContainer pose={state}>{canvases}</CanvasContainer>
77+
</div>
78+
);
79+
}
80+
81+
export default InfinityGauntlet;

src/helper/Util.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import React from 'react';
2+
import Chance from 'chance';
3+
import Dust from '../components/Dust';
4+
const chance = new Chance();
5+
6+
export function weightedRandomDistrib(peak, canvasCount) {
7+
var prob = [],
8+
seq = [];
9+
for (let i = 0; i < canvasCount; i++) {
10+
prob.push(Math.pow(canvasCount - Math.abs(peak - i), 3));
11+
seq.push(i);
12+
}
13+
return chance.weighted(seq, prob);
14+
}
15+
16+
export function createBlankImageData(imageData, canvasCount) {
17+
const imageDataArray = [];
18+
for (let i = 0; i < canvasCount; i++) {
19+
let arr = new Uint8ClampedArray(imageData.data);
20+
for (let j = 0; j < arr.length; j++) {
21+
arr[j] = 0;
22+
}
23+
imageDataArray.push(arr);
24+
}
25+
return imageDataArray;
26+
}
27+
28+
export function generateBlankCanvas(refs, state, canvasCount) {
29+
const canvases = [];
30+
for (let a = 0; a < canvasCount; a++) {
31+
const canvasStyle = {
32+
position: 'abtolute',
33+
};
34+
35+
const dustProps = {
36+
ref: refs.current[a],
37+
key: a,
38+
style: canvasStyle,
39+
pose: state,
40+
x: 75,
41+
y: -75,
42+
rotate: chance.integer({ min: -20, max: 20 }),
43+
};
44+
canvases.push(<Dust {...dustProps} />);
45+
}
46+
return canvases;
47+
}

0 commit comments

Comments
 (0)