::BASIC_26_Text_Animation_2

BamgasiJM·2025년 10월 24일

Nannou <BASIC>

목록 보기
36/41
post-thumbnail

📝 Rust Code

use nannou::prelude::*;
use nannou::noise::{NoiseFn, Perlin};
use std::fs;

// 전역 파라미터
const TEXT: &str = "Bamgasi";
const FONT_SIZE: u32 = 200;
const WAVE_AMPLITUDE: f32 = 30.0; // 웨이브 진폭
const WAVE_FREQUENCY: f32 = 0.5; // 웨이브 주파수
const WAVE_SPEED: f32 = 2.0; // 웨이브 이동 속도
const NOISE_SCALE: f64 = 15.0; // 노이즈 왜곡 강도
const NOISE_FREQUENCY: f64 = 0.03; // 노이즈 주파수
const CHAR_SPACING: f32 = 100.0; // 글자 간격

struct Model {
    font: text::Font,
    noise: Perlin,
}

struct CharacterInfo {
    character: char,
    base_x: f32,
    index: usize,
}

fn main() {
    nannou::app(model).update(update).view(view).run();
}

fn model(app: &App) -> Model {
    app.new_window()
        .size(800, 800)
        .title("Wave Distortion Typography")
        .view(view)
        .build()
        .unwrap();

    let assets = app.assets_path().unwrap();
    let font_path = assets.join("fonts").join("NotoSans-ExtraBold.ttf");
    let font_data = fs::read(font_path).expect("폰트 파일을 읽을 수 없습니다.");
    let font = text::Font::from_bytes(font_data).expect("폰트를 불러올 수 없습니다.");

    Model {
        font,
        noise: Perlin::new(),
    }
}

fn update(_app: &App, _model: &mut Model, _update: Update) {}

fn get_character_positions(text: &str) -> Vec<CharacterInfo> {
    let char_count = text.chars().count();
    let total_width = (char_count - 1) as f32 * CHAR_SPACING;
    let start_x = -total_width / 2.0;

    text.chars()
        .enumerate()
        .map(|(i, ch)| CharacterInfo {
            character: ch,
            base_x: start_x + (i as f32 * CHAR_SPACING),
            index: i,
        })
        .collect()
}

fn calculate_distorted_position(
    base_x: f32,
    index: usize,
    time: f32,
    noise: &Perlin,
) -> (f32, f32) {
    // 사인파 웨이브 효과
    let wave_phase = (base_x * WAVE_FREQUENCY) + (time * WAVE_SPEED);
    let wave_y = wave_phase.sin() * WAVE_AMPLITUDE;
    
    // 글자마다 다른 주파수로 수직 웨이브
    let vertical_phase = (time * 1.5) + (index as f32 * 0.5);
    let vertical_wave = vertical_phase.sin() * 15.0;
    
    // 노이즈 기반 왜곡
    let noise_x = noise.get([
        base_x as f64 * NOISE_FREQUENCY,
        time as f64 * 0.5,
        0.0,
    ]) * NOISE_SCALE;
    
    let noise_y = noise.get([
        base_x as f64 * NOISE_FREQUENCY,
        time as f64 * 0.5,
        1000.0,
    ]) * NOISE_SCALE;
    
    // 최종 위치 계산
    let final_x = base_x + noise_x as f32;
    let final_y = wave_y + vertical_wave + noise_y as f32;
    
    (final_x, final_y)
}

fn calculate_rotation(base_x: f32, time: f32) -> f32 {
    // 위치와 시간에 따른 회전 각도
    let rotation_phase = (base_x * 0.01) + (time * 1.2);
    rotation_phase.sin() * 0.15 // 라디안 단위
}

fn calculate_scale(index: usize, time: f32) -> f32 {
    // 글자마다 다른 스케일 변화
    let scale_phase = (time * 2.0) + (index as f32 * 0.3);
    1.0 + (scale_phase.sin() * 0.1)
}

fn calculate_opacity(base_x: f32, time: f32, noise: &Perlin) -> f32 {
    // 노이즈 기반 투명도 변화
    let opacity_noise = noise.get([
        base_x as f64 * 0.01,
        time as f64 * 0.8,
        2000.0,
    ]);
    
    (0.7 + (opacity_noise * 0.3) as f32).clamp(0.5, 1.0)
}

fn view(app: &App, model: &Model, frame: Frame) {
    let draw = app.draw();
    
    // 배경 처리 (트레일 효과)
    let frame_no = frame.nth();
    if frame_no == 0 {
        draw.background().color(hsla(0.0, 0.0, 0.02, 1.0));
    } else {
        draw.rect()
            .w_h(800.0, 800.0)
            .color(hsla(0.0, 0.0, 0.02, 0.05));
    }
    
    let time = app.time;
    let characters = get_character_positions(TEXT);
    
    // 각 글자를 개별적으로 렌더링
    for char_info in characters {
        let (x, y) = calculate_distorted_position(
            char_info.base_x,
            char_info.index,
            time,
            &model.noise,
        );
        
        let rotation = calculate_rotation(char_info.base_x, time);
        let scale = calculate_scale(char_info.index, time);
        let opacity = calculate_opacity(char_info.base_x, time, &model.noise);
        
        // 색상 변화 (시간과 위치에 따라)
        let hue = (time * 0.1 + char_info.index as f32 * 0.05) % 1.0;
        let color = hsla(hue, 0.7, 0.5, opacity);
        
        draw.text(&char_info.character.to_string())
            .font(model.font.clone())
            .font_size((FONT_SIZE as f32 * scale) as u32)
            .no_line_wrap()
            .x(x)
            .y(y)
            .rotate(rotation)
            .color(color);
    }
    
    draw.to_frame(app, &frame).unwrap();
}

profile
Coding Art with Blender / oF / Processing / p5.js / nannou

0개의 댓글