optimization of wasm and component v2
This commit is contained in:
@@ -28,23 +28,6 @@ export const NetrunnerMatrix: React.FC<NetrunnerMatrixProps> = ({
|
|||||||
const { resolvedTheme } = useTheme();
|
const { resolvedTheme } = useTheme();
|
||||||
const isDarkMode = resolvedTheme === "dark";
|
const isDarkMode = resolvedTheme === "dark";
|
||||||
|
|
||||||
// УМНОЕ МАСШТАБИРОВАНИЕ ДЛЯ ТЕЛЕФОНОВ
|
|
||||||
const applyCanvasScaling = (
|
|
||||||
canvas: HTMLCanvasElement,
|
|
||||||
container: HTMLDivElement,
|
|
||||||
) => {
|
|
||||||
const dpr = window.devicePixelRatio || 1;
|
|
||||||
// На мобилках сильно режем разрешение для турбо-производительности
|
|
||||||
const scale = dpr > 1 ? 0.6 : 1.0;
|
|
||||||
|
|
||||||
canvas.width = container.clientWidth * scale;
|
|
||||||
canvas.height = container.clientHeight * scale;
|
|
||||||
|
|
||||||
// CSS растягивает канвас на весь экран
|
|
||||||
canvas.style.width = `${container.clientWidth}px`;
|
|
||||||
canvas.style.height = `${container.clientHeight}px`;
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const canvas = canvasRef.current;
|
const canvas = canvasRef.current;
|
||||||
const container = containerRef.current;
|
const container = containerRef.current;
|
||||||
@@ -58,19 +41,30 @@ export const NetrunnerMatrix: React.FC<NetrunnerMatrixProps> = ({
|
|||||||
);
|
);
|
||||||
await wasmModule.default("/wasm-matrix/matrix_engine_bg.wasm");
|
await wasmModule.default("/wasm-matrix/matrix_engine_bg.wasm");
|
||||||
|
|
||||||
// Применяем масштаб ПЕРЕД инициализацией движка
|
// 1:1 Разрешение без мыльного скейлинга
|
||||||
applyCanvasScaling(canvas, container);
|
canvas.width = container.clientWidth;
|
||||||
|
canvas.height = container.clientHeight;
|
||||||
|
|
||||||
const engine = new wasmModule.MatrixEngine(canvas, 16);
|
const engine = new wasmModule.MatrixEngine(canvas, 16);
|
||||||
engineRef.current = engine;
|
engineRef.current = engine;
|
||||||
engine.set_theme(isSecure, isDarkMode);
|
engine.set_theme(isSecure, isDarkMode);
|
||||||
setWasmLoaded(true);
|
setWasmLoaded(true);
|
||||||
|
|
||||||
const render = () => {
|
// ОПТИМИЗАЦИЯ: Ограничиваем до 35 FPS для устранения лагов
|
||||||
engine.tick();
|
let lastTime = 0;
|
||||||
|
const fpsInterval = 1000 / 35; // ~28ms на кадр
|
||||||
|
|
||||||
|
const render = (time: number) => {
|
||||||
animationFrameId.current = requestAnimationFrame(render);
|
animationFrameId.current = requestAnimationFrame(render);
|
||||||
|
const elapsed = time - lastTime;
|
||||||
|
|
||||||
|
if (elapsed > fpsInterval) {
|
||||||
|
lastTime = time - (elapsed % fpsInterval);
|
||||||
|
engine.tick();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
render();
|
|
||||||
|
requestAnimationFrame(render);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Wasm Engine Load Failed:", e);
|
console.error("Wasm Engine Load Failed:", e);
|
||||||
}
|
}
|
||||||
@@ -80,7 +74,8 @@ export const NetrunnerMatrix: React.FC<NetrunnerMatrixProps> = ({
|
|||||||
|
|
||||||
const handleResize = () => {
|
const handleResize = () => {
|
||||||
if (canvas && container && engineRef.current) {
|
if (canvas && container && engineRef.current) {
|
||||||
applyCanvasScaling(canvas, container);
|
canvas.width = container.clientWidth;
|
||||||
|
canvas.height = container.clientHeight;
|
||||||
engineRef.current.resize(canvas.width, canvas.height);
|
engineRef.current.resize(canvas.width, canvas.height);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -97,40 +92,53 @@ export const NetrunnerMatrix: React.FC<NetrunnerMatrixProps> = ({
|
|||||||
if (engineRef.current) engineRef.current.set_theme(isSecure, isDarkMode);
|
if (engineRef.current) engineRef.current.set_theme(isSecure, isDarkMode);
|
||||||
}, [isSecure, isDarkMode]);
|
}, [isSecure, isDarkMode]);
|
||||||
|
|
||||||
// ГЛОБАЛЬНЫЙ ПЕРЕХВАТ СОБЫТИЙ (POINTER EVENTS - работает для Мыши и Тача)
|
// ГЛОБАЛЬНЫЙ ПЕРЕХВАТ (Убраны костыли со скейлингом)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handlePointerDown = (e: PointerEvent) => {
|
const triggerAction = (clientX: number, clientY: number) => {
|
||||||
if (!canvasRef.current || !engineRef.current) return;
|
if (!canvasRef.current || !engineRef.current) return;
|
||||||
const rect = canvasRef.current.getBoundingClientRect();
|
const rect = canvasRef.current.getBoundingClientRect();
|
||||||
const scale = canvasRef.current.width / rect.width; // Корректируем координаты из-за скейлинга
|
const x = clientX - rect.left;
|
||||||
const x = (e.clientX - rect.left) * scale;
|
const y = clientY - rect.top;
|
||||||
const y = (e.clientY - rect.top) * scale;
|
|
||||||
|
|
||||||
if (y < 0 || y > canvasRef.current.height) return;
|
if (y < 0 || y > rect.height) return;
|
||||||
|
|
||||||
engineRef.current.set_drawing(true);
|
engineRef.current.set_drawing(true);
|
||||||
engineRef.current.trigger_glitch(x, y);
|
engineRef.current.trigger_glitch(x, y);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePointerMove = (e: PointerEvent) => {
|
const moveAction = (clientX: number, clientY: number) => {
|
||||||
if (!canvasRef.current || !engineRef.current) return;
|
if (!canvasRef.current || !engineRef.current) return;
|
||||||
const rect = canvasRef.current.getBoundingClientRect();
|
const rect = canvasRef.current.getBoundingClientRect();
|
||||||
const scale = canvasRef.current.width / rect.width;
|
engineRef.current.update_mouse(clientX - rect.left, clientY - rect.top);
|
||||||
const x = (e.clientX - rect.left) * scale;
|
|
||||||
const y = (e.clientY - rect.top) * scale;
|
|
||||||
engineRef.current.update_mouse(x, y);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handlePointerDown = (e: PointerEvent) =>
|
||||||
|
triggerAction(e.clientX, e.clientY);
|
||||||
|
const handlePointerMove = (e: PointerEvent) =>
|
||||||
|
moveAction(e.clientX, e.clientY);
|
||||||
const handlePointerUp = () => engineRef.current?.set_drawing(false);
|
const handlePointerUp = () => engineRef.current?.set_drawing(false);
|
||||||
|
|
||||||
|
// Поддержка пальцев на мобилках
|
||||||
|
const handleTouchStart = (e: TouchEvent) =>
|
||||||
|
triggerAction(e.touches[0].clientX, e.touches[0].clientY);
|
||||||
|
const handleTouchMove = (e: TouchEvent) =>
|
||||||
|
moveAction(e.touches[0].clientX, e.touches[0].clientY);
|
||||||
|
|
||||||
window.addEventListener("pointerdown", handlePointerDown);
|
window.addEventListener("pointerdown", handlePointerDown);
|
||||||
window.addEventListener("pointermove", handlePointerMove);
|
window.addEventListener("pointermove", handlePointerMove);
|
||||||
window.addEventListener("pointerup", handlePointerUp);
|
window.addEventListener("pointerup", handlePointerUp);
|
||||||
|
|
||||||
|
window.addEventListener("touchstart", handleTouchStart, { passive: true });
|
||||||
|
window.addEventListener("touchmove", handleTouchMove, { passive: true });
|
||||||
|
window.addEventListener("touchend", handlePointerUp);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener("pointerdown", handlePointerDown);
|
window.removeEventListener("pointerdown", handlePointerDown);
|
||||||
window.removeEventListener("pointermove", handlePointerMove);
|
window.removeEventListener("pointermove", handlePointerMove);
|
||||||
window.removeEventListener("pointerup", handlePointerUp);
|
window.removeEventListener("pointerup", handlePointerUp);
|
||||||
|
window.removeEventListener("touchstart", handleTouchStart);
|
||||||
|
window.removeEventListener("touchmove", handleTouchMove);
|
||||||
|
window.removeEventListener("touchend", handlePointerUp);
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -138,8 +146,8 @@ export const NetrunnerMatrix: React.FC<NetrunnerMatrixProps> = ({
|
|||||||
<div ref={containerRef} className="absolute inset-0 overflow-hidden z-0">
|
<div ref={containerRef} className="absolute inset-0 overflow-hidden z-0">
|
||||||
<canvas
|
<canvas
|
||||||
ref={canvasRef}
|
ref={canvasRef}
|
||||||
className={`transition-opacity duration-1000 ${wasmLoaded ? "opacity-100" : "opacity-0"}`}
|
className={`w-full h-full transition-opacity duration-1000 ${wasmLoaded ? "opacity-100" : "opacity-0"}`}
|
||||||
style={{ imageRendering: "pixelated" }} // Делает апскейл четким, без "мыла"
|
// Убрали imageRendering: pixelated, чтобы текст был гладким
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
+69
-58
@@ -21,24 +21,24 @@ struct GlitchBlock {
|
|||||||
impl GlitchBlock {
|
impl GlitchBlock {
|
||||||
fn new(x: f32, y: f32, r: u8, g: u8, b: u8, bg_color: &str) -> Self {
|
fn new(x: f32, y: f32, r: u8, g: u8, b: u8, bg_color: &str) -> Self {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let w = rng.gen_range(20.0..250.0);
|
let w = rng.gen_range(20.0..150.0); // Сделал глитчи чуть компактнее для телефонов
|
||||||
let h = rng.gen_range(5.0..50.0);
|
let h = rng.gen_range(5.0..40.0);
|
||||||
|
|
||||||
let choice = rng.gen_range(0..100);
|
let choice = rng.gen_range(0..100);
|
||||||
let color = match choice {
|
let color = match choice {
|
||||||
0..=15 => bg_color.to_string(),
|
0..=15 => bg_color.to_string(),
|
||||||
16..=50 => format!("rgba({}, {}, {}, 0.9)", r, g, b),
|
16..=50 => format!("rgb({}, {}, {})", r, g, b), // Убрал альфу
|
||||||
51..=75 => format!("rgba({}, {}, {}, 0.7)", 255 - r, 255 - g, 255 - b),
|
51..=75 => format!("rgb({}, {}, {})", 255 - r, 255 - g, 255 - b),
|
||||||
76..=95 => "rgba(255, 255, 255, 0.95)".to_string(),
|
76..=95 => "rgb(255, 255, 255)".to_string(),
|
||||||
_ => "rgba(255, 20, 80, 0.95)".to_string(),
|
_ => "rgb(255, 20, 80)".to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
x: x - w / 2.0 + rng.gen_range(-120.0..120.0),
|
x: x - w / 2.0 + rng.gen_range(-100.0..100.0),
|
||||||
y: y - h / 2.0 + rng.gen_range(-120.0..120.0),
|
y: y - h / 2.0 + rng.gen_range(-100.0..100.0),
|
||||||
w,
|
w,
|
||||||
h,
|
h,
|
||||||
life: rng.gen_range(5.0..25.0),
|
life: rng.gen_range(5.0..20.0),
|
||||||
color,
|
color,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,19 +58,19 @@ impl GlassSymbol {
|
|||||||
fn new(x: f32, y: f32, charset: &[char], is_vpn_on: bool, is_dark_mode: bool) -> Self {
|
fn new(x: f32, y: f32, charset: &[char], is_vpn_on: bool, is_dark_mode: bool) -> Self {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let color = match (is_vpn_on, is_dark_mode) {
|
let color = match (is_vpn_on, is_dark_mode) {
|
||||||
(true, true) => "139, 61, 255",
|
(true, true) => "rgb(139, 61, 255)",
|
||||||
(false, true) => "0, 229, 242",
|
(false, true) => "rgb(0, 229, 242)",
|
||||||
(true, false) => "90, 30, 200",
|
(true, false) => "rgb(90, 30, 200)",
|
||||||
(false, false) => "0, 140, 160",
|
(false, false) => "rgb(0, 140, 160)",
|
||||||
};
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
x: x + rng.gen_range(-15.0..15.0),
|
x: x + rng.gen_range(-10.0..10.0),
|
||||||
y: y + rng.gen_range(-15.0..15.0),
|
y: y + rng.gen_range(-10.0..10.0),
|
||||||
value: charset[rng.gen_range(0..charset.len())],
|
value: charset[rng.gen_range(0..charset.len())],
|
||||||
life: 1.0,
|
life: 1.0,
|
||||||
max_life: rng.gen_range(30.0..80.0),
|
max_life: rng.gen_range(20.0..50.0), // Быстрее исчезают
|
||||||
velocity_y: rng.gen_range(0.1..0.5),
|
velocity_y: rng.gen_range(0.2..0.8),
|
||||||
color: color.to_string(),
|
color: color.to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -118,7 +118,7 @@ impl MatrixEngine {
|
|||||||
for _ in 0..columns {
|
for _ in 0..columns {
|
||||||
drops.push(Drop {
|
drops.push(Drop {
|
||||||
y: rng.gen_range(-20.0..0.0),
|
y: rng.gen_range(-20.0..0.0),
|
||||||
speed: rng.gen_range(0.15..0.45),
|
speed: rng.gen_range(0.2..0.5),
|
||||||
glitch_timer: 0.0,
|
glitch_timer: 0.0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -155,7 +155,7 @@ impl MatrixEngine {
|
|||||||
for _ in 0..columns {
|
for _ in 0..columns {
|
||||||
self.drops.push(Drop {
|
self.drops.push(Drop {
|
||||||
y: rng.gen_range(-20.0..height as f32),
|
y: rng.gen_range(-20.0..height as f32),
|
||||||
speed: rng.gen_range(0.15..0.45),
|
speed: rng.gen_range(0.2..0.5),
|
||||||
glitch_timer: 0.0,
|
glitch_timer: 0.0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -174,12 +174,12 @@ impl MatrixEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn trigger_glitch(&mut self, x: f32, y: f32) {
|
pub fn trigger_glitch(&mut self, x: f32, y: f32) {
|
||||||
self.emp_timer = 60.0;
|
self.emp_timer = 40.0;
|
||||||
self.emp_x = x;
|
self.emp_x = x;
|
||||||
self.emp_y = y;
|
self.emp_y = y;
|
||||||
let (r, g, b) = self.get_theme_colors();
|
let (r, g, b) = self.get_theme_colors();
|
||||||
let bg = self.get_bg_color();
|
let bg = self.get_bg_color_rgb();
|
||||||
for _ in 0..25 {
|
for _ in 0..15 {
|
||||||
self.glitch_blocks
|
self.glitch_blocks
|
||||||
.push(GlitchBlock::new(x, y, r, g, b, &bg));
|
.push(GlitchBlock::new(x, y, r, g, b, &bg));
|
||||||
}
|
}
|
||||||
@@ -194,11 +194,11 @@ impl MatrixEngine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_bg_color(&self) -> String {
|
fn get_bg_color_rgb(&self) -> String {
|
||||||
if self.is_dark_mode {
|
if self.is_dark_mode {
|
||||||
"rgba(10, 10, 12, 1.0)".to_string()
|
"rgb(10, 10, 12)".to_string()
|
||||||
} else {
|
} else {
|
||||||
"rgba(250, 250, 252, 1.0)".to_string()
|
"rgb(250, 250, 252)".to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,23 +206,25 @@ impl MatrixEngine {
|
|||||||
self.tick_count = self.tick_count.wrapping_add(1);
|
self.tick_count = self.tick_count.wrapping_add(1);
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
|
|
||||||
|
// 1. Очистка фона (единственная альфа, которая нам нужна)
|
||||||
|
self.ctx.set_global_alpha(0.25);
|
||||||
let bg_fill = if self.is_dark_mode {
|
let bg_fill = if self.is_dark_mode {
|
||||||
"rgba(10, 10, 12, 0.2)"
|
"rgb(10, 10, 12)"
|
||||||
} else {
|
} else {
|
||||||
"rgba(250, 250, 252, 0.2)"
|
"rgb(250, 250, 252)"
|
||||||
};
|
};
|
||||||
self.ctx.set_fill_style_str(bg_fill);
|
self.ctx.set_fill_style_str(bg_fill);
|
||||||
self.ctx
|
self.ctx
|
||||||
.fill_rect(0.0, 0.0, self.width as f64, self.height as f64);
|
.fill_rect(0.0, 0.0, self.width as f64, self.height as f64);
|
||||||
|
|
||||||
self.ctx
|
self.ctx
|
||||||
.set_font(&format!("bold {}px monospace", self.font_size));
|
.set_font(&format!("bold {}px monospace", self.font_size));
|
||||||
|
|
||||||
|
// Кешируем базовые строки цветов на весь кадр (ZERO ALLOCATIONS IN LOOP)
|
||||||
let (r, g, b) = self.get_theme_colors();
|
let (r, g, b) = self.get_theme_colors();
|
||||||
let bg_solid = self.get_bg_color();
|
let color_base = format!("rgb({}, {}, {})", r, g, b);
|
||||||
|
let color_head = "rgb(255, 255, 255)";
|
||||||
// ОПТИМИЗАЦИЯ ПАМЯТИ: Кешируем строки базовых цветов один раз на кадр!
|
let bg_solid = self.get_bg_color_rgb();
|
||||||
let color_base = format!("rgba({}, {}, {}, 0.6)", r, g, b);
|
|
||||||
let color_head = "rgba(255, 255, 255, 1.0)".to_string();
|
|
||||||
|
|
||||||
if rng.gen_bool(0.02) {
|
if rng.gen_bool(0.02) {
|
||||||
let rx = rng.gen_range(0.0..self.width as f32);
|
let rx = rng.gen_range(0.0..self.width as f32);
|
||||||
@@ -233,7 +235,7 @@ impl MatrixEngine {
|
|||||||
|
|
||||||
if rng.gen_bool(0.005) {
|
if rng.gen_bool(0.005) {
|
||||||
self.tear_y = rng.gen_range(0.0..self.height as f32);
|
self.tear_y = rng.gen_range(0.0..self.height as f32);
|
||||||
self.tear_timer = rng.gen_range(5.0..15.0);
|
self.tear_timer = rng.gen_range(5.0..10.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.emp_timer > 0.0 {
|
if self.emp_timer > 0.0 {
|
||||||
@@ -243,6 +245,7 @@ impl MatrixEngine {
|
|||||||
self.tear_timer -= 1.0;
|
self.tear_timer -= 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- ГОРЯЧИЙ ЦИКЛ (МАКСИМАЛЬНО ОБЛЕГЧЕН) ---
|
||||||
for i in 0..self.drops.len() {
|
for i in 0..self.drops.len() {
|
||||||
let drop = &mut self.drops[i];
|
let drop = &mut self.drops[i];
|
||||||
let mut x = (i as f32) * (self.font_size as f32);
|
let mut x = (i as f32) * (self.font_size as f32);
|
||||||
@@ -261,13 +264,13 @@ impl MatrixEngine {
|
|||||||
if self.emp_timer > 0.0 {
|
if self.emp_timer > 0.0 {
|
||||||
let dx = x - self.emp_x;
|
let dx = x - self.emp_x;
|
||||||
let dy = y - self.emp_y;
|
let dy = y - self.emp_y;
|
||||||
let dist_sq = dx * dx + dy * dy; // ОПТИМИЗАЦИЯ: Убрали .sqrt()
|
let dist_sq = dx * dx + dy * dy;
|
||||||
|
|
||||||
let emp_radius = (60.0 - self.emp_timer) * 15.0;
|
let emp_radius = (40.0 - self.emp_timer) * 15.0;
|
||||||
let emp_radius_sq = emp_radius * emp_radius;
|
let emp_radius_sq = emp_radius * emp_radius;
|
||||||
|
|
||||||
if dist_sq < emp_radius_sq {
|
if dist_sq < emp_radius_sq {
|
||||||
let ring_inner = emp_radius - 20.0;
|
let ring_inner = emp_radius - 30.0;
|
||||||
let scramble_inner = emp_radius - 80.0;
|
let scramble_inner = emp_radius - 80.0;
|
||||||
|
|
||||||
if dist_sq > ring_inner * ring_inner {
|
if dist_sq > ring_inner * ring_inner {
|
||||||
@@ -289,38 +292,39 @@ impl MatrixEngine {
|
|||||||
drop.glitch_timer = 5.0;
|
drop.glitch_timer = 5.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let time_shift = self.tick_count / 10;
|
|
||||||
let hash = (i as u32)
|
let hash = (i as u32)
|
||||||
.wrapping_mul(73856093)
|
.wrapping_mul(73856093)
|
||||||
.wrapping_add((cell_y as u32).wrapping_mul(19349663))
|
.wrapping_add((cell_y as u32).wrapping_mul(19349663))
|
||||||
.wrapping_add(time_shift);
|
.wrapping_add(self.tick_count / 8);
|
||||||
let mut char_index = (hash as usize) % self.charset.len();
|
let mut char_index = (hash as usize) % self.charset.len();
|
||||||
let is_head = rng.gen_bool(0.15);
|
|
||||||
|
|
||||||
// ОПТИМИЗАЦИЯ: В 95% случаев мы используем ссылки на заранее созданные строки, не забивая память!
|
let is_head = rng.gen_bool(0.12);
|
||||||
let mut custom_color = String::new();
|
|
||||||
let color_ref = if is_emp_ring {
|
// ОПРЕДЕЛЯЕМ ПРОЗРАЧНОСТЬ И ЦВЕТ БЕЗ FORMAT!
|
||||||
&color_head
|
let (target_alpha, target_color) = if is_emp_ring {
|
||||||
|
char_index = rng.gen_range(0..self.charset.len());
|
||||||
|
(1.0, color_head)
|
||||||
} else if is_scrambled || drop.glitch_timer > 0.0 {
|
} else if is_scrambled || drop.glitch_timer > 0.0 {
|
||||||
char_index = rng.gen_range(0..self.charset.len());
|
char_index = rng.gen_range(0..self.charset.len());
|
||||||
let alpha = rng.gen_range(0.6..1.0);
|
|
||||||
if rng.gen_bool(0.2) {
|
|
||||||
custom_color = format!("rgba(255, 255, 255, {})", alpha);
|
|
||||||
} else {
|
|
||||||
custom_color = format!("rgba({}, {}, {}, {})", r, g, b, alpha);
|
|
||||||
}
|
|
||||||
if drop.glitch_timer > 0.0 {
|
if drop.glitch_timer > 0.0 {
|
||||||
drop.glitch_timer -= 1.0;
|
drop.glitch_timer -= 1.0;
|
||||||
}
|
}
|
||||||
&custom_color
|
|
||||||
} else if is_head {
|
if rng.gen_bool(0.3) {
|
||||||
&color_head
|
(0.9, color_head)
|
||||||
} else {
|
} else {
|
||||||
&color_base
|
(0.8, color_base.as_str())
|
||||||
|
}
|
||||||
|
} else if is_head {
|
||||||
|
(1.0, color_head)
|
||||||
|
} else {
|
||||||
|
(0.6, color_base.as_str())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.ctx.set_global_alpha(target_alpha);
|
||||||
|
self.ctx.set_fill_style_str(target_color);
|
||||||
|
|
||||||
let char_str = self.charset[char_index].to_string();
|
let char_str = self.charset[char_index].to_string();
|
||||||
self.ctx.set_fill_style_str(color_ref);
|
|
||||||
self.ctx
|
self.ctx
|
||||||
.fill_text(&char_str, x as f64, y as f64)
|
.fill_text(&char_str, x as f64, y as f64)
|
||||||
.unwrap_or(());
|
.unwrap_or(());
|
||||||
@@ -328,11 +332,13 @@ impl MatrixEngine {
|
|||||||
drop.y += drop.speed;
|
drop.y += drop.speed;
|
||||||
|
|
||||||
if y > self.height as f32 && rng.gen_bool(0.01) {
|
if y > self.height as f32 && rng.gen_bool(0.01) {
|
||||||
drop.y = rng.gen_range(-10.0..0.0);
|
drop.y = rng.gen_range(-5.0..0.0);
|
||||||
drop.speed = rng.gen_range(0.15..0.45);
|
drop.speed = rng.gen_range(0.2..0.5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// РИСУЕМ БЛОКИ (АЛЬФА 0.8 ДЛЯ ВСЕХ)
|
||||||
|
self.ctx.set_global_alpha(0.8);
|
||||||
for block in self.glitch_blocks.iter_mut() {
|
for block in self.glitch_blocks.iter_mut() {
|
||||||
self.ctx.set_fill_style_str(&block.color);
|
self.ctx.set_fill_style_str(&block.color);
|
||||||
self.ctx.fill_rect(
|
self.ctx.fill_rect(
|
||||||
@@ -345,6 +351,7 @@ impl MatrixEngine {
|
|||||||
}
|
}
|
||||||
self.glitch_blocks.retain(|b| b.life > 0.0);
|
self.glitch_blocks.retain(|b| b.life > 0.0);
|
||||||
|
|
||||||
|
// РИСУЕМ СТЕКЛО (МЫШЬ)
|
||||||
if self.is_drawing && rng.gen_bool(0.3) {
|
if self.is_drawing && rng.gen_bool(0.3) {
|
||||||
self.glass_symbols.push(GlassSymbol::new(
|
self.glass_symbols.push(GlassSymbol::new(
|
||||||
self.mouse_x,
|
self.mouse_x,
|
||||||
@@ -360,12 +367,16 @@ impl MatrixEngine {
|
|||||||
sym.y += sym.velocity_y;
|
sym.y += sym.velocity_y;
|
||||||
sym.life -= 1.0;
|
sym.life -= 1.0;
|
||||||
let alpha = (sym.life / sym.max_life).max(0.0);
|
let alpha = (sym.life / sym.max_life).max(0.0);
|
||||||
self.ctx
|
|
||||||
.set_fill_style_str(&format!("rgba({}, {})", sym.color, alpha));
|
self.ctx.set_global_alpha(alpha as f64);
|
||||||
|
self.ctx.set_fill_style_str(&sym.color);
|
||||||
self.ctx
|
self.ctx
|
||||||
.fill_text(&char_str, sym.x as f64, sym.y as f64)
|
.fill_text(&char_str, sym.x as f64, sym.y as f64)
|
||||||
.unwrap_or(());
|
.unwrap_or(());
|
||||||
}
|
}
|
||||||
self.glass_symbols.retain(|s| s.life > 0.0);
|
self.glass_symbols.retain(|s| s.life > 0.0);
|
||||||
|
|
||||||
|
// СБРОС АЛЬФЫ В КОНЦЕ
|
||||||
|
self.ctx.set_global_alpha(1.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user