CSS&JS/⚡Thinkers

[JS] canvas로 첨부파일 이미지로부터 평균 배경색상 추출해서 4사분면 그라디언트

arancia_ 2026. 3. 9. 17:26

See the Pen radial gradients from input file by canvas by Oh Ikmyeong (@dpffpself) on CodePen.

HTML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html lang="en">
 
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>4사분면 그라디언트</title>
    <link rel="stylesheet" href="./style.css">
    <script src="./main.js" type="module"></script>
</head>
 
<body>
    <label>
        <span>원하는 이미지를 첨부하세요</span>
        <input type="file" id="ipt-file" accept="image/*" />
    </label>
    <section id="cv-wrap">
        <canvas id="cv" width="300" height="300" style="border:2px solid black; background:#fff;"></canvas>
    </section>
</body>
 
</html>
cs

CSS

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
{
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}
 
body {
    display: flex;
    flex-flow: column nowrap;
    justify-content: center;
    align-items: center;
    gap: 10px;
    min-height: 100vh;
    padding: 40px 0;
    background: #ccc;
}
 
#cv-wrap {
    display: flex;
    flex-flow: column nowrap;
    justify-content: center;
    align-items: center;
    position: relative;
    width: 100%;
    min-height: 100vh;
    background: #000;
}
cs

JS

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
const $file = document.getElementById("ipt-file");
const $wrap = document.getElementById("cv-wrap");
const $cv = document.getElementById("cv");
const ctx = $cv.getContext("2d");
 
/* 첨부파일 받기 시작할때 */
$file.addEventListener("change", on_file_change);
 
/**
 * 파일 첨부시
 * @param {*} e 
 * @returns 
 */
function on_file_change(e) {
    const file = e.target.files[0];
    if (!file) return;
 
    // 1. 임시 URL 생성 (파일을 이미지 소스로 사용할 수 있게 함)
    const url = URL.createObjectURL(file);
    const $img = new Image();
    $img.src = url;
 
    $img.onload = () => {
        /* 캔버스 사이즈 변경 */
        $cv.width = $img.width >= 300 ? 300 : $img.width;
        $cv.height = $img.width <= 300 ? $img.height : (($img.height * $cv.width) / $img.width);
 
        /* 캔버스에 그리기 */
        ctx.drawImage($img, 00, $img.width, $img.height, 00, $cv.width, $cv.height);
 
        /* 평균 색상 계산 실행 */
        const gradients = [];
        const dw = $cv.width / 2;
        const dh = $cv.height / 2;
        gradients.push(get_frame_and_rgb({
            dx: 0,
            dy: 0,
            dw,
            dh,
            direction: "top left"
        }));
        gradients.push(get_frame_and_rgb({
            dx: $cv.width / 2,
            dy: 0,
            dw,
            dh,
            direction: "top right"
        }));
        gradients.push(get_frame_and_rgb({
            dx: 0,
            dy: $cv.height / 2,
            dw,
            dh,
            direction: "bottom left"
        }));
        gradients.push(get_frame_and_rgb({
            dx: $cv.width / 2,
            dy: $cv.height / 2,
            dw,
            dh,
            direction: "bottom right"
        }));
 
        $wrap.style.background = gradients.join(",");
 
 
        /* 메모리 해제 */
        URL.revokeObjectURL(url);
    }
}//on_file_change
 
/**
 * 
 */
function get_frame_and_rgb({ dx, dy, dw, dh, direction }) {
    const frame = ctx.getImageData(dx, dy, dw, dh);
    const [r, g, b] = get_color_avg(frame);
    return `radial-gradient(circle at ${direction}, rgba(${r},${g},${b}, 1) , transparent 70%)`
}//get_frame_and_rgb
 
/** 
 * 평균 색상값 구하기
 * @returns {Array} [r,g,b]
*/
function get_color_avg(frame) {
    let r = 0let g = 0let b = 0;
 
    /* rgba 값이라 4만 됨 */
    const per = 4;
 
    /* 캔버스에서 이미지의 평균 색상을 계산 */
    for (let i = 0; i < frame.data.length; i += per) {
        r += frame.data[i];
        g += frame.data[i + 1];
        b += frame.data[i + 2];
    }
    r = Math.ceil(r / (frame.data.length / per));
    g = Math.ceil(g / (frame.data.length / per));
    b = Math.ceil(b / (frame.data.length / per));
 
    /* 최종 */
    return [r, g, b];
}//get_color_avg
cs