← 과제 목록

[과제] 2주차) Basic Programming 이승원

@nowgnuesLeee
  • #과제
목차

예제 만들기

처음에는 글래스 모피즘이 들어간 슬라이더를 만들려고 했는데 AI와 함께해도 생각처럼 구현이 안돼서 슬라이더를 만드는 것으로 변경했다. 브랜치

제작 과정

먼저 캔버스나 thumbs 처럼 구현에 필요한 상수들을 다음과 같이 정의했다.

//Layout
#define CANVAS_W  800
#define CANVAS_H  400
#define TRACK_W   640.0f
#define TRACK_X   ((CANVAS_W - TRACK_W) * 0.5f)
#define TRACK_CY  (CANVAS_H * 0.5f)
#define TRACK_H    20.0f

#define THUMB_W   130.0f
#define THUMB_H    84.0f

기본적으로 다루어야 하는 변수들은 선언, thumb는 그림자 효과를 줄려고 Scene으로 지정했다.

    tvg::Shape* blue = nullptr;    //filled part of the track
    tvg::Scene* thumb = nullptr;   //white pill + soft shadow
    float value = 0.4f;            //0.0 ~ 1.0
    bool dragging = false;

채워지는 너비 계산은 다음 함수를 통해서.

    //The shared track shape: the groove and the blue fill differ only in width.
    void track(tvg::Shape* shape, float width)
    {
        shape->appendRect(TRACK_X, TRACK_CY - TRACK_H * 0.5f, width, TRACK_H, TRACK_H * 0.5f, TRACK_H * 0.5f);
    }

처음 렌더는 캔버스의 배경 -> 회색 영역 -> 파란색 영역 -> thumb 로 해서 z-index도 부여해줬다. 대충 흰 배경과 회색 영역이 있고 그 위에서 파란 영역과 thumb가 움직인다고 생각하면 된다.

    bool content(tvg::Canvas* canvas, uint32_t w, uint32_t h) override
    {
        //background
        auto bg = tvg::Shape::gen();
        bg->appendRect(0, 0, w, h);
        bg->fill(248, 248, 248);
        canvas->add(bg);

        //the empty groove
        auto groove = tvg::Shape::gen();
        track(groove, TRACK_W);
        groove->fill(214, 214, 218);
        canvas->add(groove);

        //the blue progress fill (rebuilt by apply(); the gradient is retained)
        blue = tvg::Shape::gen();
        auto grad = tvg::LinearGradient::gen();
        grad->linear(TRACK_X, 0, TRACK_X + TRACK_W, 0);
        tvg::Fill::ColorStop stops[2];
        stops[0] = {0.0f, 96, 150, 245, 255};
        stops[1] = {1.0f, 56, 108, 240, 255};
        grad->colorStops(stops, 2);
        blue->fill(grad);
        canvas->add(blue);

        //the thumb: a white pill with a soft drop shadow, slid via translate
        thumb = tvg::Scene::gen();

        auto pill = tvg::Shape::gen();
        pill->appendRect(TRACK_X - THUMB_W * 0.5f, TRACK_CY - THUMB_H * 0.5f, THUMB_W, THUMB_H, THUMB_H * 0.5f, THUMB_H * 0.5f);
        pill->fill(255, 255, 255);
        thumb->add(pill);

        thumb->add(tvg::SceneEffect::DropShadow, 0, 30, 63, 26, 90.0, 1.5, 6.0, 50);   //rgba, angle, dist, sigma, quality
        canvas->add(thumb);

        apply();
        return true;
    }

파란 영역과 thumb의 위치 계산 함수. value는 슬라이더의 0-1 사이의 값이고 이를 너비랑 곱해서 thumb 위치 및 파란 영역이 어느정도 있어야 하는지 결정한다.

    //Reposition the blue fill and the thumb for the current `value`.
    void apply()
    {
        auto dx = value * TRACK_W;

        blue->reset();   //keeps the gradient fill
        track(blue, dx);

        thumb->translate(dx, 0);
    }

트랙 영역이 클릭되고 드래깅 된다며 해당 위치를 통해 value를 계산, apply 및 track 함수를 통해 파란 영역과 thumb의 위치를 지정한다. 결국 동작 자체는 thumb의 클릭과 상관 없이 트랙 영역이 클릭된다면 해당 위치를 기반으로 thumb를 이동시키고 파란 영역 값을 재계산 한다고 생각하면 된다.

    //Map a mouse x onto the track. Returns false if (x, y) is off the slider.
    bool grab(int32_t x, int32_t y)
    {
        if (y < (int)(TRACK_CY - THUMB_H) || y > (int)(TRACK_CY + THUMB_H)) return false;   //generous vertical slop
        if (x < (int)(TRACK_X - THUMB_W * 0.5f) || x > (int)(TRACK_X + TRACK_W + THUMB_W * 0.5f)) return false;

        auto v = ((float)x - TRACK_X) / TRACK_W;
        value = (v < 0.0f) ? 0.0f : (v > 1.0f ? 1.0f : v);
        return true;
    }

    bool clickdown(tvg::Canvas* canvas, int32_t x, int32_t y) override
    {
        if (!grab(x, y)) return false;
        dragging = true;
        apply();
        canvas->update();
        return true;
    }

    bool motion(tvg::Canvas* canvas, int32_t x, int32_t y) override
    {
        if (!dragging) return false;
        auto prev = value;
        grab(x, (int32_t)TRACK_CY);       //track horizontally even if the cursor drifts
        if (value == prev) return false;  //ignore no-op moves
        apply();
        canvas->update();
        return true;
    }

    bool clickup(tvg::Canvas* canvas, int32_t x, int32_t y) override
    {
        dragging = false;
        return false;
    }

결과

글래스모피즘을 구현하지 못해서 아쉽지만 그래도 적당히 이쁘게 나온 것 같아서 만족합니다. CleanShot 2026-07-25 at 01 38 27

댓글

Discussion 원문