-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript.js
More file actions
238 lines (205 loc) · 10.7 KB
/
script.js
File metadata and controls
238 lines (205 loc) · 10.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
document.addEventListener('DOMContentLoaded', () => {
const inputContainer = document.getElementById('input-container');
const startButton = document.getElementById('startButton');
const textInput = document.getElementById('input-text'); // 텍스트 입력 필드
const fileUpload = document.getElementById('file-upload'); // 파일 업로드 필드
const canvas = document.getElementById('matrixCanvas'); // 매트릭스 효과를 위한 캔버스
const speedSlider = document.getElementById('speedSlider');
const speedValueDisplay = document.getElementById('speedValue');
if (!inputContainer || !startButton || !textInput || !fileUpload || !canvas || !speedSlider || !speedValueDisplay) {
console.error(
'필수 HTML 요소 중 일부를 찾을 수 없습니다. ID를 확인하세요: input-container, startButton, input-text, file-upload, matrixCanvas, speedSlider, speedValueDisplay'
);
return;
}
const ctx = canvas.getContext('2d');
let animationId = null;
const fontSize = 16;
let drops = [];
let currentCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ハミヒーウシナモニサワツオリアホテマケメエカキムユラセネスタヌヘヲイクコソチトノフヤヨルレロン"; // 기본 문자셋
let animationDelay = parseInt(speedSlider.value, 10);
let lastFrameTime = 0;
function setupCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
ctx.fillStyle = '#000';
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
if (speedValueDisplay) {
speedValueDisplay.textContent = animationDelay;
}
speedSlider.addEventListener('input', (event) => {
animationDelay = parseInt(event.target.value, 10);
if (speedValueDisplay) {
speedValueDisplay.textContent = animationDelay;
}
});
// 매트릭스 효과를 그리는 핵심 루프
function runMatrixDrawingLoop(timestamp) {
animationId = requestAnimationFrame(runMatrixDrawingLoop); // 다음 프레임 요청
const elapsed = timestamp - lastFrameTime;
if (elapsed >= animationDelay) {
lastFrameTime = timestamp - (elapsed % animationDelay);
// 이전 프레임의 잔상을 만들기 위해 약간 투명한 검은색으로 덮음
ctx.fillStyle = 'rgba(0, 0, 0, 0.05)'; // 이 값으로 잔상의 길이를 조절
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#2C6E49'; // 요청하신 글자 색상
ctx.font = fontSize + 'px monospace';
const columns = Math.floor(canvas.width / fontSize);
// drops 배열이 현재 컬럼 수와 다르면 (예: 초기화 안됨, 창 크기 변경 직후) 재설정
if (drops.length !== columns) {
drops = [];
for (let i = 0; i < columns; i++) {
drops[i] = 1 + Math.floor(Math.random() * (canvas.height / fontSize));
}
}
for (let i = 0; i < drops.length; i++) {
if (currentCharacters.length === 0) break;
const charIndex = Math.floor(Math.random() * currentCharacters.length);
const text = currentCharacters[charIndex];
// 세로 빈 줄 효과: 15% 확률로 문자 그리지 않음
if (Math.random() > 0.15) {
ctx.fillText(text, i * fontSize, drops[i] * fontSize);
}
// 문자가 화면 하단을 넘어가고, 랜덤 조건을 만족하면 다시 위에서 시작
if (drops[i] * fontSize > canvas.height && Math.random() > 0.975) {
drops[i] = 0;
}
drops[i]++;
}
}
}
// 매트릭스 애니메이션을 초기화하고 시작하는 함수
function startMatrixAnimation(textForMatrix) {
if (animationId) {
cancelAnimationFrame(animationId);
animationId = null;
}
if (textForMatrix && textForMatrix.trim() !== "") {
const uniqueChars = [...new Set(textForMatrix.replace(/\s/g, ''))];
if (uniqueChars.length > 0) {
currentCharacters = uniqueChars.join('');
} else {
// 입력이 공백만으로 이루어진 경우 기본값 사용 또는 짧은 문자열
currentCharacters = "01";
}
} else {
currentCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ハミヒーウシナモニサワツオリアホテマケメエカキムユラセネスタヌヘヲイクコソチトノフヤヨルレロン";
}
setupCanvas(); // 캔버스 크기 설정 및 초기화
const columns = Math.floor(canvas.width / fontSize);
drops = [];
for (let i = 0; i < columns; i++) {
// 각 열의 문자 시작 위치를 무작위로 설정하여 자연스러운 효과 연출
drops[i] = 1 + Math.floor(Math.random() * (canvas.height / fontSize));
}
lastFrameTime = performance.now(); // 애니메이션 시작 시 lastFrameTime 초기화
if (animationId) cancelAnimationFrame(animationId); // 중복 방지
animationId = requestAnimationFrame(runMatrixDrawingLoop); // 루프 시작
}
setupCanvas(); // 페이지 로드 시 캔버스 설정
window.addEventListener('resize', () => {
const wasAnimating = !!animationId;
if (animationId) {
cancelAnimationFrame(animationId);
animationId = null;
}
setupCanvas(); // 캔버스 크기 재설정 및 클리어
if (wasAnimating && inputContainer.style.display === 'none') {
const columns = Math.floor(canvas.width / fontSize);
drops = [];
for (let i = 0; i < columns; i++) {
drops[i] = 1 + Math.floor(Math.random() * (canvas.height / fontSize));
}
lastFrameTime = performance.now(); // 시간 초기화
animationId = requestAnimationFrame(runMatrixDrawingLoop); // 애니메이션 재시작
}
});
startButton.addEventListener('click', () => {
const file = fileUpload.files[0];
const textValue = textInput.value;
const executeContentAndHide = (content) => {
startMatrixAnimation(content);
inputContainer.style.display = 'none';
};
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
executeContentAndHide(e.target.result);
fileUpload.value = ''; // 파일 입력 초기화
};
reader.onerror = () => {
console.error("파일을 읽는 중 오류가 발생했습니다.");
alert("파일 읽기 오류. 텍스트 영역의 내용으로 시도합니다 (내용이 있다면).");
executeContentAndHide(textValue);
fileUpload.value = ''; // 파일 입력 초기화
};
reader.readAsText(file);
} else {
executeContentAndHide(textValue);
}
});
document.addEventListener('click', (event) => {
if (inputContainer.style.display === 'none') {
if (event.target !== startButton && !startButton.contains(event.target)) {
inputContainer.style.display = '';
if (animationId) {
cancelAnimationFrame(animationId);
animationId = null;
}
// 입력창이 나타날 때 캔버스를 검게 칠하고 메시지 표시 (선택 사항)
setupCanvas(); // 캔버스를 검은색으로 클리어
ctx.fillStyle = '#FFF';
ctx.font = '20px monospace';
ctx.textAlign = 'center';
ctx.fillText("텍스트를 입력하거나 파일을 업로드하세요", canvas.width / 2, canvas.height / 2);
ctx.textAlign = 'left'; // 기본값으로 복원
}
}
});
// 드래그 앤 드롭 기능 추가
// dragenter: 드래그된 항목이 유효한 드롭 대상 위로 들어갈 때 발생
canvas.addEventListener('dragenter', (event) => {
event.preventDefault(); // 기본 동작 방지
canvas.classList.add('drag-over');
});
// dragover: 드래그된 항목이 유효한 드롭 대상 위에 있을 때 발생 (수백 ms마다 발생)
canvas.addEventListener('dragover', (event) => {
event.preventDefault(); // 기본 동작 방지 (이것이 없으면 drop 이벤트가 발생하지 않음)
// dragenter에서 이미 추가했으므로, 여기서 또 추가할 필요는 없지만,
// 사용자가 창 외부에서 직접 특정 지점으로 드래그할 경우를 대비해 유지하거나,
// dragenter에서만 처리해도 무방합니다. 일관성을 위해 유지.
if (!canvas.classList.contains('drag-over')) {
canvas.classList.add('drag-over');
}
});
// dragleave: 드래그된 항목이 유효한 드롭 대상을 벗어날 때 발생
canvas.addEventListener('dragleave', (event) => {
event.preventDefault(); // 기본 동작 방지
canvas.classList.remove('drag-over');
});
// drop: 드래그된 항목이 유효한 드롭 대상에 놓였을 때 발생
canvas.addEventListener('drop', (event) => {
event.preventDefault(); // 기본 동작 방지 (브라우저가 파일을 여는 것을 막음)
canvas.classList.remove('drag-over');
const files = event.dataTransfer.files;
if (files.length > 0) {
// 첫 번째 .txt 파일만 처리
const file = Array.from(files).find(f => f.name.endsWith('.txt') || f.type === 'text/plain');
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
startMatrixAnimation(e.target.result); // 애니메이션 시작
inputContainer.style.display = 'none'; // 입력창 숨기기
};
reader.onerror = () => {
console.error("파일을 읽는 중 오류가 발생했습니다.");
alert("파일 읽기 오류.");
};
reader.readAsText(file);
} else {
alert(".txt 파일만 드롭해주세요.");
}
}
});
});