본문 바로가기
Programming

[PROGRAMMING] 프로그래밍 (Programming) - 이미지 색상 추출(Image color picaker)

by 물코더 2021. 10. 9.

코드

· HTML, CSS(SCSS), 자바스크립트(Javascript)

기능

· 이미지 파일에서 색상 데이터를 추출 (Image Color Picaker)

 

1. HTML & CSS

1) 코드 뷰 

<body>
	<!-- 메인 컨텐츠 영역 -->
    <main id="main-body">
    
		<!-- example : 컨테이너 -->
        <section class="container flex-align-center flex-column" id="example"> 
			<!-- example : 컨텐츠 -->
            <article class="content" name="example"> 

                <!-- 이미지 업로드 영역 -->
                <label id="del-upload" for="btn-upload">

                    <input type="file" id="btn-upload" hidden accept="image/png, image/jpeg" />

                    <!-- 이미지 컨테이너 (figure) -->
                    <figure class="ct-img">

                        <!-- 메인 이미지 (썸네일) (#thumnail) -->
                        <img class="img-main" id="thumnail" decoding="async" loading="lazy" />

                        <!-- 색상표 (palette) -->
                        <figcaption id="palette"></figcaption>
                    </figure>

                </label>
                
                <!-- canvas : 메인 캔버스(canvas) -->
                <canvas id="canvas-main" hidden></canvas>

            </article>
            
        </section>
      
    </main>

</body>
#main-body #example > .content 
{
    width: 100%;
    height: 100vh;
    padding: 0 20px;
    max-width: 1280px; 
}
#main-body #example > .content > #del-upload 
{
    background-color: rgba(0, 0, 0, 0.25);
    width: 227px;
    max-width: 227px;
    height: 350px;
    max-height: 350px;
    display: block;
    padding: 20px;
    left: 50%;
    top: 50%;
    margin-left: -113.5px;
    margin-top: -175px;
    cursor: pointer; 
}
#main-body #example > .content > #del-upload .ct-img 
{
    width: 187px;
    max-width: 187px;
    height: 310px;
    max-height: 310px;
    display: block; 
}
#main-body #example > .content > #del-upload .ct-img > img.img-main 
{
    width: 187px;
    max-width: 187px;
    height: 270px;
    max-height: 270px;
    display: block;
    clear: both; 
}
#main-body #example > .content > #del-upload .ct-img > img.img-main:not([src]) 
{
    display: none; 
}
#main-body #example > .content > #del-upload .ct-img > #palette 
{
    width: inherit;
    height: 40px;
    max-height: 40px;
    padding: 10px 0;
    text-align: center; 
}
#main-body #example > .content > #del-upload .ct-img > #palette > div 
{
    background-color: #ddd;
    width: 20px;
    height: 20px;
    display: inline-block;
    border: 1px solid #CCCCCC; 
}
#main-body #example > .content > #del-upload .ct-img > #palette > div + div { margin-left: 5px; }
#main-body #example > .content > #del-upload:not([data-file]) { border: 1px dashed #C8C8C8; }
#main-body #example > .content > #del-upload:not([data-file]) > .ct-img { display: none; }
#main-body #example > .content > #del-upload[data-file] { border: 1px solid #C8C8C8; }
#main-body #example > .content #canvas-main { position: absolute; }

2) 노드 뷰

· main > .container > .content > label#del-upload

· main > .container > .content > label#del-upload > input[type="file"]#btn-upload

· label#del-upload > figure.ct-img > img#thumnail, figcaption#palette 

main > .container > .content > label#del-upload
main > .container > .content > label#del-upload > figure.ct-img
main > .container > .content > label#del-upload > figure.ct-img > img#thumnail
main > .container > .content > label#del-upload > figure.ct-img > figcaption#palette

2. 자바스크립트 

· label#del-upload > input[type="file"]#btn-upload 버튼 이벤트 바인드 ( onchange )

· label#del-upload > figure.ct-img > img#thumnail 이미지 이벤트 바인드 ( onload )

 

1) 코드 뷰 

· document.onload 

// 도큐먼트 BODY : event "onload"
function ex_onload() 
{
  // 이벤트 바인드 : 파일 업로드 
  // #del-upload > #btn-upload
  var upload = document.getElementById( "btn-upload" );
  if ( upload ) upload.onchange = ex_file_upload;

  // 이벤트 바인드 : 메인 이미지 변경 
  var thumnail = document.getElementById( "thumnail" );
  if ( thumnail ) thumnail.onload = ex_img_onload;
}

· input[type="file"]#btn-upload  :: onchange

// 파일 업로드 (file upload)
// input[type="file"]#btn-upload : event "onchange"
function ex_file_upload (e) 
{
  // 파일 리스트 초기화 (file.files)
  var self = this; // <input type="file" />

  var filelist = this.files;
  if ( filelist.length == 0 ) return;

  // FileReader 객체 생성
  var fileReader = new FileReader();
  // FileReader.onload 바인드 
  fileReader.onload = function () {
    // #del-upload 속성 변경
    self.parentNode.setAttribute ( "data-file", "1" ); 

    // 메인 이미지 (#thumnail) 
    // #del-upload > .ct-img > .img-main#thumnail
    var thumnail = document.getElementById( "thumnail" );
    thumnail.src = this.result; // 이미지 src 
  };

  fileReader.readAsDataURL( filelist[0] ); // base64 인코딩 스트링 데이터
}

· label#del-upload > figure.ct-img > img#thumnail :: onload

// 메인 이미지 변경 (img on load) 
// img#thumnail : event "onload"
function ex_img_onload (e)
{
  // this == img (이미지 노드)  
  var self = this;

  // #canvas-main 메인 캔버스 
  var cnv = document.getElementById( "canvas-main" );
  var ctx = cnv.getContext( "2d" );
  
  cnv.setAttribute( "width", this.width ); // #thumnail 사이즈 
  cnv.setAttribute( "height", this.height );

  // 캔버스 이미지 그리기
  ctx.drawImage( this, 0, 0, this.width, this.height );

  // 캔버스 데이터 (RGBA) 불러오기
  var imgData = ctx.getImageData( 0, 0, cnv.width, cnv.height );

  // 캔버스 데이터 파싱 
  var colors = []; // 색상 배열 (값-hex)
  var blocksize = 1; // 블록 사이즈 (px)
  
  var count = 0; 
  var i = -4; // R,G,B,A (4)

  while ( ( i += blocksize * 4 ) < imgData.data.length )
  {
    ++ count; // 카운트

    // getImageData().data 에서 해당 픽셀 RGBA 값 추출
    var v_rgba = [ imgData.data[i], imgData.data[i + 1], imgData.data[i + 2], imgData.data[i + 3] ];

    // Array.prototype.map : RGBA 값(0~255) -> HEX 값 변환 
    var v_hex = v_rgba.map( function(color_val) { 
      var _hex = color_val.toString(16);
      return _hex.length == 1 ? "0" + _hex : _hex;; 
    } );

    v_hex = v_hex.join( "" ); // Array -> String 합치기 
    colors.push( v_hex ); // 색상 배열 추가
  }
  
  // 색상 별 카운트
  var picaker = {};
  var old_color = null; // 이전 색상(hex)
  colors.sort().forEach( function(colorhex, arrindx, arr){

    // item == 현재 생상 값 (current color) (hex)
    if ( old_color != colorhex )
    {
      // 새 색상 배열 추가        
      picaker[colorhex] = 0; 
      old_color = colorhex; // 이전(마지막) 색상 업데이트      
    }

    return picaker[colorhex] = ( picaker[colorhex] || 0 ) + 1; // 색상 카운트
  } );

  // 정렬(sort) : 색상 코드 반복 횟수 
  colors = picaker;
  picaker = [];

  for ( var color in colors ) 
  {    
    picaker.push( [color, colors[color]] ); // [색상, 횟수]
  }

  // 상위 5개 추출
  picaker.sort( function ( a,b ) { return a[1] - b[1]; } );
  picaker.reverse();

  picaker = picaker.slice(0, 5); // 추출
  
  // 배경 그레디언트, 색상표 아이템 생성
  var color_gradient = "";

  // #palette > div
  var palette = document.getElementById( "palette" );  
  palette.innerHTML = "";
  picaker.forEach( function(item) {
    var _color = item[0];

    // 배경 그레디언트 
    if ( color_gradient != "" ) color_gradient += ",";
    color_gradient += " #" + _color;    

    // 색상표 아이템 생성 (div)
    var _div = document.createElement( "div" );
    // div 배경 색상(background-color) 초기화
    _div.style.backgroundColor = "#" + _color;
    palette.appendChild( _div );

  } );

  // 배경 색상 그레디언트 (document.body)
  var body = document.body;
  body.style.mozBackground = "linear-gradient(to right, " + color_gradient + ")";
  body.style.webkitBackground = "linear-gradient(to right, " + color_gradient + ")";
  body.style.background = "linear-gradient(to right, " + color_gradient + ")";
}

 

반응형

 

3. 결과 

 

반응형

댓글